首页 > 代码库 > 自定义ListView,实现Item侧滑删除及侧滑出菜单效果
自定义ListView,实现Item侧滑删除及侧滑出菜单效果
本程序是基于网上开源项目修改而来,具体来源忘了,懒得搜了,如果有不合适的地方,请原作者联系我,我会及时回复和处理的!
该例子程序中主要包含两个ListView,一个是实现侧滑删除,一个是侧滑出菜单,代码中的注释很全,我就不在赘述了,直接贴上核心代码和效果图,程序源码在最后,如果有不太明白的地方可以留言评论,我会及时回复,大牛勿喷啊!
侧滑删除ListView:
package com.example.testslidelistview; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.ListView; import android.widget.Scroller; /** * 侧滑删除Item的ListView,此处是对网上开源的一个Listview的完善, * 实现在手指滑动时item的透明度随之改变,并增加回到原位置的动画过程 * @author zhangshuo */ public class SlideListView extends ListView { /** * 当前滑动的ListView position */ private int slidePosition; /** * 手指按下X的坐标 */ private int downY; /** * 手指按下Y的坐标 */ private int downX; /** * 屏幕宽度 */ private int screenWidth; /** * ListView的item */ private View itemView; /** * 滑动类 */ private Scroller scroller; private static final int SNAP_VELOCITY = 600; /** * 速度追踪对象 */ private VelocityTracker velocityTracker; /** * 是否响应滑动,默认为不响应 */ private boolean isSlide = false; /** * 认为是用户滑动的最小距离 */ private int mTouchSlop; /** * 移除item后的回调接口 */ private RemoveListener mRemoveListener; /** * 标示是否移除 */ private boolean isRemove = false; /** * 用来指示item滑出屏幕的方向,向左或者向右,用一个枚举值来标记 */ private RemoveDirection removeDirection; // 滑动删除方向的枚举值 public enum RemoveDirection { RIGHT, LEFT, NONE; } public SlideListView(Context context) { this(context, null); } public SlideListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); screenWidth = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth(); scroller = new Scroller(context); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } /** * 设置滑动删除的回调接口 * @param removeListener */ public void setRemoveListener(RemoveListener removeListener) { this.mRemoveListener = removeListener; } /** * 分发事件,主要做的是判断点击的是那个item, 以及通过postDelayed来设置响应左右滑动事件 */ @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { System.out.println("dispatch-->" + "down"); addVelocityTracker(event); // 假如scroller滚动还没有结束,我们直接返回 if (!scroller.isFinished()) { return false; } downX = (int) event.getX(); downY = (int) event.getY(); slidePosition = pointToPosition(downX, downY); // 无效的position, 不做任何处理 if (slidePosition == AdapterView.INVALID_POSITION) { return super.dispatchTouchEvent(event); } // 获取我们点击的item view itemView = getChildAt(slidePosition - getFirstVisiblePosition()); break; } case MotionEvent.ACTION_MOVE: { System.out.println("dispatch-->" + "move"); if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY || (Math.abs(event.getX() - downX) > mTouchSlop && Math .abs(event.getY() - downY) < mTouchSlop)) { isSlide = true; } break; } case MotionEvent.ACTION_UP: recycleVelocityTracker(); break; } return super.dispatchTouchEvent(event); } /** * 往右滑动,getScrollX()返回的是左边缘的距离,就是以View左边缘为原点到开始滑动的距离,所以向右边滑动为负值 */ private void scrollRight() { removeDirection = RemoveDirection.RIGHT; final int delta = (screenWidth + itemView.getScrollX()); // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item scroller.startScroll(itemView.getScrollX(), 0, -delta, 0, Math.abs(delta)); postInvalidate(); // 刷新itemView } /** * 向左滑动,根据上面我们知道向左滑动为正值 */ private void scrollLeft() { removeDirection = RemoveDirection.LEFT; final int delta = (screenWidth - itemView.getScrollX()); // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item scroller.startScroll(itemView.getScrollX(), 0, delta, 0, Math.abs(delta)); postInvalidate(); // 刷新itemView } /** * 滑动会原来的位置 */ private void scrollBack(){ removeDirection = RemoveDirection.NONE; scroller.startScroll(itemView.getScrollX(), 0, -itemView.getScrollX(), 0, Math.abs(itemView.getScrollX())); postInvalidate(); // 刷新itemView } /** * 根据手指滚动itemView的距离来判断是滚动到开始位置还是向左或者向右滚动 */ private void scrollByDistanceX() { // 如果向左滚动的距离大于屏幕的二分之一,就让其删除 if (itemView.getScrollX() >= screenWidth / 2) { scrollLeft(); } else if (itemView.getScrollX() <= -screenWidth / 2) { scrollRight(); } else { // 滚回到原始位置 scrollBack(); } } /** * 处理我们拖动ListView item的逻辑 */ @Override public boolean onTouchEvent(MotionEvent ev) { if (isSlide && slidePosition != AdapterView.INVALID_POSITION) { System.out.println("touch-->" + "开始"); requestDisallowInterceptTouchEvent(true); addVelocityTracker(ev); final int action = ev.getAction(); int x = (int) ev.getX(); switch (action) { case MotionEvent.ACTION_DOWN: System.out.println("touch-->" + "down"); break; case MotionEvent.ACTION_MOVE: System.out.println("touch-->" + "move"); MotionEvent cancelEvent = MotionEvent.obtain(ev); cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (ev.getActionIndex()<< MotionEvent.ACTION_POINTER_INDEX_SHIFT)); onTouchEvent(cancelEvent); int deltaX = downX - x; // 手指拖动itemView滚动, deltaX大于0向左滚动,小于0向右滚 itemView.scrollTo(deltaX, 0); // 根据手指滑动的距离,调整透明度 itemView.setAlpha(1f - Math.abs((float)deltaX/screenWidth)); return true; //拖动的时候ListView不滚动 case MotionEvent.ACTION_UP: System.out.println("touch-->" + "up"); // 手指离开的时候就不响应左右滚动 isSlide = false; int velocityX = getScrollVelocity(); if (velocityX > SNAP_VELOCITY) { scrollRight(); } else if (velocityX < -SNAP_VELOCITY) { scrollLeft(); } else { scrollByDistanceX(); } recycleVelocityTracker(); break; } } //否则直接交给ListView来处理onTouchEvent事件 return super.onTouchEvent(ev); } @Override public void computeScroll() { // 调用startScroll的时候scroller.computeScrollOffset()返回true, if (scroller.computeScrollOffset()) { // 让ListView item根据当前的滚动偏移量进行滚动 itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY()); itemView.setAlpha(1f - Math.abs((float)scroller.getCurrX()/screenWidth)); postInvalidate(); // 滚动动画结束的时候调用回调接口 if (scroller.isFinished() && removeDirection != RemoveDirection.NONE) { if (mRemoveListener == null) { throw new NullPointerException("RemoveListener is null, we should called setRemoveListener()"); } itemView.scrollTo(0, 0); itemView.setAlpha(1f); mRemoveListener.removeItem(removeDirection, slidePosition); } } } /** * 添加用户的速度跟踪器 * * @param event */ private void addVelocityTracker(MotionEvent event) { if (velocityTracker == null) { velocityTracker = VelocityTracker.obtain(); } velocityTracker.addMovement(event); } /** * 移除用户速度跟踪器 */ private void recycleVelocityTracker() { if (velocityTracker != null) { velocityTracker.recycle(); velocityTracker = null; } } /** * 获取X方向的滑动速度,大于0向右滑动,反之向左 * * @return */ private int getScrollVelocity() { velocityTracker.computeCurrentVelocity(1000); int velocity = (int) velocityTracker.getXVelocity(); return velocity; } /** * * 当ListView item滑出屏幕,回调这个接口 * 我们需要在回调方法removeItem()中移除该Item,然后刷新ListView * * @author xiaanming * */ public interface RemoveListener { public void removeItem(RemoveDirection direction, int position); } }侧滑菜单ListView:
package com.example.testslidelistview; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.widget.AdapterView; import android.widget.ListView; import android.widget.Scroller; /** * 侧向滑出菜单的ListView * 使用请注意与ListView的Item的布局配合, * 该效果的实现是基于在Item的布局中通过设置PaddingLeft和PaddingRight来隐藏左右菜单的, * 所以使用此ListView时,请务必在布局Item时使用PaddingLeft和PaddingRight; * 或者自己改写此ListView,已达到想要的实现方式 * @author zhangshuo */ public class SlideListView2 extends ListView { /**禁止侧滑模式*/ public static int MOD_FORBID = 0; /**从左向右滑出菜单模式*/ public static int MOD_LEFT = 1; /**从右向左滑出菜单模式*/ public static int MOD_RIGHT = 2; /**左右均可以滑出菜单模式*/ public static int MOD_BOTH = 3; /**当前的模式*/ private int mode = MOD_FORBID; /**左侧菜单的长度*/ private int leftLength = 0; /**右侧菜单的长度*/ private int rightLength = 0; /** * 当前滑动的ListView position */ private int slidePosition; /** * 手指按下X的坐标 */ private int downY; /** * 手指按下Y的坐标 */ private int downX; /** * ListView的item */ private View itemView; /** * 滑动类 */ private Scroller scroller; /** * 认为是用户滑动的最小距离 */ private int mTouchSlop; /** * 判断是否可以侧向滑动 */ private boolean canMove = false; /** * 标示是否完成侧滑 */ private boolean isSlided = false; public SlideListView2(Context context) { this(context, null); } public SlideListView2(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlideListView2(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); scroller = new Scroller(context); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } /** * 初始化菜单的滑出模式 * @param mode */ public void initSlideMode(int mode){ this.mode = mode; } /** * 处理我们拖动ListView item的逻辑 */ @Override public boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); int lastX = (int) ev.getX(); switch (action) { case MotionEvent.ACTION_DOWN: System.out.println("touch-->" + "down"); /*当前模式不允许滑动,则直接返回,交给ListView自身去处理*/ if(this.mode == MOD_FORBID){ return super.onTouchEvent(ev); } // 如果处于侧滑完成状态,侧滑回去,并直接返回 if (isSlided) { scrollBack(); return false; } // 假如scroller滚动还没有结束,我们直接返回 if (!scroller.isFinished()) { return false; } downX = (int) ev.getX(); downY = (int) ev.getY(); slidePosition = pointToPosition(downX, downY); // 无效的position, 不做任何处理 if (slidePosition == AdapterView.INVALID_POSITION) { return super.onTouchEvent(ev); } // 获取我们点击的item view itemView = getChildAt(slidePosition - getFirstVisiblePosition()); /*此处根据设置的滑动模式,自动获取左侧或右侧菜单的长度*/ if(this.mode == MOD_BOTH){ this.leftLength = -itemView.getPaddingLeft(); this.rightLength = -itemView.getPaddingRight(); }else if(this.mode == MOD_LEFT){ this.leftLength = -itemView.getPaddingLeft(); }else if(this.mode == MOD_RIGHT){ this.rightLength = -itemView.getPaddingRight(); } break; case MotionEvent.ACTION_MOVE: System.out.println("touch-->" + "move"); if (!canMove && slidePosition != AdapterView.INVALID_POSITION && (Math.abs(ev.getX() - downX) > mTouchSlop && Math.abs(ev .getY() - downY) < mTouchSlop)) { int offsetX = downX - lastX; if(offsetX > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)){ /*从右向左滑*/ canMove = true; }else if(offsetX < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)){ /*从左向右滑*/ canMove = true; }else{ canMove = false; } /*此段代码是为了避免我们在侧向滑动时同时出发ListView的OnItemClickListener时间*/ MotionEvent cancelEvent = MotionEvent.obtain(ev); cancelEvent .setAction(MotionEvent.ACTION_CANCEL | (ev.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); onTouchEvent(cancelEvent); } if (canMove) { /*设置此属性,可以在侧向滑动时,保持ListView不会上下滚动*/ requestDisallowInterceptTouchEvent(true); // 手指拖动itemView滚动, deltaX大于0向左滚动,小于0向右滚 int deltaX = downX - lastX; if(deltaX < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)){ /*向左滑*/ itemView.scrollTo(deltaX, 0); }else if(deltaX > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)){ /*向右滑*/ itemView.scrollTo(deltaX, 0); }else{ itemView.scrollTo(0, 0); } return true; // 拖动的时候ListView不滚动 } case MotionEvent.ACTION_UP: System.out.println("touch-->" + "up"); if (canMove){ canMove = false; scrollByDistanceX(); } break; } // 否则直接交给ListView来处理onTouchEvent事件 return super.onTouchEvent(ev); } /** * 根据手指滚动itemView的距离来判断是滚动到开始位置还是向左或者向右滚动 */ private void scrollByDistanceX() { /*当前模式不允许滑动,则直接返回*/ if(this.mode == MOD_FORBID){ return; } if(itemView.getScrollX() > 0 && (this.mode == MOD_BOTH || this.mode == MOD_RIGHT)){ /*从右向左滑*/ if (itemView.getScrollX() >= rightLength / 2) { scrollLeft(); } else { // 滚回到原始位置 scrollBack(); } }else if(itemView.getScrollX() < 0 && (this.mode == MOD_BOTH || this.mode == MOD_LEFT)){ /*从左向右滑*/ if (itemView.getScrollX() <= -leftLength / 2) { scrollRight(); } else { // 滚回到原始位置 scrollBack(); } }else{ // 滚回到原始位置 scrollBack(); } } /** * 往右滑动,getScrollX()返回的是左边缘的距离,就是以View左边缘为原点到开始滑动的距离,所以向右边滑动为负值 */ private void scrollRight() { isSlided = true; final int delta = (leftLength + itemView.getScrollX()); // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item scroller.startScroll(itemView.getScrollX(), 0, -delta, 0, Math.abs(delta)); postInvalidate(); // 刷新itemView } /** * 向左滑动,根据上面我们知道向左滑动为正值 */ private void scrollLeft() { isSlided = true; final int delta = (rightLength - itemView.getScrollX()); // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item scroller.startScroll(itemView.getScrollX(), 0, delta, 0, Math.abs(delta)); postInvalidate(); // 刷新itemView } /** * 滑动会原来的位置 */ private void scrollBack() { isSlided = false; scroller.startScroll(itemView.getScrollX(), 0, -itemView.getScrollX(), 0, Math.abs(itemView.getScrollX())); postInvalidate(); // 刷新itemView } @Override public void computeScroll() { // 调用startScroll的时候scroller.computeScrollOffset()返回true, if (scroller.computeScrollOffset()) { // 让ListView item根据当前的滚动偏移量进行滚动 itemView.scrollTo(scroller.getCurrX(), scroller.getCurrY()); postInvalidate(); } } /** * 提供给外部调用,用以将侧滑出来的滑回去 */ public void slideBack() { this.scrollBack(); } }
注意侧滑菜单ListView的使用需要配合Item布局(主要是PaddingLeft和PaddingRight这两个属性),Item布局如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="-181dp" android:paddingRight="-180dp" android:background="@color/wheat" android:orientation="horizontal" > <LinearLayout android:id="@+id/llayout_left" android:layout_width="180dp" android:layout_height="match_parent" > <RelativeLayout android:id="@+id/delete1" android:layout_width="90dp" android:layout_height="match_parent" android:background="@color/slategray" android:clickable="true" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:text="左删除" android:textColor="@color/floralwhite" android:textSize="15sp" /> </RelativeLayout> <RelativeLayout android:id="@+id/other1" android:layout_width="90dp" android:layout_height="match_parent" android:background="@color/tomato" android:clickable="true" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:text="左其他" android:textColor="@color/floralwhite" android:textSize="15sp" /> </RelativeLayout> </LinearLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_toLeftOf="@+id/llayout_right" android:orientation="vertical" > <TextView android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:paddingLeft="10dp" android:paddingRight="10dp" android:text="标题" android:textColor="@color/orange" android:textSize="17sp" /> <TextView android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="时间" android:textColor="@color/black" android:textSize="13sp" /> <TextView android:id="@+id/content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="内容" android:textColor="@color/black" android:textSize="13sp" /> </LinearLayout> <LinearLayout android:id="@+id/llayout_right" android:layout_width="180dp" android:layout_height="match_parent" android:layout_alignParentRight="true" > <RelativeLayout android:id="@+id/other2" android:layout_width="90dp" android:layout_height="match_parent" android:background="@color/slategray" android:clickable="true" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:text="右其他" android:textColor="@color/floralwhite" android:textSize="15sp" /> </RelativeLayout> <RelativeLayout android:id="@+id/delete2" android:layout_width="90dp" android:layout_height="match_parent" android:background="@color/tomato" android:clickable="true" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:text="右删除" android:textColor="@color/floralwhite" android:textSize="15sp" /> </RelativeLayout> </LinearLayout> </RelativeLayout> </LinearLayout>截图:
好了,这里是源码下载地址:http://download.csdn.net/detail/super_spy/7667793
自定义ListView,实现Item侧滑删除及侧滑出菜单效果
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。