Android 自定义ListView Item侧滑删除
本程序是基于网上开源项目修改而来,具体来源忘了,懒得搜了,如果有不合适的地方,请原作者联系我,我会及时回复和处理的!
该例子程序中主要包含两个ListView,一个是实现侧滑删除,一个是侧滑出菜单,代码中的注释很全,我就不在赘述了,直接贴上核心代码和效果图。
侧滑删除ListView:
002.package com.example.testslidelistview;003.import android.content.Context;004.import android.util.AttributeSet;005.import android.view.MotionEvent;006.import android.view.VelocityTracker;007.import android.view.View;008.import android.view.ViewConfiguration;009.import android.view.WindowManager;010.import android.widget.AdapterView;011.import android.widget.ListView;012.import android.widget.Scroller;013. 014./**015.*
侧滑删除Item的ListView,此处是对网上开源的一个Listview的完善,016.*
实现在手指滑动时item的透明度随之改变,并增加回到原位置的动画过程017.*
@author zhangshuo018.*/019.public class SlideListView extends ListView
{020./**021.*
当前滑动的ListView position022.*/023.private int slidePosition;024./**025.*
手指按下X的坐标026.*/027.private int downY;028./**029.*
手指按下Y的坐标030.*/031.private int downX;032./**033.*
屏幕宽度034.*/035.private int screenWidth;036./**037.*
ListView的item038.*/039.private View
itemView;040./**041.*
滑动类042.*/043.private Scroller
scroller;044.private static final int SNAP_VELOCITY
= 600;045./**046.*
速度追踪对象047.*/048.private VelocityTracker
velocityTracker;049./**050.*
是否响应滑动,默认为不响应051.*/052.private boolean isSlide
= false;053./**054.*
认为是用户滑动的最小距离055.*/056.private int mTouchSlop;057./**058.*
移除item后的回调接口059.*/060.private RemoveListener
mRemoveListener;061./**062.*
标示是否移除063.*/064.private boolean isRemove
= false;065./**066.*
用来指示item滑出屏幕的方向,向左或者向右,用一个枚举值来标记067.*/068.private RemoveDirection
removeDirection;069. 070.//
滑动删除方向的枚举值071.public enum RemoveDirection
{072.RIGHT,
LEFT, NONE;073.}074. 075. 076.public SlideListView(Context
context) {077.this(context, null);078.}079. 080.public SlideListView(Context
context, AttributeSet attrs) {081.this(context,
attrs, 0);082.}083. 084.public SlideListView(Context
context, AttributeSet attrs, int defStyle)
{085.super(context,
attrs, defStyle);086.screenWidth
= ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();087.scroller
= new Scroller(context);088.mTouchSlop
= ViewConfiguration.get(getContext()).getScaledTouchSlop();089.}090. 091./**092.*
设置滑动删除的回调接口093.*
@param removeListener094.*/095.public void setRemoveListener(RemoveListener
removeListener) {096.this.mRemoveListener
= removeListener;097.}098. 099./**100.*
分发事件,主要做的是判断点击的是那个item, 以及通过postDelayed来设置响应左右滑动事件101.*/102.@Override103.public boolean dispatchTouchEvent(MotionEvent
event) {104.switch (event.getAction())
{105.case MotionEvent.ACTION_DOWN:
{106.System.out.println("dispatch-->" + "down");107.addVelocityTracker(event);108. 109.//
假如scroller滚动还没有结束,我们直接返回110.if (!scroller.isFinished())
{111.return false;112.}113.downX
= (int)
event.getX();114.downY
= (int)
event.getY();115. 116.slidePosition
= pointToPosition(downX, downY);117. 118.//
无效的position, 不做任何处理119.if (slidePosition
== AdapterView.INVALID_POSITION) {120.return super.dispatchTouchEvent(event);121.}122. 123.//
获取我们点击的item view124.itemView
= getChildAt(slidePosition - getFirstVisiblePosition());125.break;126.}127.case MotionEvent.ACTION_MOVE:
{128.System.out.println("dispatch-->" + "move");129.if (Math.abs(getScrollVelocity())
> SNAP_VELOCITY130.||
(Math.abs(event.getX() - downX) > mTouchSlop && Math131..abs(event.getY()
- downY) < mTouchSlop)) {132.isSlide
= true;133. 134.}135.break;136.}137.case MotionEvent.ACTION_UP:138.recycleVelocityTracker();139.break;140.}141. 142.return super.dispatchTouchEvent(event);143.}144. 145./**146.*
往右滑动,getScrollX()返回的是左边缘的距离,就是以View左边缘为原点到开始滑动的距离,所以向右边滑动为负值147.*/148.private void scrollRight()
{149.removeDirection
= RemoveDirection.RIGHT;150.final int delta
= (screenWidth + itemView.getScrollX());151.//
调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item152.scroller.startScroll(itemView.getScrollX(), 0,
-delta, 0,153.Math.abs(delta));154.postInvalidate(); //
刷新itemView155.}156. 157./**158.*
向左滑动,根据上面我们知道向左滑动为正值159.*/160.private void scrollLeft()
{161.removeDirection
= RemoveDirection.LEFT;162.final int delta
= (screenWidth - itemView.getScrollX());163.//
调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item164.scroller.startScroll(itemView.getScrollX(), 0,
delta, 0,165.Math.abs(delta));166.postInvalidate(); //
刷新itemView167.}168. 169./**170.*
滑动会原来的位置171.*/172.private void scrollBack(){173.removeDirection
= RemoveDirection.NONE;174.scroller.startScroll(itemView.getScrollX(), 0,
-itemView.getScrollX(), 0,175.Math.abs(itemView.getScrollX()));176.postInvalidate(); //
刷新itemView177.}178. 179./**180.*
根据手指滚动itemView的距离来判断是滚动到开始位置还是向左或者向右滚动181.*/182.private void scrollByDistanceX()
{183.//
如果向左滚动的距离大于屏幕的二分之一,就让其删除184.if (itemView.getScrollX()
>= screenWidth / 2)
{185.scrollLeft();186.} else if (itemView.getScrollX()
<= -screenWidth / 2)
{187.scrollRight();188.} else {189.//
滚回到原始位置190.scrollBack();191.}192. 193.}194. 195./**196.*
处理我们拖动ListView item的逻辑197.*/198.@Override199.public boolean onTouchEvent(MotionEvent
ev) {200.if (isSlide
&& slidePosition != AdapterView.INVALID_POSITION) {201.System.out.println("touch-->" + "开始");202.requestDisallowInterceptTouchEvent(true);203.addVelocityTracker(ev);204.final int action
= ev.getAction();205.int x
= (int)
ev.getX();206.switch (action)
{207.case MotionEvent.ACTION_DOWN:208.System.out.println("touch-->" + "down");209.break;210.case MotionEvent.ACTION_MOVE:211.System.out.println("touch-->" + "move");212.MotionEvent
cancelEvent = MotionEvent.obtain(ev);213.cancelEvent.setAction(MotionEvent.ACTION_CANCEL
|214.(ev.getActionIndex()<<
MotionEvent.ACTION_POINTER_INDEX_SHIFT));215.onTouchEvent(cancelEvent);216. 217.int deltaX
= downX - x;218. 219.//
手指拖动itemView滚动, deltaX大于0向左滚动,小于0向右滚220.itemView.scrollTo(deltaX, 0);221.//
根据手指滑动的距离,调整透明度222.itemView.setAlpha(1f
- Math.abs((float)deltaX/screenWidth));223. 224.return true; //拖动的时候ListView不滚动225.case MotionEvent.ACTION_UP:226.System.out.println("touch-->" + "up");227.//
手指离开的时候就不响应左右滚动228.isSlide
= false;229.int velocityX
= getScrollVelocity();230.if (velocityX
> SNAP_VELOCITY) {231.scrollRight();232.} else if (velocityX
< -SNAP_VELOCITY) {233.scrollLeft();234.} else {235.scrollByDistanceX();236.}237. 238.recycleVelocityTracker();239. 240.break;241.}242.}243. 244.//否则直接交给ListView来处理onTouchEvent事件245.return super.onTouchEvent(ev);246.}247. 248.@Override249.public void computeScroll()
{250.//
调用startScroll的时候scroller.computeScrollOffset()返回true,251.if (scroller.computeScrollOffset())
{252.//
让ListView item根据当前的滚动偏移量进行滚动253.itemView.scrollTo(scroller.getCurrX(),
scroller.getCurrY());254. 255.itemView.setAlpha(1f
- Math.abs((float)scroller.getCurrX()/screenWidth));256. 257.postInvalidate();258. 259.//
滚动动画结束的时候调用回调接口260.if (scroller.isFinished()
&& removeDirection != RemoveDirection.NONE) {261.if (mRemoveListener
== null)
{262.throw new NullPointerException("RemoveListener
is null, we should called setRemoveListener()");263.}264.itemView.scrollTo(0, 0);265.itemView.setAlpha(1f);266.mRemoveListener.removeItem(removeDirection,
slidePosition);267.}268.}269.}270. 271./**272.*
添加用户的速度跟踪器273.*274.*
@param event275.*/276.private void addVelocityTracker(MotionEvent
event) {277.if (velocityTracker
== null)
{278.velocityTracker
= VelocityTracker.obtain();279.}280. 281.velocityTracker.addMovement(event);282.}283. 284./**285.*
移除用户速度跟踪器286.*/287.private void recycleVelocityTracker()
{288.if (velocityTracker
!= null)
{289.velocityTracker.recycle();290.velocityTracker
= null;291.}292.}293. 294./**295.*
获取X方向的滑动速度,大于0向右滑动,反之向左296.*297.*
@return298.*/299.private int getScrollVelocity()
{300.velocityTracker.computeCurrentVelocity(1000);301.int velocity
= (int)
velocityTracker.getXVelocity();302.return velocity;303.}304. 305./**306.*307.*
当ListView item滑出屏幕,回调这个接口308.*
我们需要在回调方法removeItem()中移除该Item,然后刷新ListView309.*310.*
@author xiaanming311.*312.*/313.public interface RemoveListener
{314.public void removeItem(RemoveDirection
direction, int position);315.}316. 317.}
001.package com.example.testslidelistview;002. 003.import android.content.Context;004.import android.util.AttributeSet;005.import android.view.MotionEvent;006.import android.view.View;007.import android.view.ViewConfiguration;008.import android.widget.AdapterView;009.import android.widget.ListView;010.import android.widget.Scroller;011. 012./**013.*
侧向滑出菜单的ListView014.*
使用请注意与ListView的Item的布局配合,015.*
该效果的实现是基于在Item的布局中通过设置PaddingLeft和PaddingRight来隐藏左右菜单的,016.*
所以使用此ListView时,请务必在布局Item时使用PaddingLeft和PaddingRight;017.*
或者自己改写此ListView,已达到想要的实现方式018.*
@author zhangshuo019.*/020.public class SlideListView2 extends ListView
{021. 022./**禁止侧滑模式*/023.public static int MOD_FORBID
= 0;024./**从左向右滑出菜单模式*/025.public static int MOD_LEFT
= 1;026./**从右向左滑出菜单模式*/027.public static int MOD_RIGHT
= 2;028./**左右均可以滑出菜单模式*/029.public static int MOD_BOTH
= 3;030./**当前的模式*/031.private int mode
= MOD_FORBID;032./**左侧菜单的长度*/033.private int leftLength
= 0;034./**右侧菜单的长度*/035.private int rightLength
= 0;036. 037./**038.*
当前滑动的ListView position039.*/040.private int slidePosition;041./**042.*
手指按下X的坐标043.*/044.private int downY;045./**046.*
手指按下Y的坐标047.*/048.private int downX;049./**050.*
ListView的item051.*/052.private View
itemView;053./**054.*
滑动类055.*/056.private Scroller
scroller;057./**058.*
认为是用户滑动的最小距离059.*/060.private int mTouchSlop;061. 062./**063.*
判断是否可以侧向滑动064.*/065.private boolean canMove
= false;066./**067.*
标示是否完成侧滑068.*/069.private boolean isSlided
= false;070. 071.public SlideListView2(Context
context) {072.this(context, null);073.}074. 075.public SlideListView2(Context
context, AttributeSet attrs) {076.this(context,
attrs, 0);077.}078. 079.public SlideListView2(Context
context, AttributeSet attrs, int defStyle)
{080.super(context,
attrs, defStyle);081.scroller
= new Scroller(context);082.mTouchSlop
= ViewConfiguration.get(getContext()).getScaledTouchSlop();083.}084. 085./**086.*
初始化菜单的滑出模式087.*
@param mode088.*/089.public void initSlideMode(int mode){090.this.mode
= mode;091.}092. 093./**094.*
处理我们拖动ListView item的逻辑095.*/096.@Override097.public boolean onTouchEvent(MotionEvent
ev) {098. 099.final int action
= ev.getAction();100.int lastX
= (int)
ev.getX();101. 102.switch (action)
{103.case MotionEvent.ACTION_DOWN:104.System.out.println("touch-->" + "down");105. 106./*当前模式不允许滑动,则直接返回,交给ListView自身去处理*/107.if(this.mode
== MOD_FORBID){108.return super.onTouchEvent(ev);109.}110. 111.//
如果处于侧滑完成状态,侧滑回去,并直接返回112.if (isSlided)
{113.scrollBack();114.return false;115.}116.//
假如scroller滚动还没有结束,我们直接返回117.if (!scroller.isFinished())
{118.return false;119.}120.downX
= (int)
ev.getX();121.downY
= (int)
ev.getY();122. 123.slidePosition
= pointToPosition(downX, downY);124. 125.//
无效的position, 不做任何处理126.if (slidePosition
== AdapterView.INVALID_POSITION) {127.return super.onTouchEvent(ev);128.}129. 130.//
获取我们点击的item view131.itemView
= getChildAt(slidePosition - getFirstVisiblePosition());132. 133./*此处根据设置的滑动模式,自动获取左侧或右侧菜单的长度*/134.if(this.mode
== MOD_BOTH){135.this.leftLength
= -itemView.getPaddingLeft();136.this.rightLength
= -itemView.getPaddingRight();137.}else if(this.mode
== MOD_LEFT){138.this.leftLength
= -itemView.getPaddingLeft();139.}else if(this.mode
== MOD_RIGHT){140.this.rightLength
= -itemView.getPaddingRight();141.}142. 143.break;144.case MotionEvent.ACTION_MOVE:145.System.out.println("touch-->" + "move");146. 147.if (!canMove148.&&
slidePosition != AdapterView.INVALID_POSITION149.&&
(Math.abs(ev.getX() - downX) > mTouchSlop && Math.abs(ev150..getY()
- downY) < mTouchSlop)) {151.int offsetX
= downX - lastX;152.if(offsetX
> 0 &&
(this.mode
== MOD_BOTH || this.mode
== MOD_RIGHT)){153./*从右向左滑*/154.canMove
= true;155.}else if(offsetX
< 0 &&
(this.mode
== MOD_BOTH || this.mode
== MOD_LEFT)){156./*从左向右滑*/157.canMove
= true;158.}else{159.canMove
= false;160.}161./*此段代码是为了避免我们在侧向滑动时同时出发ListView的OnItemClickListener时间*/162.MotionEvent
cancelEvent = MotionEvent.obtain(ev);163.cancelEvent164..setAction(MotionEvent.ACTION_CANCEL165.|
(ev.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));166.onTouchEvent(cancelEvent);167.}168.if (canMove)
{169./*设置此属性,可以在侧向滑动时,保持ListView不会上下滚动*/170.requestDisallowInterceptTouchEvent(true);171. 172.//
手指拖动itemView滚动, deltaX大于0向左滚动,小于0向右滚173.int deltaX
= downX - lastX;174.if(deltaX
< 0 &&
(this.mode
== MOD_BOTH || this.mode
== MOD_LEFT)){175./*向左滑*/176.itemView.scrollTo(deltaX, 0);177.}else if(deltaX
> 0 &&
(this.mode
== MOD_BOTH || this.mode
== MOD_RIGHT)){178./*向右滑*/179.itemView.scrollTo(deltaX, 0);180.}else{181.itemView.scrollTo(0, 0);182.}183.return true; //
拖动的时候ListView不滚动184.}185.case MotionEvent.ACTION_UP:186.System.out.println("touch-->" + "up");187.if (canMove){188.canMove
= false;189.scrollByDistanceX();190.}191.break;192.}193. 194.//
否则直接交给ListView来处理onTouchEvent事件195.return super.onTouchEvent(ev);196.}197. 198./**199.*
根据手指滚动itemView的距离来判断是滚动到开始位置还是向左或者向右滚动200.*/201.private void scrollByDistanceX()
{202./*当前模式不允许滑动,则直接返回*/203.if(this.mode
== MOD_FORBID){204.return;205.}206.if(itemView.getScrollX()
> 0 &&
(this.mode
== MOD_BOTH || this.mode
== MOD_RIGHT)){207./*从右向左滑*/208.if (itemView.getScrollX()
>= rightLength / 2)
{209.scrollLeft();210.} else {211.//
滚回到原始位置212.scrollBack();213.}214.}else if(itemView.getScrollX()
< 0 &&
(this.mode
== MOD_BOTH || this.mode
== MOD_LEFT)){215./*从左向右滑*/216.if (itemView.getScrollX()
<= -leftLength / 2)
{217.scrollRight();218.} else {219.//
滚回到原始位置220.scrollBack();221.}222.}else{223.//
滚回到原始位置224.scrollBack();225.}226. 227.}228. 229./**230.*
往右滑动,getScrollX()返回的是左边缘的距离,就是以View左边缘为原点到开始滑动的距离,所以向右边滑动为负值231.*/232.private void scrollRight()
{233.isSlided
= true;234.final int delta
= (leftLength + itemView.getScrollX());235.//
调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item236.scroller.startScroll(itemView.getScrollX(), 0,
-delta, 0,237.Math.abs(delta));238.postInvalidate(); //
刷新itemView239.}240. 241./**242.*
向左滑动,根据上面我们知道向左滑动为正值243.*/244.private void scrollLeft()
{245.isSlided
= true;246.final int delta
= (rightLength - itemView.getScrollX());247.//
调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item248.scroller.startScroll(itemView.getScrollX(), 0,
delta, 0,249.Math.abs(delta));250.postInvalidate(); //
刷新itemView251.}252. 253./**254.*
滑动会原来的位置255.*/256.private void scrollBack()
{257.isSlided
= false;258.scroller.startScroll(itemView.getScrollX(), 0,
-itemView.getScrollX(),259.0,
Math.abs(itemView.getScrollX()));260.postInvalidate(); //
刷新itemView261.}262. 263.@Override264.public void computeScroll()
{265.//
调用startScroll的时候scroller.computeScrollOffset()返回true,266.if (scroller.computeScrollOffset())
{267.//
让ListView item根据当前的滚动偏移量进行滚动268.itemView.scrollTo(scroller.getCurrX(),
scroller.getCurrY());269. 270.postInvalidate();271.}272.}273. 274./**275.*
提供给外部调用,用以将侧滑出来的滑回去276.*/277.public void slideBack()
{278.this.scrollBack();279.}280. 281.}注意侧滑菜单ListView的使用需要配合Item布局(主要是PaddingLeft和PaddingRight这两个属性),Item布局如下:
001.<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"002.xmlns:tools="http://schemas.android.com/tools"003.android:layout_width="match_parent"004.android:layout_height="match_parent"005.android:paddingLeft="-181dp"006.android:paddingRight="-180dp"007.android:background="@color/wheat"008.android:orientation="horizontal" >009. 010.<LinearLayout011.android:id="@+id/llayout_left"012.android:layout_width="180dp"013.android:layout_height="match_parent" >014. 015.<RelativeLayout016.android:id="@+id/delete1"017.android:layout_width="90dp"018.android:layout_height="match_parent"019.android:background="@color/slategray"020.android:clickable="true" >021. 022.<TextView023.android:layout_width="wrap_content"024.android:layout_height="wrap_content"025.android:layout_centerInParent="true"026.android:gravity="center"027.android:text="左删除"028.android:textColor="@color/floralwhite"029.android:textSize="15sp" />030.</RelativeLayout>031. 032.<RelativeLayout033.android:id="@+id/other1"034.android:layout_width="90dp"035.android:layout_height="match_parent"036.android:background="@color/tomato"037.android:clickable="true" >038. 039.<TextView040.android:layout_width="wrap_content"041.android:layout_height="wrap_content"042.android:layout_centerInParent="true"043.android:gravity="center"044.android:text="左其他"045.android:textColor="@color/floralwhite"046.android:textSize="15sp" />047.</RelativeLayout>048.</LinearLayout>049. 050. 051.<RelativeLayout052.android:layout_width="match_parent"053.android:layout_height="match_parent">054.<LinearLayout055.android:layout_width="match_parent"056.android:layout_height="match_parent"057.android:layout_toLeftOf="@+id/llayout_right"058.android:orientation="vertical" >059. 060.<TextView061.android:id="@+id/title"062.android:layout_width="match_parent"063.android:layout_height="wrap_content"064.android:gravity="center_vertical"065.android:paddingLeft="10dp"066.android:paddingRight="10dp"067.android:text="标题"068.android:textColor="@color/orange"069.android:textSize="17sp" />070. 071.<TextView072.android:id="@+id/time"073.android:layout_width="wrap_content"074.android:layout_height="wrap_content"075.android:layout_marginLeft="10dp"076.android:text="时间"077.android:textColor="@color/black"078.android:textSize="13sp" />079. 080.<TextView081.android:id="@+id/content"082.android:layout_width="wrap_content"083.android:layout_height="wrap_content"084.android:layout_marginLeft="10dp"085.android:text="内容"086.android:textColor="@color/black"087.android:textSize="13sp" />088.</LinearLayout>089. 090.<LinearLayout091.android:id="@+id/llayout_right"092.android:layout_width="180dp"093.android:layout_height="match_parent"094.android:layout_alignParentRight="true" >095. 096.<RelativeLayout097.android:id="@+id/other2"098.android:layout_width="90dp"099.android:layout_height="match_parent"100.android:background="@color/slategray"101.android:clickable="true" >102. 103.<TextView104.android:layout_width="wrap_content"105.android:layout_height="wrap_content"106.android:layout_centerInParent="true"107.android:gravity="center"108.android:text="右其他"109.android:textColor="@color/floralwhite"110.android:textSize="15sp" />111.</RelativeLayout>112. 113.<RelativeLayout114.android:id="@+id/delete2"115.android:layout_width="90dp"116.android:layout_height="match_parent"117.android:background="@color/tomato"118.android:clickable="true" >119. 120.<TextView121.android:layout_width="wrap_content"122.android:layout_height="wrap_content"123.android:layout_centerInParent="true"124.android:gravity="center"125.android:text="右删除"126.android:textColor="@color/floralwhite"127.android:textSize="15sp" />128.</RelativeLayout>129.</LinearLayout>130.</RelativeLayout>131. 132. 133.</LinearLayout>截图:

浙公网安备 33010602011771号