首页 > 其他分享 >listview滑动删除

listview滑动删除

时间:2022-12-15 23:08:47浏览次数:64  
标签:动画 listview 删除 item Item 滑动 ListView 我们


今天还是给大家带来自定义控件的编写,自定义一个ListView的左右滑动删除Item的效果,这个效果之前已经实现过了,有兴趣的可以看下​​Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果​​,之前使用的是滑动类Scroller来实现的,但是看了下通知栏的左右滑动删除效果,确实很棒,当我们滑动Item超过一半的时候,item的透明度就变成了0,我们就知道抬起手指的时候item就被删除了,当item的透明度不为0的时候,我们抬起手指Item会回到起始位置,这样我们就知道拖动到什么位置item会删除,什么位置Item不删除,用户体验更好了,还有一个效果,就是我们滑动删除了item的时候,ListView的其他item会出现向上或者向下滚动的效果,感觉效果很棒,所以在GitHub上面搜索了下,发现很多开源库都有这个效果,比如​​ListViewAnimations​​​, ​​android-swipelistview​​​等等,我看了下实现原理,使用的是Jake Wharton的动画开源库​​NineOldAndroids​​,这个库究竟是干嘛的呢?在API3.0(Honeycomb), SDK新增了一个android.animation包,里面的类是实现动画效果相关的类,通过Honeycomb API,能够实现非常复杂的动画效果,但是如果开发者想在3.0以下使用这一套API, 则需要使用开源框架Nine Old Androids,在这个库中会根据我们运行的机器判断其SDK版本,如果是API3.0以上则使用Android自带的动画类,否则就使用Nine Old Androids库中,这是一个兼容库,接下来我们就来看看这个效果的具体实现吧

实现该效果的主要思路

  1. 先根据手指触摸的点来获取点击的是ListView的哪一个Item
  2. 当手指在屏幕上面滑动的时候,我们要使得Item跟随手指的滑动而滑动
  3. 当我们抬起手指的时候,我们根据滑动的距离或者手指在屏幕上面的速度来判断Item是滑出屏幕还是滑动至其实位置
  4. Item滑出屏幕时,使ListView的其他item产生向上挤压或者向下挤压的效果

大致的思路这是这四步,其中的一些细节接下来我会一一为大家解答的,接下来我们就用代码来实现这种效果吧

首先我们新建一个工程,叫Swipedismisslistview,我们需要将Nine Old Androids这个库引入到工程,大家可以去​​https://github.com/JakeWharton/NineOldAndroids​​下载,可以使用Jar包,也可以使用工程库的形式引入到我们自己的工程,我们还需要自定义一个ListView,我们先看代码然后给大家讲解下具体的功能实现


[java] ​​ view plain​​ ​​cop



  1. package
  2.   
  3. import static
  4. import static
  5. import
  6. import
  7. import
  8. import
  9. import
  10. import
  11. import
  12. import
  13. import
  14.   
  15. import
  16. import
  17. import
  18. import
  19. import
  20. /**
  21.  * @blog http://blog.csdn.net/xiaanming
  22.  * 
  23.  * @author xiaanming
  24.  *
  25.  */
  26. public class SwipeDismissListView extends
  27. /**
  28.      * 认为是用户滑动的最小距离
  29.      */
  30. private int
  31. /**
  32.      * 滑动的最小速度
  33.      */
  34. private int
  35. /**
  36.      * 滑动的最大速度
  37.      */
  38. private int
  39. /**
  40.      * 执行动画的时间
  41.      */
  42. protected long mAnimationTime = 150;  
  43. /**
  44.      * 用来标记用户是否正在滑动中
  45.      */
  46. private boolean
  47. /**
  48.      * 滑动速度检测类
  49.      */
  50. private
  51. /**
  52.      * 手指按下的position
  53.      */
  54. private int
  55. /**
  56.      * 按下的item对应的View
  57.      */
  58. private
  59. private float
  60. private float
  61. /**
  62.      * item的宽度
  63.      */
  64. private int
  65. /**
  66.      * 当ListView的Item滑出界面回调的接口
  67.      */
  68. private
  69.   
  70. /**
  71.      * 设置动画时间
  72.      * 
  73.      * @param mAnimationTime
  74.      */
  75. public void setmAnimationTime(long
  76. this.mAnimationTime = mAnimationTime;  
  77.     }  
  78.   
  79. /**
  80.      * 设置删除回调接口
  81.      * 
  82.      * @param onDismissCallback
  83.      */
  84. public void
  85. this.onDismissCallback = onDismissCallback;  
  86.     }  
  87.   
  88. public
  89. this(context, null);  
  90.     }  
  91.   
  92. public
  93. this(context, attrs, 0);  
  94.     }  
  95.   
  96. public
  97. int
  98. super(context, attrs, defStyle);  
  99.   
  100.         ViewConfiguration vc = ViewConfiguration.get(context);  
  101.         mSlop = vc.getScaledTouchSlop();  
  102. 8; //获取滑动的最小速度
  103. //获取滑动的最大速度
  104.     }  
  105.   
  106.       
  107. @Override
  108. public boolean
  109. switch
  110. case
  111.             handleActionDown(ev);  
  112. break;  
  113. case
  114. return
  115. case
  116.             handleActionUp(ev);  
  117. break;  
  118.         }  
  119. return super.onTouchEvent(ev);  
  120.     }  
  121.   
  122. /**
  123.      * 按下事件处理
  124.      * 
  125.      * @param ev
  126.      * @return
  127.      */
  128. private void
  129.         mDownX = ev.getX();  
  130.         mDownY = ev.getY();  
  131.           
  132. int) mDownX, (int) mDownY);  
  133.   
  134. if
  135. return;  
  136.         }  
  137.   
  138.         mDownView = getChildAt(mDownPosition - getFirstVisiblePosition());  
  139.   
  140. if (mDownView != null) {  
  141.             mViewWidth = mDownView.getWidth();  
  142.         }  
  143.   
  144. //加入速度检测
  145.         mVelocityTracker = VelocityTracker.obtain();  
  146.         mVelocityTracker.addMovement(ev);  
  147.     }  
  148.       
  149.   
  150. /**
  151.      * 处理手指滑动的方法
  152.      * 
  153.      * @param ev
  154.      * @return
  155.      */
  156. private boolean
  157. if (mVelocityTracker == null || mDownView == null) {  
  158. return super.onTouchEvent(ev);  
  159.         }  
  160.   
  161. // 获取X方向滑动的距离
  162. float
  163. float
  164.   
  165. // X方向滑动的距离大于mSlop并且Y方向滑动的距离小于mSlop,表示可以滑动
  166. if
  167. true;  
  168.               
  169. //当手指滑动item,取消item的点击事件,不然我们滑动Item也伴随着item点击事件的发生
  170.             MotionEvent cancelEvent = MotionEvent.obtain(ev);  
  171.             cancelEvent.setAction(MotionEvent.ACTION_CANCEL |  
  172.                        (ev.getActionIndex()<< MotionEvent.ACTION_POINTER_INDEX_SHIFT));  
  173.             onTouchEvent(cancelEvent);  
  174.         }  
  175.   
  176. if
  177. // 跟谁手指移动item
  178.             ViewHelper.setTranslationX(mDownView, deltaX);  
  179. // 透明度渐变
  180.             ViewHelper.setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX)/ mViewWidth)));  
  181.   
  182. // 手指滑动的时候,返回true,表示SwipeDismissListView自己处理onTouchEvent,其他的就交给父类来处理
  183. return true;  
  184.         }  
  185.   
  186. return super.onTouchEvent(ev);  
  187.   
  188.     }  
  189.   
  190. /**
  191.      * 手指抬起的事件处理
  192.      * @param ev
  193.      */
  194. private void
  195. if (mVelocityTracker == null || mDownView == null|| !mSwiping) {  
  196. return;  
  197.         }  
  198.   
  199. float
  200.           
  201. //通过滑动的距离计算出X,Y方向的速度
  202. 1000);  
  203. float
  204. float
  205.           
  206. boolean dismiss = false; //item是否要滑出屏幕
  207. boolean dismissRight = false;//是否往右边删除
  208.           
  209. //当拖动item的距离大于item的一半,item滑出屏幕
  210. if (Math.abs(deltaX) > mViewWidth / 2) {  
  211. true;  
  212. 0;  
  213.               
  214. //手指在屏幕滑动的速度在某个范围内,也使得item滑出屏幕
  215. else if
  216.                 && velocityX <= mMaxFlingVelocity && velocityY < velocityX) {  
  217. true;  
  218. 0;  
  219.         }  
  220.   
  221. if
  222.             ViewPropertyAnimator.animate(mDownView)  
  223. //X轴方向的移动距离
  224. 0)  
  225.                     .setDuration(mAnimationTime)  
  226. new
  227. @Override
  228. public void
  229. //Item滑出界面之后执行删除
  230.                             performDismiss(mDownView, mDownPosition);  
  231.                         }  
  232.                     });  
  233. else
  234. //将item滑动至开始位置
  235.             ViewPropertyAnimator.animate(mDownView)  
  236. 0)  
  237. 1)     
  238. null);  
  239.         }  
  240.           
  241. //移除速度检测
  242. if(mVelocityTracker != null){  
  243.             mVelocityTracker.recycle();  
  244. null;  
  245.         }  
  246.           
  247. false;  
  248.     }  
  249.       
  250.   
  251.       
  252. /**
  253.      * 在此方法中执行item删除之后,其他的item向上或者向下滚动的动画,并且将position回调到方法onDismiss()中
  254.      * @param dismissView
  255.      * @param dismissPosition
  256.      */
  257. private void performDismiss(final View dismissView, final int
  258. final ViewGroup.LayoutParams lp = dismissView.getLayoutParams();//获取item的布局参数
  259. final int originalHeight = dismissView.getHeight();//item的高度
  260.   
  261. 0).setDuration(mAnimationTime);  
  262.         animator.start();  
  263.   
  264. new
  265. @Override
  266. public void
  267. if (onDismissCallback != null) {  
  268.                     onDismissCallback.onDismiss(dismissPosition);  
  269.                 }  
  270.   
  271. //这段代码很重要,因为我们并没有将item从ListView中移除,而是将item的高度设置为0
  272. //所以我们在动画执行完毕之后将item设置回来
  273.                 ViewHelper.setAlpha(dismissView, 1f);  
  274. 0);  
  275.                 ViewGroup.LayoutParams lp = dismissView.getLayoutParams();  
  276.                 lp.height = originalHeight;  
  277.                 dismissView.setLayoutParams(lp);  
  278.   
  279.             }  
  280.         });  
  281.   
  282. new
  283. @Override
  284. public void
  285. //这段代码的效果是ListView删除某item之后,其他的item向上滑动的效果
  286.                 lp.height = (Integer) valueAnimator.getAnimatedValue();  
  287.                 dismissView.setLayoutParams(lp);  
  288.             }  
  289.         });  
  290.   
  291.     }  
  292.   
  293. /**
  294.      * 删除的回调接口
  295.      * 
  296.      * @author xiaanming
  297.      * 
  298.      */
  299. public interface
  300. public void onDismiss(int
  301.     }  
  302.   
  303. }  

看过

​​Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果​​你会发现,这个自定义的SwipeDismissListView只重写了onTouchEvent()方法,其实我们重写这一个方法就能实现我们需要的效果

1. 我们先看手指按下屏幕的处理方法handleActionDown();该方法里面根据我们手指按下的点根据pointToPosition()方法来获取我们点击的position,然后利用getChildAt()来获取我们按下的item的View对象,并且加入手指在屏幕滑动的速度检查,这一步相对来说还是比较简单

2. 接下来就是手指在屏幕上面滑动的处理方法handleActionMove(),这个方法就稍微的复杂些,我们需要根据手指在X轴的滑动距离和Y轴的滑动距离来判断是ListView item的水平滑动还是ListView的上下滑动,当满足Math.abs(deltaX) > mSlop && Math.abs(deltaY) < mSlop这个条件时候,我们用一个布尔值mSwiping来标记Item现在处于水平滑动的状态,这时候我们需要处理Item跟随手指的滑动而滑动的逻辑,我们使用ViewHelper来处理Item的滑动逻辑,这个类会根据机器的SDK版本来判断使用Android系统的API还是NineOldandroids中自己实现的API使得View滑动的效果,NineOldandroids中主要使用Camera(可以实现各种复杂动画效果的类),我们直接使用ViewHelper的setTranslationX()和setAlpha()就实现了item滑动和透明度渐变的效果,为了使得我们在滑动item的时候,ListView不上下滚动,我们必须返回true来屏蔽ListView的上下滚动,这里需要我们要非常熟悉Android的事件分发机制,这里我就不说明了,大家不了解的去网上找找相关的文章看看
还有一个问题,就是当我们滑动ListView的item的时候,会伴随着item的点击事件,这不是我们想要的效果,所以当Item滑动的时候我们需要取消ListView Item的点击事件


3. 在看手指抬起的时候的处理方法handleActionUp(),这里面需要根据手指的滑动速度或者Item移动的距离来判断Item是滑出屏幕还是滑动至起始位置,并且要判断item向左还是向右滑出屏幕等等逻辑,具体的逻辑可以看代码,相信大家都看得懂.

我这里要说说ViewPropertyAnimator类,这个类能更好的实现一个View同时进行多个动画的功能,当然我们也可以使用ObjectAnimator利用AnimatorSet来实现一个View上的多个同时进行的动画效果,例如我们可以将


[java] ​​ view plain​​ ​​co



  1. ViewPropertyAnimator.animate(mDownView)  
  2. //X轴方向的移动距离
  3. 0)  
  4.         .setDuration(mAnimationTime)  
  5. new
  6. @Override
  7. public void
  8. //Item滑出界面之后执行删除
  9.                 performDismiss(mDownView, mDownPosition);  
  10.             }  
  11.         });  

替换成

[java] ​​ view plain​​ ​​cop



  1. AnimatorSet set = new
  2. "translationX", dismissRight ? mViewWidth : -mViewWidth),   
  3. "alpha", 0));  
  4.             set.setDuration(mAnimationTime).start();  
  5. new
  6. @Override
  7. public void
  8. //Item滑出界面之后执行删除
  9.                             performDismiss(mDownView, mDownPosition);  
  10.                         }  
  11.                     });  

在效果上面是一样的,但是ViewPropertyAnimator在性能上要比使用ObjectAnimator来实现多个同时进行的动画要高的多,举个例子,假如要对View使用移动和透明度的动画,使用ViewPropertyAnimator的话,某个时间点上我们只需要调用一次invalidate()方法刷新界面就行了,而使用ObjectAnimator的话,移动的动画需要调用invalidate(),透明度的动画也需要调用invalidate()方法,在性能上使用AnimationSet比ViewPropertyAnimator要低,但是有的时候我们还是需要使用ObjectAnimator,比如,在某个时间内,我们需要将View先变大在变小在变大等复杂情况,这时候ObjectAnimator就派上用场了,例如

[java] ​​ view plain​​ ​​cop



  1. ObjectAnimator.ofInt(mDownView, "scaleX", 0 ,100 ,0, 100).setDuration(100).start()  


通过上面的几步我们就实现了ListView的左右滑动删除item的效果啦,但是还有一个效果,item删除之后,ListView的其他item向上或者向下缓缓滑动的效果,实现这个也很容易,就是动态设置item的高度,item高度逐渐变小,这样其他的item就会出现向上或者向下挤压的效果啦!


4. 这里我们使用的是ValueAnimator这个类,这个类并不是针对View作用的动画,而是对某个值作用的动画,他默认使用的Interpolator(插补器)是AccelerateDecelerateInterpolator(开始和结束的时候慢,中间快) , 举个很简单的例子,我们在10秒内使用ValueAnimator将某个值从0变化到100,如果使用LinearInterpolator(线性插补器,匀速变化)在第2秒的时候,这个值变成了20,而是用AccelerateDecelerateInterpolator,可能在第二秒的时候这个值为15或者13,所以我们在ValueAnimator变化的时候设置值动画变化的监听器AnimatorUpdateListener就知道某个时间这个值变成了多少,从而对View的某个属性进行设置(例如大小),所以ValueAnimator是间接的对View设置动画的

了解了ValueAnimator的使用原理,我们就可以现实上面的动画效果了,我们使用ValueAnimator将item的高度变成0,设置ValueAnimator变化的监听,我们在回调函数onAnimationUpdate()中动态的设置item的高度, 然后添加AnimatorListener监听动画的状态(例如动画开始,结束,重复等)监听,在动画结束的回调函数onAnimationEnd()中删除该item的数据,调用notifyDataSetChanged刷新ListView,看看下面这段代码


[java] ​​ view plain​​ ​​cop



  1. ViewHelper.setAlpha(dismissView, 1f);  
  2. 0);  
  3.                 ViewGroup.LayoutParams lp = dismissView.getLayoutParams();  
  4.                 lp.height = originalHeight;  
  5.                 dismissView.setLayoutParams(lp);  

我们使用动画只是将item移动出了屏幕,并且将item的高度设置为了0,并没有将item的View从ListView中Remove掉,况且ListView也不能直接Remove掉Item的,只能将数据源删除,在调用notifyDataSetChanged()刷新,所以我们需要将刚刚滑出屏幕高度设置为0的Item恢复回来


自定义控件的代码我们已经编写完了,接下来我们就要使用它了,先看界面的布局代码


[java] ​​ view plain​​ ​​co



  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. "http://schemas.android.com/tools"
  3. "match_parent"
  4. "match_parent">    
  5.     
  6.     <com.example.swipedismisslistview.SwipeDismissListView  
  7. "@+id/swipeDismissListView"
  8. "match_parent"
  9. "match_parent"
  10. "@android:color/transparent"
  11. "@android:color/transparent">    
  12.     </com.example.swipedismisslistview.SwipeDismissListView>  
  13.     
  14. </RelativeLayout>    


很简单,一个RelativeLayout包裹我们自定义的ListView控件,接下来就是主界面的代码编写,跟平常的ListView使用一样,但是我们需要设置OnDismissCallback()监听,在

onDismiss()中删除该位置对于的数据,刷新ListView


[java] ​​ view plain​​ ​​co


  1. package
  2.   
  3. import
  4. import
  5.   
  6. import
  7. import
  8. import
  9. import
  10. import
  11. import
  12. import
  13.   
  14. import
  15.   
  16. public class SwipeActivity extends
  17. private
  18. private
  19. private List<String> dataSourceList = new
  20.   
  21. @Override
  22. protected void
  23. super.onCreate(savedInstanceState);  
  24.         setContentView(R.layout.activity_swipe);  
  25.         init();  
  26.     }  
  27.   
  28. private void
  29.         swipeDismissListView = (SwipeDismissListView) findViewById(R.id.swipeDismissListView);  
  30. for (int i = 0; i < 20; i++) {  
  31. "滑动删除"
  32.         }  
  33.   
  34. new ArrayAdapter<String>(this,  
  35.                 android.R.layout.simple_list_item_1,  
  36.                 android.R.id.text1, dataSourceList);  
  37.           
  38.         swipeDismissListView.setAdapter(adapter);  
  39.           
  40. new
  41.               
  42. @Override
  43. public void onDismiss(int
  44.                  adapter.remove(adapter.getItem(dismissPosition));   
  45.             }  
  46.         });  
  47.           
  48.           
  49. new
  50.   
  51. @Override
  52. public void
  53. int position, long
  54. this, adapter.getItem(position), Toast.LENGTH_SHORT).show();  
  55.             }  
  56.         });  
  57.   
  58.     }  
  59.   
  60. }  

所有的代码都已经编写完毕了,接下来就是运行工程,看看具体的效果是不是我们想要的

listview滑动删除_Math

好了,今天的讲解到这里结束了,有了NineOldAndroids我们可以在2.x的手机上面实现许多复杂的动画效果,文章也介绍了关于开源库NineOldAndroids使用的一些知识,文章有点长,希望读者还是先将文章看下,然后自己看看能不能自己实现出来,有什么不明白的地方请在下面留言,我会为大家解答的!

​​项目源码,点击下载​​



ps: 下载源码的时候运行出错,是因为我加入了NineOldAndroids的Jar包,然后又加入了NineOldAndroids工程库,主要是我写DEMO的时候为了方便看源码就导入了NineOldAndroids工程库,大家删除一个JAR包或者NineOldAndroids工程库 就能解决了

标签:动画,listview,删除,item,Item,滑动,ListView,我们
From: https://blog.51cto.com/u_13657808/5946617

相关文章

  • Windows系统CMD命令行添加或删除路由
    1,按Win键输入“CMD”,右键“以管理员身份运行”  2,在CMD窗口输入“ipconfig”并按Enter键  3,找到自己的网卡对应的“默认网关”,执行如下命令添加路由: routead......
  • mysql binlog日志自动清理及手动删除
    说明当开启mysql数据库主从时,会产生大量如mysql-bin.00000*log的文件,这会大量耗费您的硬盘空间。如:mysql-bin.000001mysql-bin.000002mysql-bin.000003mysql-bin.0......
  • 力扣---740. 删除并获得点数
    给你一个整数数组 nums ,你可以对它进行一些操作。每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除所有等于 nums[i]-1和nums[i......
  • git添加/删除远程仓库
    1.远程仓库路径查询2.添加远程仓库3.删除指定的远程:4.本地初始化一个项目参考:注意:仓库只有管理员建的你才有权限上传,不然自己建的也没用,没权限上传1.远程仓库路径......
  • ListView 加载图片实现三级缓存效果
    importjava.io.FileOutputStream;importjava.io.InputStream;importjava.net.HttpURLConnection;importjava.net.URL;importjava.util.HashMap;importjava.util.Map;i......
  • LeetCode80. 删除排序数组中的重复项 II
    给定一个排序数组,你需要在​​原地​​删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在​​原地​​修改输入数组并在......
  • Git命令行删除远程分支
    首先查看当前所有的分支gitbranch-a红色的​​remote/origin/main​​​和​​remote/origin/main1​​为远程分支我们删除​​remote/origin/main1​​通过命令gitpu......
  • 删除链表中重复的节点
    在一个排序的链表中,存在重复的节点,请删除该链表中重复的节点,重复的节点不保留。/***Definitionforsingly-linkedlist.*structListNode{*intval;*......
  • 在O(1)时间删除链表结点
    给定单向链表的一个节点指针,定义一个函数在O(1)时间删除该结点。假设链表一定存在,并且该节点一定不是尾节点。/***Definitionforsingly-linkedlist.*structLi......
  • mac系统彻底删除vscode
    1.退出vscode应用。2.输入如下指令,删除vscode的设置和配置: sudorm-rf$HOME/Library/Application\Support/Code3.输入如下指令,删除vscode的插件: sudorm......