zoukankan      html  css  js  c++  java
  • android-侧滑菜单

    引子

    移动平台上很常见的侧滑菜单布局,本文将给出控件源码,以及调用的代码。

    效果动态图

     

     源代码

    自定义ViewGroup : Swipe2DeleteViewGroup.java
      1 package com.example.administrator.technologystackapp.activities.custom;
      2 
      3 import android.content.Context;
      4 import android.graphics.Rect;
      5 import android.util.AttributeSet;
      6 import android.view.MotionEvent;
      7 import android.view.VelocityTracker;
      8 import android.view.View;
      9 import android.view.ViewConfiguration;
     10 import android.view.ViewGroup;
     11 import android.widget.Scroller;
     12 
     13 
     14 /**
     15  * Created by wupengjian on 16/11/9.
     16  * <p/>
     18  */
     19 public class Swipe2DeleteViewGroup extends ViewGroup {
     20 
     21     private static final int STATUS_NORMAL = 0;
     22     private static final int STATUS_EXPAND = 1;
     23     private static final int HOVER_TAP_SLOP = 10;
     24     private static final int HOVER_TAP_TIMEOUT = 150;
     25     private View mCenterView;
     26     private Scroller mScroller;
     27     private VelocityTracker mVelocityTracker = null;
     28     private ViewConfiguration mViewConfiguration;
     29     private float mLastTouchX, mScrollX;
     30     private int mMaxScrollDistance, mMinScrollDistance;
     31     private int mStatus = STATUS_NORMAL;
     32     private MotionEvent mMoveDownEvent;
     33     private OnItemClickListener mOnItemClickListener;
     34 
     35     public Swipe2DeleteViewGroup(Context context) {
     36         this(context, null);
     37     }
     38 
     39     public Swipe2DeleteViewGroup(Context context, AttributeSet attrs) {
     40         this(context, attrs, 0);
     41     }
     42 
     43     public Swipe2DeleteViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
     44         super(context, attrs, defStyleAttr);
     45         mScroller = new Scroller(context);
     46         mViewConfiguration = ViewConfiguration.get(context);
     47     }
     48 
     49     @Override
     50     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     51         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     52 
     53         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
     54         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
     55         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
     56         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
     57 
     58         mCenterView = null;
     59         mMaxScrollDistance = 0;
     60 
     61         int childCount = getChildCount();
     62         for (int i = 0; i < childCount; i++) {
     63             View child = getChildAt(i);
     64             if (child.getVisibility() == GONE) {
     65                 continue;
     66             }
     67             int childWidth;
     68             if (mCenterView == null) {
     69                 mCenterView = child;
     70                 childWidth = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
     71             } else {
     72                 childWidth = heightMeasureSpec;
     73                 //最大滚动距离就是所有菜单item的宽度的和
     74                 mMaxScrollDistance += MeasureSpec.getSize(childWidth);
     75             }
     76             child.measure(childWidth, heightMeasureSpec);
     77         }
     78         setMeasuredDimension(widthSize, heightSize);
     79     }
     80 
     81     @Override
     82     protected void onLayout(boolean changed, int l, int t, int r, int b) {
     83 
     84         int childCount = getChildCount();
     85         int offset = 0;
     86         for (int i = 0; i < childCount; i++) {
     87             View child = getChildAt(i);
     88             if (child.getVisibility() == GONE) {
     89                 continue;
     90             }
     91             setChildFrame(child, offset, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
     92             offset += child.getMeasuredWidth();
     93         }
     94     }
     95 
     96     private void setChildFrame(View child, int left, int top, int width, int height) {
     97         child.layout(left, top, left + width, top + height);
     98     }
     99 
    100     @Override
    101     public boolean onTouchEvent(MotionEvent event) {
    102         super.onTouchEvent(event);
    103         switch (event.getAction()) {
    104             case MotionEvent.ACTION_DOWN:
    105                 mMoveDownEvent = MotionEvent.obtain(event);
    106                 if (mVelocityTracker == null) {
    107                     mVelocityTracker = VelocityTracker.obtain();
    108                 } else {
    109                     mVelocityTracker.clear();
    110                 }
    111                 mVelocityTracker.addMovement(event);
    112                 break;
    113             case MotionEvent.ACTION_MOVE:
    114 
    115                 mVelocityTracker.addMovement(event);
    116                 updateScrollX(mLastTouchX - event.getRawX());
    117                 break;
    118             case MotionEvent.ACTION_UP:
    119                 int downX = (int) mMoveDownEvent.getRawX();
    120                 int downY = (int) mMoveDownEvent.getRawY();
    121                 //如果事件坐标在以按下时坐标为中心的宽度为 2 * HOVER_TAP_SLOP 的正方形内,则认为这个事件是点击事件
    122                 Rect rect = new Rect(downX - HOVER_TAP_SLOP, downY - HOVER_TAP_SLOP, downX + HOVER_TAP_SLOP, downY + HOVER_TAP_SLOP);
    123                 //如果按下手指和抬起手指时的 坐标和时间 相差不是很大,则可以认为是点击
    124                 boolean intent2Click = rect.contains((int) event.getRawX(), (int) event.getRawY());
    125                 boolean isTimeNotTooLong = event.getEventTime() - event.getDownTime() < HOVER_TAP_TIMEOUT;
    126                 if (intent2Click && isTimeNotTooLong) {
    127 
    128                     handleClickEvent(event);
    129                 } else {
    130 
    131                     mVelocityTracker.computeCurrentVelocity(1000);
    132                     float velocityX = mVelocityTracker.getXVelocity();
    133                     if (Math.abs(velocityX) > mViewConfiguration.getScaledMinimumFlingVelocity()) {
    134                         if (velocityX > 0) {
    135                             hideMenu();
    136                         } else {
    137                             showMenu();
    138                         }
    139                     } else {
    140 
    141                         toggleStatus();
    142                     }
    143                 }
    144                 if (mVelocityTracker != null) {
    145                     mVelocityTracker.recycle();
    146                     mVelocityTracker = null;
    147                 }
    148                 break;
    149             case MotionEvent.ACTION_CANCEL:
    150                 toggleStatus();
    151                 break;
    152         }
    153         mLastTouchX = event.getRawX();
    154         return true;
    155     }
    156 
    157     /**
    158      * 处理点击事件
    159      *
    160      * @param event
    161      */
    162     private void handleClickEvent(MotionEvent event) {
    163 
    164         int index = 0;
    165         int childCount = getChildCount();
    166         for (int i = 0; i < childCount; i++) {
    167             View child = getChildAt(i);
    168             if (child.getVisibility() == GONE) {
    169                 continue;
    170             }
    171             if (isMotionEventInView(child, event)) {
    172                 //如果点击的是主item,如果当前是菜单展开状态,则先收起菜单,并消费此次点击
    173                 if (child == mCenterView && mStatus != STATUS_NORMAL) {
    174 
    175                     hideMenu();
    176                 } else if (null != mOnItemClickListener) {
    177 
    178                     mOnItemClickListener.onItemClick(child, index, child == mCenterView);
    179                 }
    180                 break;
    181             }
    182             index++;
    183         }
    184     }
    185 
    186     /**
    187      * 判断点击事件是否在view中
    188      *
    189      * @param view
    190      * @param event
    191      * @return
    192      */
    193     private boolean isMotionEventInView(View view, MotionEvent event) {
    194         int[] location = new int[2];
    195         view.getLocationOnScreen(location);
    196         int x = location[0];
    197         int y = location[1];
    198         boolean isBeyondLeft = event.getRawX() < x;
    199         boolean isBeyondTop = event.getRawY() < y;
    200         boolean isBeyondRight = event.getRawX() > (x + view.getMeasuredWidth());
    201         boolean isBeyondBottom = event.getRawY() > (y + view.getMeasuredHeight());
    202         return !isBeyondLeft && !isBeyondTop && !isBeyondRight && !isBeyondBottom;
    203     }
    204 
    205     private void toggleStatus() {
    206         int scrollThreshold = getMeasuredHeight();
    207         if (mScrollX < scrollThreshold) {
    208 
    209             hideMenu();
    210         } else if (mScrollX > scrollThreshold) {
    211 
    212             showMenu();
    213         }
    214     }
    215 
    216     /**
    217      * 显示菜单
    218      */
    219     private void showMenu() {
    220 
    221         mStatus = STATUS_EXPAND;
    222         updateScrollX(mMaxScrollDistance);
    223     }
    224 
    225     /**
    226      * 隐藏菜单
    227      */
    228     private void hideMenu() {
    229 
    230         mStatus = STATUS_NORMAL;
    231         updateScrollX(-mMaxScrollDistance);
    232     }
    233 
    234     private void updateScrollX(float distance) {
    235 
    236         float dx = mScrollX + distance;
    237         if (dx < mMinScrollDistance) {
    238 
    239             dx = mMinScrollDistance;
    240         } else if (dx > mMaxScrollDistance) {
    241 
    242             dx = mMaxScrollDistance;
    243         }
    244         mScrollX = dx;
    245         smoothScrollTo((int) dx, 0);
    246     }
    247 
    248     private void smoothScrollTo(int fx, int fy) {
    249         int dx = fx - mScroller.getFinalX();
    250         int dy = fy - mScroller.getFinalY();
    251         smoothScrollBy(dx, dy);
    252     }
    253 
    254     private void smoothScrollBy(int dx, int dy) {
    255         mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
    256         invalidate();
    257     }
    258 
    259     @Override
    260     public void computeScroll() {
    261         if (mScroller.computeScrollOffset()) {
    262             scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
    263             postInvalidate();
    264         }
    265         super.computeScroll();
    266     }
    267 
    268     public void setOnItemClickListener(OnItemClickListener listener) {
    269         mOnItemClickListener = listener;
    270     }
    271 
    272     public interface OnItemClickListener {
    273         void onItemClick(View view, int index, boolean isCenterView);
    274     }
    275 }

    布局文件  swip_delete.xml 

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical">
     6 
     7     <TextView
     8         android:id="@+id/tv"
     9         android:layout_width="2000dp"
    10         android:layout_height="50dp"
    11         android:gravity="center_vertical"
    12         android:padding="10dp"
    13         android:text="侧滑删除菜单" />
    14 
    15     <com.example.administrator.technologystackapp.activities.custom.Swipe2DeleteViewGroup
    16         android:id="@+id/swipe2delete"
    17         android:layout_width="match_parent"
    18         android:layout_height="80dp"
    19         android:background="@android:color/white">
    20 
    21         <TextView
    22             android:layout_width="match_parent"
    23             android:layout_height="match_parent"
    24             android:gravity="center"
    25             android:text="主布局" />
    26 
    27         <TextView
    28             android:layout_width="match_parent"
    29             android:layout_height="match_parent"
    30             android:background="@android:color/holo_green_light"
    31             android:gravity="center"
    32             android:text="置顶"
    33             android:textColor="@android:color/white" />
    34 
    35         <TextView
    36             android:layout_width="match_parent"
    37             android:layout_height="match_parent"
    38             android:background="@android:color/holo_green_light"
    39             android:gravity="center"
    40             android:text="测试"
    41             android:textColor="@android:color/white"
    42             android:visibility="gone" />
    43 
    44         <TextView
    45             android:layout_width="match_parent"
    46             android:layout_height="match_parent"
    47             android:background="@android:color/holo_red_light"
    48             android:gravity="center"
    49             android:text="删除"
    50             android:textColor="@android:color/white" />
    51 
    52     </com.example.administrator.technologystackapp.activities.custom.Swipe2DeleteViewGroup>
    53 </LinearLayout>

     MainActivity.java 

     1 import android.os.Bundle;
     2 import android.view.View;
     3 import android.widget.TextView;
     4 import android.widget.Toast;
     5 
     6 import com.example.administrator.technologystackapp.R;
     7 import com.example.administrator.technologystackapp.activities.activity.manager.BaseActivity;
     8 import com.example.administrator.technologystackapp.activities.custom.Swipe2DeleteViewGroup;
     9 
    10 public class ActivitySwipe2delete extends BaseActivity {
    11 
    12     private Swipe2DeleteViewGroup mSwipe2Delete;
    13 
    14     @Override
    15     protected void onCreate(Bundle savedInstanceState) {
    16         super.onCreate(savedInstanceState);
    17         setContentView(R.layout.swip_delete);
    18         mSwipe2Delete = (Swipe2DeleteViewGroup) findViewById(R.id.swipe2delete);
    19         mSwipe2Delete.setOnItemClickListener(new Swipe2DeleteViewGroup.OnItemClickListener() {
    20             @Override
    21             public void onItemClick(View view, int index, boolean isCenterView) {
    22                 if (view instanceof TextView) {
    23                     TextView textView = (TextView) view;
    24                     String str = textView.getText().toString();
    25                     Toast.makeText(ActivitySwipe2delete.this, String.format("%s , isCenterView: %s", str, isCenterView), Toast.LENGTH_SHORT).show();
    26                 }
    27             }
    28         });
    29     }
    30 
    31 }

    鸣谢

      源代码是参考了CSDN大神的思路写出来的,但是他的博客地址,找不到了╮( ̄▽ ̄")╭

      不过感谢一下这位大神,代码我已经贡献出来了,各位看官请自便。

  • 相关阅读:
    jQuery 语法
    jQuery 简介
    把数据存储到 XML 文件
    XML 注意事项
    XML DOM (Document Object Model) 定义了访问和操作 XML 文档的标准方法。
    通过 PHP 生成 XML
    XML 命名空间(XML Namespaces)
    XML to HTML
    XMLHttpRequest 对象
    使用 XSLT 显示 XML
  • 原文地址:https://www.cnblogs.com/hankzhouAndroid/p/9159101.html
Copyright © 2011-2022 走看看