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

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

猜你喜欢

转载自www.cnblogs.com/hankzhouAndroid/p/9159101.html