引子

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

效果动态图

源代码

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

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

转载于:https://www.cnblogs.com/hankzhouAndroid/p/9159101.html

android-侧滑菜单相关推荐

  1. 原生Android 侧滑菜单实践(部分)

    此为第一个制作侧滑菜单的实践 . 此部分仅仅为部分实践: 仅缺menu的字符串布局,以及需要修改的MainActivity.java文件,也是需要主要修改的地方. 从使用MD设计-进行侧滑菜单的制作( ...

  2. android自定义侧滑菜单代码,原生Android 侧滑菜单实践(部分)

    此为第一个制作侧滑菜单的实践 . 此部分仅仅为部分实践: 仅缺menu的字符串布局,以及需要修改的MainActivity.java文件,也是需要主要修改的地方. 从使用MD设计-进行侧滑菜单的制作( ...

  3. Android 侧滑菜单(抽屉)的使用总结

    侧滑菜单这种设计,在很多 APP 上面都有看到,例如大版本3.0之前的知乎.网易新闻.滴滴打车等.有些热衷于 Android Material Design 的开发者,甚至将一些 IOS 化的 And ...

  4. android侧滑菜单ui,Android:UI控件DrawerLayout、Fragment、SlidingLayout侧滑菜单

    DrawerLayout与Fragment的联用 1.xml代码 xmlns:android="http://schemas.android.com/apk/res/android" ...

  5. android 点击侧滑代码,代码分析Android实现侧滑菜单

    Android 侧滑菜单的实现,参考网上的代码,实现侧滑菜单.最重要的是这个动画类UgcAnimations,如何使用动画类来侧滑的封装FlipperLayout. 1.实现效果 2.动画类UgcAn ...

  6. Android 自定义控件打造史上最简单的侧滑菜单

    侧滑菜单在很多应用中都会见到,最近QQ5.0侧滑还玩了点花样~~对于侧滑菜单,一般大家都会自定义ViewGroup,然后隐藏菜单栏,当手指滑动时,通过Scroller或者不断的改变leftMargin ...

  7. Android仿QQ侧滑菜单

    先上效果图: GIF图有点模糊,源码已上传Github:Android仿QQ侧滑菜单 ####整体思路: 自定义ItemView的根布局(SwipeMenuLayout extends LinearL ...

  8. Android自定义View之仿QQ侧滑菜单实现

    最近,由于正在做的一个应用中要用到侧滑菜单,所以通过查资料看视频,学习了一下自定义View,实现一个类似于QQ的侧滑菜单,顺便还将其封装为自定义组件,可以实现类似QQ的侧滑菜单和抽屉式侧滑菜单两种菜单 ...

  9. Android自定义顶部栏及侧滑菜单和fragment+viewpag滑动切换的实现

    嘿嘿嘿,关于android滑动的操作,是不是经常都会用到呢. 我肯定也要学习一下啦. https://blog.csdn.net/u013184970/article/details/82882107 ...

  10. android.support.v7 fragme,打造最强RecyclerView侧滑菜单,长按拖拽Item,滑动删除Item

    前几天写了一片关于RecyclerView滑动删除Item,RecyclerView长按拖拽Item的博客,本来很简单一个使用,阅读量还挺高的,原博客传送门. 今天介绍一个RecyclerView I ...

最新文章

  1. 如何测试ASP.NET Core Web API
  2. linux硬件设备操作函数 open
  3. (递归7)生成可重集的排列
  4. 物联网模式下的污水处理前景分析
  5. android ble 调试工具,分享一个蓝牙BLE调试小程序LightBlue
  6. 【C语言】常用字符(string库函数,ctype库函数),字符数组的输入与处理)
  7. Windows Store App 图像
  8. Description Resource Path LocationType Java compiler level does not match the version of the instal
  9. Pascal数据结构与算法
  10. 用python把unix格式转换成windows格式
  11. 杀毒软件可能令企业用户陷入更大危机
  12. 【网络安全】目前看到最全的恶意软件分析大合集
  13. houdini 粒子
  14. 中国行政村边界数据、乡镇街道边界
  15. 怎么避开平台多ip检测
  16. erpc Linux 本地环境的搭建和使用
  17. 关于AWS亚马逊云计算平台EC2如何删除快照
  18. .net平台SqlSuger学习之旅——1、安装SqlSugar
  19. matlab iri模型,IRI2012电离层模型 matlab代码
  20. 被迫毕业,面试 30 家公司,终于上岸了!

热门文章

  1. Scala集合:List修改及删除数据
  2. 【面试题视频讲解】TreeSet使用示例
  3. python保留关键字列表
  4. MySQL数据库优化实战
  5. 【软考-软件设计师】总线结构
  6. 从Jenkins或Gerrit上获取拉代码和编译命令
  7. 【已解决】单片机串口通讯中RXD与TXD如何连线?
  8. 4.2.2 OS之磁盘调度算法(FCFS、SSTF、SCAN、LOOK、S-SCAN、C-LOOK)
  9. java ee web高级,Java EE Web高级开发案例
  10. sqlplus几个存储过程执行变量值窜掉了_基于大数据的冷连轧过程控制优化技术研究...