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大神的思路写出来的,但是他的博客地址,找不到了╮( ̄▽ ̄")╭

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

  • 相关阅读:
    3Dtouch 的实际应用详解(tableView中的应用)
    使用UIScrollView的zoomscale实现图片捏合放大
    SVN命令使用详解
    参数传递
    cookie文件路径
    XML和HTML之间的差异
    cssSelector元素定位方法
    如何调用一个数据完整的firefox浏览器
    Android生命周期详解
    android 利用countDownLatch实现主线程与子线程之间的同步
  • 原文地址:https://www.cnblogs.com/hankzhouAndroid/p/9159101.html
Copyright © 2011-2022 走看看