300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > ListView Item侧滑菜单

ListView Item侧滑菜单

时间:2020-08-21 08:02:36

相关推荐

ListView Item侧滑菜单

一 概述

当下从QQ主界面出现Item侧滑菜单出现之后,很多公司的项目都需要添加这种让人感觉炫酷的功能,但是,目前android API中根本没有提供这种实现,因此就要用自定义的方法来实现该功能。本篇文章实现的基本思路如下:1.自定义ListView,获取当前滑动的View。2.自定义LinearLayout充当Item根布局。3.ListView中按下后,事件交由自定义LinearLayout处理。

二 效果

三 具体实现

重写ListView 的onTouchEvent方法,代码如下:

@Overridepublic boolean onTouchEvent(MotionEvent ev){switch (ev.getAction()){case MotionEvent.ACTION_DOWN:int x = (int) ev.getX();int y = (int) ev.getY();int position = pointToPosition(x,y);if (position != INVALID_POSITION){mSwipeMenuLayout = (SwipeMenuLayout) getChildAt(position - getFirstVisiblePosition());}break;}if (mSwipeMenuLayout != null){mSwipeMenuLayout.onTouchEvent(ev);}return super.onTouchEvent(ev);}

这些代码相对比较简单,没有什么复杂的逻辑,比较关键的是获取用户按下的点, 用ListView的pointToPosition方法,传入获取的x,y坐标得到当下点击 的Item的position,重点说一下 ListView的getChildAt(int index)方法, 因为ListView与GridView的View复用机制,这个方法默认得到的是当前可见 区域(列表可滚动)的子项!也就表示它的取值范围是在 >= ListView.getFirstVisiblePosition() && <= ListView.getLastVisiblePosition(); 因此在程序中获取当前 点击Item View所复用的是哪个Item的View 就需要用 position(当前通过xy坐标获取到的) - (减去) ListView.getFirstVisiblePosition() 得到的即是复用的Item的position,感兴趣的话,自己可以尝试着看看。

2. 自定义LinearLayout重写其onTouchEvent方法:

/*** Created by admin on /1/20.*/public class SwipeMenuLayout extends LinearLayout{/*** 关闭*/public static final int SLIDE_STATUS_OFF = 0;/*** 开始滚动*/public static final int SLIDE_STATUS_START_SCROLL = 1;/*** 已经打开*/public static final int SLIDE_STATUS_ON = 2;private static final int TAN = 2;/*** 滚动类*/private Scroller mScroller;/*** 屏幕尺寸类*/private int[] screenSize;/*** 主内容区域*/private ViewGroup mContent;/*** 菜单区域*/private ViewGroup mMenu;/*** 默认的宽度*/private int mMenuWidth = 120;private int lastX,lastY;public SwipeMenuLayout(Context context){this(context,null);}public SwipeMenuLayout(Context context, AttributeSet attrs){this(context, attrs,0);}@TargetApi(Build.VERSION_CODES.HONEYCOMB)public SwipeMenuLayout(Context context, AttributeSet attrs, int defStyleAttr){super(context, attrs, defStyleAttr);init(context);}private void init(Context context){screenSize = ScreenUtil.getScreenSize(context);mMenuWidth = ConvertUtils.dp2px(context,mMenuWidth);this.setOrientation(LinearLayout.HORIZONTAL);mScroller = new Scroller(context);}boolean isOnce = false;@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){if (!isOnce){mContent = (ViewGroup) getChildAt(0);mContent.getLayoutParams().width = screenSize[0];mMenu = (ViewGroup) getChildAt(1);mMenu.getLayoutParams().width = mMenuWidth;}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}/*** 设置默认的显示*/public void shrink(){Log.e("----------",getScrollX() + "");if (getScrollX() != 0){smoothScrollTo(0, 0);}}@Overridepublic boolean onTouchEvent(MotionEvent event){/*** 重置上次滑动的View*/if (onSlidingListener != null) {onSlidingListener.onSliding(this, SLIDE_STATUS_START_SCROLL);}int x = (int) event.getX();int y = (int) event.getY();int scrollX = getScrollX();switch (event.getAction()){case MotionEvent.ACTION_DOWN:{/*** 如果没有结束则停止动画*/if (!mScroller.isFinished()) {mScroller.abortAnimation();}break;}case MotionEvent.ACTION_MOVE:{int deltaX = x - lastX;int deltaY = y - lastY;if (Math.abs(deltaX) < Math.abs(deltaY) * TAN) {break;}int newScrollX = scrollX - deltaX;if (deltaX != 0) {if (newScrollX < 0) {newScrollX = 0;} else if (newScrollX > mMenuWidth) {newScrollX = mMenuWidth;}this.scrollTo(newScrollX, 0);}break;}case MotionEvent.ACTION_UP:{int mX = 0;if (scrollX - mMenuWidth * 0.75f > 0) {mX = mMenuWidth;}this.smoothScrollTo(mX, 0);if (onSlidingListener != null) {onSlidingListener.onSliding(this, mX == 0 ? SLIDE_STATUS_OFF : SLIDE_STATUS_ON);}break;}}lastX = x;lastY = y;return super.onTouchEvent(event);}/*** 换换滚动到的位置* @param x* @param y*/private void smoothScrollTo(int x, int y){int scrollX = getScrollX();int dealtX = x - scrollX;/*** 1.startX* 2.startY* 3.endX* 4.endY* 5.duration*/mScroller.startScroll(scrollX,0,dealtX,0,Math.abs(dealtX) * 3);invalidate();}@Overridepublic void computeScroll(){if (puteScrollOffset()){scrollTo(mScroller.getCurrX(), mScroller.getCurrY());postInvalidate();}}public void setOnSlidingListener(OnSlidingListener onSlidingListener){this.onSlidingListener = onSlidingListener;}private OnSlidingListener onSlidingListener;public interface OnSlidingListener{void onSliding(View view, int state);}}

上面SwipeMenuLayout 这个类中

(1) init(context)方法中初始化获取了屏幕的宽高值,滑动出的菜单的宽度设置,在这里只是为了展示效果我设置了一个固定的值,若是你有兴趣,可以尝试调用菜单区域的OnMeasure方法获取设置的值。

(2) OnMeasure设置默认显示方式。

(3) onTouchEvent方法中:首先 发送了一个回调事件,目的是还原上次打开的Item

ACTION_DOWN:若是上次的还未滚动完则终止上次的

ACTION_MOVE: 添加了Item范围的控制,其中scrollX (当前view的左上角相对于母视图的左上角的X轴偏移量) ,deltaX 将要滚动的值 (本次滑动的最后位置,减去上次的位置余下的需要滑动的值),x(本次滑动停留的位置),lastX(保留的上次滑动的最后位置)

ACTION_UP:scrollX (当前view的左上角相对于母视图的左上角的X轴偏移量) 如果这个偏移量的值大于了Menu视图的0.75也就是3/4那么就显示菜单(这个值可以自己设置),发送回调通知菜单展示。

(4) smoothScrollTo(int x,int y)在这个方法中主要说一下 startScroll(startX,startY,endX,endY,duration) startX:起始X轴滚动位置 startY:起始Y轴滚动位置

endX:结束X轴的位置 endY:起始Y轴的位置 duration:间隔时间

那么该类中还有一个方法没有讲:

@Overridepublic void computeScroll(){if (puteScrollOffset()){scrollTo(mScroller.getCurrX(), mScroller.getCurrY());postInvalidate();}}

这个方法是干什么的呢?当我们执行ontouch或invalidate()或postInvalidate()都会导致这个方法的执行 ,因此我们在这个方法中判断了mScroller的startScroll()方法是否执行完毕,若是完成的话,则滚动到最终位置,并重新绘制界面。

3.事件传递

这个就更简单啦,因为我们重写了ItemView的OnTouchEvent方法,因此在ListView中我们做了引用SwipeMenuLayout来对应当前滑动的Item的View 并调用该Item View的onTouchEvent()传入当前的点击事件,就完成了事件的传递。

4.后记

因为这个代码是第一个完成的初品版本,因此可能在代码中还有没有测到的bug,后面会有源码奉上,若是你在使用的过程中,发现有问题,或者有更好的思路,欢迎你在留言区留言,谢谢。

至此 这个简单的Item侧滑就实现了。代码中没有太多比较难理解的地方,只要我们能稍稍多想一点,了解View的绘制已经生命周期原理。建议大家没事多练练 多看些思路比较清晰的大神的blog 相信你会有所收获:

大神爱哥博客传送

/aigestudio

尤其喜欢他的自定义控件系列

大神郭霖博客传送

/sinyu890807

大神鸿洋博客传送

/lmj623565791

源码下载

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。