生活随笔
收集整理的這篇文章主要介紹了
Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17539199 )
我在上一篇文章中Android 帶你從源碼的角度解析Scroller的滾動(dòng)實(shí)現(xiàn)原理從源碼的角度介紹了Scroller的滾動(dòng)實(shí)現(xiàn)原理,相信大家對Scroller的使用有一定的了解,這篇文章就給大家?guī)硎褂肧croller的小例子,來幫助大家更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我們就能實(shí)現(xiàn)很多滑動(dòng)的效果。例如側(cè)滑菜單,launcher,ListView的下拉刷新等等效果,我今天實(shí)現(xiàn)的是ListView的item的左右滑動(dòng)刪除item的效果,現(xiàn)在很多朋友看到這個(gè)效果應(yīng)該是在Android的通知欄下拉中看到這個(gè)滑動(dòng)刪除的效果吧,我看到這個(gè)效果是在我之前的三星手機(jī)上左右滑動(dòng)打電話發(fā)短信的效果,感覺很棒,不過現(xiàn)在很多手機(jī)聯(lián)系人滑動(dòng)都不是我之前那臺手機(jī)的效果啦,網(wǎng)上很多朋友也寫了關(guān)于滑動(dòng)刪除ListView的item的例子,有些是滑動(dòng)手指離開之后然后給item加向左或者向右的移動(dòng)動(dòng)畫,我覺得這樣子的用戶體驗(yàn)不是很好,所以今天自己也寫了一個(gè)關(guān)于ListView左右滑動(dòng)刪除Item的小例子,ListView的item會隨著手指在屏幕上的滑動(dòng)而滑動(dòng),手指離開屏幕的時(shí)候item會根據(jù)判斷向左或者向右劃出屏幕,就是跟通知欄的效果差不多,接下來就帶大家來實(shí)現(xiàn)這個(gè)效果。
先說下實(shí)現(xiàn)該效果的主要思路
先根據(jù)手指觸摸的點(diǎn)來獲取點(diǎn)擊的是ListView的哪一個(gè)item 手指在屏幕中滑動(dòng)我們利用scrollBy()來使該item跟隨手指一起滑動(dòng) 手指放開的時(shí)候,我們判斷手指拖動(dòng)的距離來判斷item到底是滑出屏幕還是回到開始位置
主要思路就是上面這三步,接下來我們就用代碼來實(shí)現(xiàn)吧,首先我們新建一個(gè)項(xiàng)目,叫SlideCutListView
根據(jù)需求我們需要自己自定義一個(gè)ListView來實(shí)現(xiàn)該功能,接下來先貼出代碼再講解具體的實(shí)現(xiàn)
[java] ?view plaincopy
package ?com.example.slidecutlistview;?? ?? 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;?? ?? ? ? ? ? ? ?? public ? class ?SlideCutListView? extends ?ListView?{?? ????? ? ?? ????private ? int ?slidePosition;?? ????? ? ?? ????private ? int ?downY;?? ????? ? ?? ????private ? int ?downX;?? ????? ? ?? ????private ? int ?screenWidth;?? ????? ? ?? ????private ?View?itemView;?? ????? ? ?? ????private ?Scroller?scroller;?? ????private ? static ? final ? int ?SNAP_VELOCITY?=? 600 ;?? ????? ? ?? ????private ?VelocityTracker?velocityTracker;?? ????? ? ?? ????private ? boolean ?isSlide?=? false ;?? ????? ? ?? ????private ? int ?mTouchSlop;?? ????? ? ?? ????private ?RemoveListener?mRemoveListener;?? ????? ? ?? ????private ?RemoveDirection?removeDirection;?? ?? ?????? ????public ? enum ?RemoveDirection?{?? ????????RIGHT,?LEFT;?? ????}?? ?? ?? ????public ?SlideCutListView(Context?context)?{?? ????????this (context,? null );?? ????}?? ?? ????public ?SlideCutListView(Context?context,?AttributeSet?attrs)?{?? ????????this (context,?attrs,? 0 );?? ????}?? ?? ????public ?SlideCutListView(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();?? ????}?? ?????? ????? ? ? ?? ????public ? void ?setRemoveListener(RemoveListener?removeListener)?{?? ????????this .mRemoveListener?=?removeListener;?? ????}?? ?? ????? ? ?? ????@Override ?? ????public ? boolean ?dispatchTouchEvent(MotionEvent?event)?{?? ????????switch ?(event.getAction())?{?? ????????case ?MotionEvent.ACTION_DOWN:?{?? ????????????addVelocityTracker(event);?? ?? ?????????????? ????????????if ?(!scroller.isFinished())?{?? ????????????????return ? super .dispatchTouchEvent(event);?? ????????????}?? ????????????downX?=?(int )?event.getX();?? ????????????downY?=?(int )?event.getY();?? ?? ????????????slidePosition?=?pointToPosition(downX,?downY);?? ?? ?????????????? ????????????if ?(slidePosition?==?AdapterView.INVALID_POSITION)?{?? ????????????????return ? super .dispatchTouchEvent(event);?? ????????????}?? ?? ?????????????? ????????????itemView?=?getChildAt(slidePosition?-?getFirstVisiblePosition());?? ????????????break ;?? ????????}?? ????????case ?MotionEvent.ACTION_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);?? ????}?? ?? ????? ? ?? ????private ? void ?scrollRight()?{?? ????????removeDirection?=?RemoveDirection.RIGHT;?? ????????final ? int ?delta?=?(screenWidth?+?itemView.getScrollX());?? ?????????? ????????scroller.startScroll(itemView.getScrollX(),?0 ,?-delta,? 0 ,?? ????????????????Math.abs(delta));?? ????????postInvalidate();??? ????}?? ?? ????? ? ?? ????private ? void ?scrollLeft()?{?? ????????removeDirection?=?RemoveDirection.LEFT;?? ????????final ? int ?delta?=?(screenWidth?-?itemView.getScrollX());?? ?????????? ????????scroller.startScroll(itemView.getScrollX(),?0 ,?delta,? 0 ,?? ????????????????Math.abs(delta));?? ????????postInvalidate();??? ????}?? ?? ????? ? ?? ????private ? void ?scrollByDistanceX()?{?? ?????????? ????????if ?(itemView.getScrollX()?>=?screenWidth?/? 2 )?{?? ????????????scrollLeft();?? ????????}?else ? if ?(itemView.getScrollX()?<=?-screenWidth?/? 2 )?{?? ????????????scrollRight();?? ????????}?else ?{?? ?????????????? ????????????itemView.scrollTo(0 ,? 0 );?? ????????}?? ?? ????}?? ?? ????? ? ?? ????@Override ?? ????public ? boolean ?onTouchEvent(MotionEvent?ev)?{?? ????????if ?(isSlide?&&?slidePosition?!=?AdapterView.INVALID_POSITION)?{?? ????????????requestDisallowInterceptTouchEvent(true );?? ????????????addVelocityTracker(ev);?? ????????????final ? int ?action?=?ev.getAction();?? ????????????int ?x?=?( int )?ev.getX();?? ????????????switch ?(action)?{?? ????????????case ?MotionEvent.ACTION_DOWN:?? ????????????????break ;?? ????????????case ?MotionEvent.ACTION_MOVE:?? ?????????????????? ????????????????MotionEvent?cancelEvent?=?MotionEvent.obtain(ev);?? ????????????????cancelEvent.setAction(MotionEvent.ACTION_CANCEL?|?? ???????????????????????????(ev.getActionIndex()<<?MotionEvent.ACTION_POINTER_INDEX_SHIFT));?? ????????????????onTouchEvent(cancelEvent);?? ?????????????????? ????????????????int ?deltaX?=?downX?-?x;?? ????????????????downX?=?x;?? ?? ?????????????????? ????????????????itemView.scrollBy(deltaX,?0 );?? ?????????????????? ????????????????return ? true ;?? ?? ????????????case ?MotionEvent.ACTION_UP:?? ????????????????int ?velocityX?=?getScrollVelocity();?? ????????????????if ?(velocityX?>?SNAP_VELOCITY)?{?? ????????????????????scrollRight();?? ????????????????}?else ? if ?(velocityX?<?-SNAP_VELOCITY)?{?? ????????????????????scrollLeft();?? ????????????????}?else ?{?? ????????????????????scrollByDistanceX();?? ????????????????}?? ?????????????????? ????????????????recycleVelocityTracker();?? ?????????????????? ????????????????isSlide?=?false ;?? ????????????????break ;?? ????????????}?? ????????}?? ?? ?????????? ????????return ? super .onTouchEvent(ev);?? ????}?? ?? ????@Override ?? ????public ? void ?computeScroll()?{?? ?????????? ????????if ?(scroller.computeScrollOffset())?{?? ?????????????? ????????????itemView.scrollTo(scroller.getCurrX(),?scroller.getCurrY());?? ?????????????? ????????????postInvalidate();?? ?? ?????????????? ????????????if ?(scroller.isFinished())?{?? ????????????????if ?(mRemoveListener?==? null )?{?? ????????????????????throw ? new ?NullPointerException( "RemoveListener?is?null,?we?should?called?setRemoveListener()" );?? ????????????????}?? ?????????????????? ????????????????itemView.scrollTo(0 ,? 0 );?? ????????????????mRemoveListener.removeItem(removeDirection,?slidePosition);?? ????????????}?? ????????}?? ????}?? ?? ????? ? ? ? ?? ????private ? void ?addVelocityTracker(MotionEvent?event)?{?? ????????if ?(velocityTracker?==? null )?{?? ????????????velocityTracker?=?VelocityTracker.obtain();?? ????????}?? ?? ????????velocityTracker.addMovement(event);?? ????}?? ?? ????? ? ?? ????private ? void ?recycleVelocityTracker()?{?? ????????if ?(velocityTracker?!=? null )?{?? ????????????velocityTracker.recycle();?? ????????????velocityTracker?=?null ;?? ????????}?? ????}?? ?? ????? ? ? ? ?? ????private ? int ?getScrollVelocity()?{?? ????????velocityTracker.computeCurrentVelocity(1000 );?? ????????int ?velocity?=?( int )?velocityTracker.getXVelocity();?? ????????return ?velocity;?? ????}?? ?? ????? ? ? ? ? ? ? ?? ????public ? interface ?RemoveListener?{?? ????????public ? void ?removeItem(RemoveDirection?direction,? int ?position);?? ????}?? ?? }??
首先我們重寫dispatchTouchEvent()方法,該方法是事件的分發(fā)方法,我們在里面只做了一些簡單的步驟,我們按下屏幕的時(shí)候,如果某個(gè)item正在進(jìn)行滾動(dòng),我們直接交給SlideCutListView的父類處理分發(fā)事件,否則根據(jù)點(diǎn)擊的X,Y坐標(biāo)利用pointToPosition(int x, int y)來獲取點(diǎn)擊的是ListView的哪一個(gè)position,從而獲取到我們需要滑動(dòng)的item的View,我們還在該方法加入了滑動(dòng)速度的檢測,并且在ACTION_MOVE的時(shí)候來判斷是否響應(yīng)item的左右移動(dòng),用isSlide來記錄是否響應(yīng)左右滑動(dòng) 然后就是重寫onTouchEvent()方法,我們先判斷isSlide為true,并且我們點(diǎn)擊的是ListView上面的有效的position,否則直接交給SlideCutListView的父類也就是ListView來處理,在ACTION_MOVE中調(diào)用scrollBy()來移動(dòng)item,scrollBy()是相對item的上一個(gè)位置進(jìn)行移動(dòng)的,所以我們每次都要用現(xiàn)在移動(dòng)的距離減去上一個(gè)位置的距離然后賦給scrollBy()方法,這樣子我們就實(shí)現(xiàn)了item的平滑移動(dòng),當(dāng)我們將手指抬起的時(shí)候,我們先根據(jù)手指滑動(dòng)的速度來確定是item是滑出屏幕還是滑動(dòng)至原始位置,如果向右的速度大于我們設(shè)置的SNAP_VELOCITY,item就調(diào)用scrollRight()方法滾出屏幕,如果向左的速度小于-SNAP_VELOCITY,則調(diào)用scrollLeft()向左滾出屏幕,如果我們是緩慢的移動(dòng)item,則調(diào)用scrollByDistanceX()方法來判斷是滾到那個(gè)位置
在scrollRight()和scrollLeft()方法中我們使用Scroller類的startScroll()方法先設(shè)置滾動(dòng)的參數(shù),然后調(diào)用postInvalidate()來刷新界面,界面刷新就會調(diào)用computeScroll()方法,我們在里面處理滾動(dòng)邏輯就行了,值得一提的是computeScroll()里面的這段代碼
[java] ?view plaincopy
itemView.scrollTo( 0 ,? 0 );??
我們需要將該item滾動(dòng)在(0, 0 )這個(gè)點(diǎn),因?yàn)槲覀冎皇菍istView的Item滾動(dòng)出屏幕而已,并沒有將該item移除,而且我們不能手動(dòng)調(diào)用removeView()來從ListView中移除該item,我們只能通過改變ListView的數(shù)據(jù),然后通過notifyDataSetChanged()來刷新ListView,所以我們需要將其滾動(dòng)至(0, 0),這里比較關(guān)鍵。
定義好了我們左右滑動(dòng)的ListView,接下來就來使用它,布局很簡單,一個(gè)RelativeLayout包裹我們自定義的ListView
[html] ?view plaincopy
< RelativeLayout ? 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: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
<? xml ? version = "1.0" ? encoding = "UTF-8" ?> ?? < LinearLayout ? xmlns:android = "http://schemas.android.com/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 > ??
還記得我在上一篇文章中提到過調(diào)用scrollTo()方法是對里面的子View進(jìn)行滾動(dòng)的,而不是對整個(gè)布局進(jìn)行滾動(dòng)的,所以我們用LinearLayout來套住我們的item的布局,這點(diǎn)需要注意一下,不然滾動(dòng)的只是TextView。
主頁面MainActivity里面的代碼比較簡單,里面使用的也是ArrayAdapter,相信大家都能看懂
[html] ?view plaincopy
package?com.example.slidecutlistview;?? ?? import?java.util.ArrayList;?? import?java.util.List;?? ?? import?android.app.Activity;?? import?android.os.Bundle;?? import?android.view.View;?? import?android.widget.AdapterView;?? import?android.widget.AdapterView.OnItemClickListener;?? import?android.widget.ArrayAdapter;?? import?android.widget.Toast;?? ?? import?com.example.slidecutlistview.SlideCutListView.RemoveDirection;?? import?com.example.slidecutlistview.SlideCutListView.RemoveListener;?? ?? public?class?MainActivity?extends?Activity?implements?RemoveListener{?? ????private?SlideCutListView?slideCutListView?;?? ????private?ArrayAdapter< String > ?adapter;?? ????private?List< String > ? dataSourceList ?=? new ?ArrayList < String > ();?? ?? ????@Override?? ????protected?void?onCreate(Bundle?savedInstanceState)?{?? ????????super.onCreate(savedInstanceState);?? ????????setContentView(R.layout.activity_main);?? ????????init();?? ????}?? ?? ????private?void?init()?{?? ????????slideCutListView ?=?(SlideCutListView)?findViewById(R.id.slideCutListView);?? ????????slideCutListView.setRemoveListener(this);?? ?????????? ????????for(int?i = 0 ;?i < 20 ;?i++){?? ????????????dataSourceList.add("滑動(dòng)刪除"?+?i);??? ????????}?? ?????????? ????????adapter ?=? new ?ArrayAdapter < String > (this,?R.layout.listview_item,?R.id.list_item,?dataSourceList);?? ????????slideCutListView.setAdapter(adapter);?? ?????????? ????????slideCutListView.setOnItemClickListener(new?OnItemClickListener()?{?? ?? ????????????@Override?? ????????????public?void?onItemClick(AdapterView<? > ?parent,?View?view,?? ????????????????????int?position,?long?id)?{?? ????????????????Toast.makeText(MainActivity.this,?dataSourceList.get(position),?Toast.LENGTH_SHORT).show();?? ????????????}?? ????????});?? ????}?? ?? ?????? ????//滑動(dòng)刪除之后的回調(diào)方法?? ????@Override?? ????public?void?removeItem(RemoveDirection?direction,?int?position)?{?? ????????adapter.remove(adapter.getItem(position));?? ????????switch?(direction)?{?? ????????case?RIGHT:?? ????????????Toast.makeText(this,?"向右刪除??"+?position,?Toast.LENGTH_SHORT).show();?? ????????????break;?? ????????case?LEFT:?? ????????????Toast.makeText(this,?"向左刪除??"+?position,?Toast.LENGTH_SHORT).show();?? ????????????break;?? ?? ????????default:?? ????????????break;?? ????????}?? ?????????? ????}????? ?? ?? }??
這里面需要對SlideCutListView設(shè)置RemoveListener,然后我們在回調(diào)方法removeItem(RemoveDirection direction, int position)中刪除該position的數(shù)據(jù),在調(diào)用notifyDataSetChanged()刷新ListView,我這里用的是ArrayAdatper,直接調(diào)用remove()就可以了。
所有的代碼就編寫完了,我們來運(yùn)行下程序看看效果吧
好了,今天的講解就到此結(jié)束了,有疑問的朋友可以在下面留言,我會幫大家解答的。今天是2013年的最后一天,希望大家開開心心度過2013,也開開心心的迎接2014,提前祝大家元旦快樂!
項(xiàng)目源碼,點(diǎn)擊下載
總結(jié)
以上是生活随笔 為你收集整理的Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。