300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果

Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果

时间:2024-06-17 23:21:29

相关推荐

Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果

本文出自xiaanming的博客(/xiaanming/article/details/17539199)

我在上一篇文章中Android 带你从源码的角度解析Scroller的滚动实现原理从源码的角度介绍了Scroller的滚动实现原理,相信大家对Scroller的使用有一定的了解,这篇文章就给大家带来使用Scroller的小例子,来帮助大家更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我们就能实现很多滑动的效果。例如侧滑菜单,launcher,ListView的下拉刷新等等效果,我今天实现的是ListView的item的左右滑动删除item的效果,现在很多朋友看到这个效果应该是在Android的通知栏下拉中看到这个滑动删除的效果吧,我看到这个效果是在我之前的三星手机上左右滑动打电话发短信的效果,感觉很棒,不过现在很多手机联系人滑动都不是我之前那台手机的效果啦,网上很多朋友也写了关于滑动删除ListView的item的例子,有些是滑动手指离开之后然后给item加向左或者向右的移动动画,我觉得这样子的用户体验不是很好,所以今天自己也写了一个关于ListView左右滑动删除Item的小例子,ListView的item会随着手指在屏幕上的滑动而滑动,手指离开屏幕的时候item会根据判断向左或者向右划出屏幕,就是跟通知栏的效果差不多,接下来就带大家来实现这个效果。

先说下实现该效果的主要思路

先根据手指触摸的点来获取点击的是ListView的哪一个item手指在屏幕中滑动我们利用scrollBy()来使该item跟随手指一起滑动手指放开的时候,我们判断手指拖动的距离来判断item到底是滑出屏幕还是回到开始位置 主要思路就是上面这三步,接下来我们就用代码来实现吧,首先我们新建一个项目,叫SlideCutListView

根据需求我们需要自己自定义一个ListView来实现该功能,接下来先贴出代码再讲解具体的实现

[java]view plaincopypackagecom.example.slidecutlistview; importandroid.content.Context; importandroid.util.AttributeSet; importandroid.view.MotionEvent; importandroid.view.VelocityTracker; importandroid.view.View; importandroid.view.ViewConfiguration; importandroid.view.WindowManager; importandroid.widget.AdapterView; importandroid.widget.ListView; importandroid.widget.Scroller; /** *@blog/xiaanming * *@authorxiaanming * */ publicclassSlideCutListViewextendsListView{ /** *当前滑动的ListViewposition */ privateintslidePosition; /** *手指按下X的坐标 */ privateintdownY; /** *手指按下Y的坐标 */ privateintdownX; /** *屏幕宽度 */ privateintscreenWidth; /** *ListView的item */ privateViewitemView; /** *滑动类 */ privateScrollerscroller; privatestaticfinalintSNAP_VELOCITY=600; /** *速度追踪对象 */ privateVelocityTrackervelocityTracker; /** *是否响应滑动,默认为不响应 */ privatebooleanisSlide=false; /** *认为是用户滑动的最小距离 */ privateintmTouchSlop; /** *移除item后的回调接口 */ privateRemoveListenermRemoveListener; /** *用来指示item滑出屏幕的方向,向左或者向右,用一个枚举值来标记 */ privateRemoveDirectionremoveDirection; //滑动删除方向的枚举值 publicenumRemoveDirection{ RIGHT,LEFT; } publicSlideCutListView(Contextcontext){ this(context,null); } publicSlideCutListView(Contextcontext,AttributeSetattrs){ this(context,attrs,0); } publicSlideCutListView(Contextcontext,AttributeSetattrs,intdefStyle){ super(context,attrs,defStyle); screenWidth=((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth(); scroller=newScroller(context); mTouchSlop=ViewConfiguration.get(getContext()).getScaledTouchSlop(); } /** *设置滑动删除的回调接口 *@paramremoveListener */ publicvoidsetRemoveListener(RemoveListenerremoveListener){ this.mRemoveListener=removeListener; } /** *分发事件,主要做的是判断点击的是那个item,以及通过postDelayed来设置响应左右滑动事件 */ @Override publicbooleandispatchTouchEvent(MotionEventevent){ switch(event.getAction()){ caseMotionEvent.ACTION_DOWN:{ addVelocityTracker(event); //假如scroller滚动还没有结束,我们直接返回 if(!scroller.isFinished()){ returnsuper.dispatchTouchEvent(event); } downX=(int)event.getX(); downY=(int)event.getY(); slidePosition=pointToPosition(downX,downY); //无效的position,不做任何处理 if(slidePosition==AdapterView.INVALID_POSITION){ returnsuper.dispatchTouchEvent(event); } //获取我们点击的itemview itemView=getChildAt(slidePosition-getFirstVisiblePosition()); break; } caseMotionEvent.ACTION_MOVE:{ if(Math.abs(getScrollVelocity())>SNAP_VELOCITY ||(Math.abs(event.getX()-downX)>mTouchSlop&&Math .abs(event.getY()-downY)<mTouchSlop)){ isSlide=true; } break; } caseMotionEvent.ACTION_UP: recycleVelocityTracker(); break; } returnsuper.dispatchTouchEvent(event); } /** *往右滑动,getScrollX()返回的是左边缘的距离,就是以View左边缘为原点到开始滑动的距离,所以向右边滑动为负值 */ privatevoidscrollRight(){ removeDirection=RemoveDirection.RIGHT; finalintdelta=(screenWidth+itemView.getScrollX()); //调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item scroller.startScroll(itemView.getScrollX(),0,-delta,0, Math.abs(delta)); postInvalidate();//刷新itemView } /** *向左滑动,根据上面我们知道向左滑动为正值 */ privatevoidscrollLeft(){ removeDirection=RemoveDirection.LEFT; finalintdelta=(screenWidth-itemView.getScrollX()); //调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item scroller.startScroll(itemView.getScrollX(),0,delta,0, Math.abs(delta)); postInvalidate();//刷新itemView } /** *根据手指滚动itemView的距离来判断是滚动到开始位置还是向左或者向右滚动 */ privatevoidscrollByDistanceX(){ //如果向左滚动的距离大于屏幕的二分之一,就让其删除 if(itemView.getScrollX()>=screenWidth/2){ scrollLeft(); }elseif(itemView.getScrollX()<=-screenWidth/2){ scrollRight(); }else{ //滚回到原始位置,为了偷下懒这里是直接调用scrollTo滚动 itemView.scrollTo(0,0); } } /** *处理我们拖动ListViewitem的逻辑 */ @Override publicbooleanonTouchEvent(MotionEventev){ if(isSlide&&slidePosition!=AdapterView.INVALID_POSITION){ requestDisallowInterceptTouchEvent(true); addVelocityTracker(ev); finalintaction=ev.getAction(); intx=(int)ev.getX(); switch(action){ caseMotionEvent.ACTION_DOWN: break; caseMotionEvent.ACTION_MOVE: MotionEventcancelEvent=MotionEvent.obtain(ev); cancelEvent.setAction(MotionEvent.ACTION_CANCEL| (ev.getActionIndex()<<MotionEvent.ACTION_POINTER_INDEX_SHIFT)); onTouchEvent(cancelEvent); intdeltaX=downX-x; downX=x; //手指拖动itemView滚动,deltaX大于0向左滚动,小于0向右滚 itemView.scrollBy(deltaX,0); returntrue;//拖动的时候ListView不滚动 caseMotionEvent.ACTION_UP: intvelocityX=getScrollVelocity(); if(velocityX>SNAP_VELOCITY){ scrollRight(); }elseif(velocityX<-SNAP_VELOCITY){ scrollLeft(); }else{ scrollByDistanceX(); } recycleVelocityTracker(); //手指离开的时候就不响应左右滚动 isSlide=false; break; } } //否则直接交给ListView来处理onTouchEvent事件 returnsuper.onTouchEvent(ev); } @Override publicvoidcomputeScroll(){ //调用startScroll的时候puteScrollOffset()返回true, if(puteScrollOffset()){ //让ListViewitem根据当前的滚动偏移量进行滚动 itemView.scrollTo(scroller.getCurrX(),scroller.getCurrY()); postInvalidate(); //滚动动画结束的时候调用回调接口 if(scroller.isFinished()){ if(mRemoveListener==null){ thrownewNullPointerException("RemoveListenerisnull,weshouldcalledsetRemoveListener()"); } itemView.scrollTo(0,0); mRemoveListener.removeItem(removeDirection,slidePosition); } } } /** *添加用户的速度跟踪器 * *@paramevent */ privatevoidaddVelocityTracker(MotionEventevent){ if(velocityTracker==null){ velocityTracker=VelocityTracker.obtain(); } velocityTracker.addMovement(event); } /** *移除用户速度跟踪器 */ privatevoidrecycleVelocityTracker(){ if(velocityTracker!=null){ velocityTracker.recycle(); velocityTracker=null; } } /** *获取X方向的滑动速度,大于0向右滑动,反之向左 * *@return */ privateintgetScrollVelocity(){ puteCurrentVelocity(1000); intvelocity=(int)velocityTracker.getXVelocity(); returnvelocity; } /** * *当ListViewitem滑出屏幕,回调这个接口 *我们需要在回调方法removeItem()中移除该Item,然后刷新ListView * *@authorxiaanming * */ publicinterfaceRemoveListener{ publicvoidremoveItem(RemoveDirectiondirection,intposition); } } 首先我们重写dispatchTouchEvent()方法,该方法是事件的分发方法,我们在里面只做了一些简单的步骤,我们按下屏幕的时候,如果某个item正在进行滚动,我们直接交给SlideCutListView的父类处理分发事件,否则根据点击的X,Y坐标利用pointToPosition(int x, int y)来获取点击的是ListView的哪一个position,从而获取到我们需要滑动的item的View,我们还在该方法加入了滑动速度的检测,并且在ACTION_MOVE的时候来判断是否响应item的左右移动,用isSlide来记录是否响应左右滑动然后就是重写onTouchEvent()方法,我们先判断isSlide为true,并且我们点击的是ListView上面的有效的position,否则直接交给SlideCutListView的父类也就是ListView来处理,在ACTION_MOVE中调用scrollBy()来移动item,scrollBy()是相对item的上一个位置进行移动的,所以我们每次都要用现在移动的距离减去上一个位置的距离然后赋给scrollBy()方法,这样子我们就实现了item的平滑移动,当我们将手指抬起的时候,我们先根据手指滑动的速度来确定是item是滑出屏幕还是滑动至原始位置,如果向右的速度大于我们设置的SNAP_VELOCITY,item就调用scrollRight()方法滚出屏幕,如果向左的速度小于-SNAP_VELOCITY,则调用scrollLeft()向左滚出屏幕,如果我们是缓慢的移动item,则调用scrollByDistanceX()方法来判断是滚到那个位置 在scrollRight()和scrollLeft()方法中我们使用Scroller类的startScroll()方法先设置滚动的参数,然后调用postInvalidate()来刷新界面,界面刷新就会调用computeScroll()方法,我们在里面处理滚动逻辑就行了,值得一提的是computeScroll()里面的这段代码[java]view plaincopyitemView.scrollTo(0,0);

我们需要将该item滚动在(0, 0 )这个点,因为我们只是将ListView的Item滚动出屏幕而已,并没有将该item移除,而且我们不能手动调用removeView()来从ListView中移除该item,我们只能通过改变ListView的数据,然后通过notifyDataSetChanged()来刷新ListView,所以我们需要将其滚动至(0, 0),这里比较关键。

定义好了我们左右滑动的ListView,接下来就来使用它,布局很简单,一个RelativeLayout包裹我们自定义的ListView

[html]view plaincopy<RelativeLayoutxmlns:android="/apk/res/android" xmlns:tools="/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/darker_gray"> <com.example.slidecutlistview.SlideCutListView android:id="@+id/slideCutListView" android:layout_width="match_parent" android:layout_height="match_parent" android:listSelector="@android:color/transparent" android:divider="@drawable/reader_item_divider" android:cacheColorHint="@android:color/transparent"> </com.example.slidecutlistview.SlideCutListView> </RelativeLayout>

接下来我们来看看ListView的item的布局[html]view plaincopy<?xmlversion="1.0"encoding="UTF-8"?> <LinearLayoutxmlns:android="/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/friendactivity_comment_detail_list2"> <TextView android:id="@+id/list_item" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="15dip"/> </LinearLayout> </LinearLayout> 还记得我在上一篇文章中提到过调用scrollTo()方法是对里面的子View进行滚动的,而不是对整个布局进行滚动的,所以我们用LinearLayout来套住我们的item的布局,这点需要注意一下,不然滚动的只是TextView。

主页面MainActivity里面的代码比较简单,里面使用的也是ArrayAdapter,相信大家都能看懂

[html]view plaincopypackagecom.example.slidecutlistview; importjava.util.ArrayList; importjava.util.List; importandroid.app.Activity; importandroid.os.Bundle; importandroid.view.View; importandroid.widget.AdapterView; importandroid.widget.AdapterView.OnItemClickListener; importandroid.widget.ArrayAdapter; importandroid.widget.Toast; importcom.example.slidecutlistview.SlideCutListView.RemoveDirection; importcom.example.slidecutlistview.SlideCutListView.RemoveListener; publicclassMainActivityextendsActivityimplementsRemoveListener{ privateSlideCutListViewslideCutListView; privateArrayAdapter<String>adapter; privateList<String>dataSourceList=newArrayList<String>(); @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } privatevoidinit(){ slideCutListView=(SlideCutListView)findViewById(R.id.slideCutListView); slideCutListView.setRemoveListener(this); for(inti=0;i<20;i++){ dataSourceList.add("滑动删除"+i); } adapter=newArrayAdapter<String>(this,R.layout.listview_item,R.id.list_item,dataSourceList); slideCutListView.setAdapter(adapter); slideCutListView.setOnItemClickListener(newOnItemClickListener(){ @Override publicvoidonItemClick(AdapterView<?>parent,Viewview, intposition,longid){ Toast.makeText(MainActivity.this,dataSourceList.get(position),Toast.LENGTH_SHORT).show(); } }); } //滑动删除之后的回调方法 @Override publicvoidremoveItem(RemoveDirectiondirection,intposition){ adapter.remove(adapter.getItem(position)); switch(direction){ caseRIGHT: Toast.makeText(this,"向右删除"+position,Toast.LENGTH_SHORT).show(); break; caseLEFT: Toast.makeText(this,"向左删除"+position,Toast.LENGTH_SHORT).show(); break; default: break; } } }

这里面需要对SlideCutListView设置RemoveListener,然后我们在回调方法removeItem(RemoveDirection direction, int position)中删除该position的数据,在调用notifyDataSetChanged()刷新ListView,我这里用的是ArrayAdatper,直接调用remove()就可以了。

所有的代码就编写完了,我们来运行下程序看看效果吧

好了,今天的讲解就到此结束了,有疑问的朋友可以在下面留言,我会帮大家解答的。今天是的最后一天,希望大家开开心心度过,也开开心心的迎接,提前祝大家元旦快乐!

项目源码,点击下载

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