Android ListView 側滑效果實現(滑動展開、滑動刪除)

轉載請註明出處:http://blog.csdn.net/lonelyroamer/article/details/42439875
java


項目須要ListView滑動刪除的效果,首先確定是拿來主義,在網上搜了一遍,發現這樣的東西真很多,比較有名的Github上的SwipeListView。可是我的嘗試了一下,發現它的bug很多,而且達不到我想要的效果。因而又嘗試了一下其餘的例子,發現基本效果都有,可是都有很多問題。要麼事件衝突,要麼OnItemListView或者某個Button響應不了。沒辦法,只能本身着手寫一個。android

最開始,寫了一個最簡單的,只支持右邊的側滑,而且只支持一種滑動的模式,先在項目裏面用上。而後想將這個項目完善起來,斷斷續續的寫了一段時間。期間Android5.0發佈了,新出的控件RecyclerView 將要取代ListView的地位。頓時以爲本身作了無用功,而後就中斷了。git

可是後面發現RecyclerView畢竟是新出的控件,不少基於ListView的第三方庫,RecyclerView並無(用的人少,天然沒人去寫)。因而又把這個撿起來從新寫,說不定之後會用獲得。github


先看一下運行的demo效果,maven



實現的具體細節就不描述了,後面會上傳完整的代碼。說一下使用的問題。ide

一、在你的layout文件中添加改控件:佈局

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片性能

  1. <com.roamer.slidelistview.SlideListView  動畫

  2.     xmlns:slide="http://schemas.android.com/apk/res-auto"  ui

  3.     android:id="@+id/list_view"  

  4.     android:layout_width="match_parent"  

  5.     android:layout_height="match_parent"  

  6.     slide:slideAnimationTime="200"  

  7.     slide:slideLeftAction="scroll"  

  8.     slide:slideMode="both"  

  9.     slide:slideRightAction="scroll" />  


二、繼承SlideBaseAdapter,重寫裏面的getFrontViewId(),getLeftBackViewId(),getRightBackViewId()方法。

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. @Override  

  2. public int getFrontViewId(int position) {  

  3.     return R.layout.row_front_view;  

  4. }  

  5.   

  6. @Override  

  7. public int getLeftBackViewId(int position) {  

  8.     return R.layout.row_left_back_view;  

  9. }  

  10.   

  11. @Override  

  12. public int getRightBackViewId(int position) {  

  13.     return R.layout.row_right_back_view;  

  14. }  

一個Item可不能夠側滑,取決於三點,優先級從上到下。

(1)、你是否爲該Item提供了LeftBackView或者RightBackView

(2)、在SlideBaseAdapter中有一個方法,能夠改變某個位置的SlideMode,你能夠單獨改變某個position的SlideMode

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. /** 

  2.  * At first,your whole item slide mode is base on the SlideListView's 

  3.  * SlideMode.<br/> 

  4.  * but your can change the slide mode at one or more position in this 

  5.  * adapter by override this method 

  6.  *  

  7.  * @param position 

  8.  * @return 

  9.  */  

  10. public SlideMode getSlideModeInPosition(int position) {  

  11.     return mSlideMode;  

  12. }  


(3)、你的SlideListView設置的SlideMode的值


三、SlideAction表示側滑展開的方式,目前有兩種SCROLL和REVEAL。效果分別以下:

SCROLL:


REVEAL:


四、若是你的front view 和back view組合起來有多種視圖,那麼你還必須重寫getItemViewType()和getViewTypeCount()方法,不然視圖重用的時候會混亂

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. @Override  

  2. public int getFrontViewId(int position) {  

  3.     return R.layout.row_front_view;  

  4. }  

  5.   

  6. @Override  

  7. public int getLeftBackViewId(int position) {  

  8.     if (position % 2 == 0) {  

  9.         return R.layout.row_left_back_view;  

  10.     }  

  11.     return R.layout.row_right_back_view;  

  12. }  

  13.   

  14. @Override  

  15. public int getRightBackViewId(int position) {  

  16.     return R.layout.row_right_back_view;  

  17. }  

  18.   

  19. @Override  

  20. public int getItemViewType(int position) {  

  21.     if (position % 2 == 0) {  

  22.         return 0;  

  23.     }  

  24.     return 1;  

  25. }  

  26.   

  27. @Override  

  28. public int getViewTypeCount() {  

  29.     return 2;  

  30. }  


若是該代碼對你有幫助,不勝榮幸。若是有BUG,歡迎指正。

有人說你上傳個資源還要分,好吧,此次不要分。下載戳這裏




BUG FIX

一、OnScrollListener不正確回調致使的問題(2015-01-06 10點)

Android3.0如下,ListView的OnScrollListener回調的狀態會不正確,參考http://blog.csdn.net/ygc87/article/details/8078798.

這樣就會致使當滑動到頂部或者底部,而後在繼續滑動的時候,isInScrolling得不到正確的狀態值。下一次滑動的時候會致使Item不能滑動。

[cpp] view plaincopy

  1. private OnScrollListener mInnerOnScrollListener = new OnScrollListener() {  

  2.     @Override  

  3.     public void onScrollStateChanged(AbsListView view, int scrollState) {  

  4.         if (scrollState == SCROLL_STATE_IDLE) {  

  5.             isInScrolling = false;  

  6.         } else {  

  7.             isInScrolling = true;  

  8.         }  

  9.         if (mOnScrollListener != null) {  

  10.             mOnScrollListener.onScrollStateChanged(view, scrollState);  

  11.         }  

  12.     }  

  13.   

  14.     @Override  

  15.     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {  

  16.         if (mOnScrollListener != null) {  

  17.             mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);  

  18.         }  

  19.     }  

  20. };  


解決辦法:

在SlideListView中添加以下代碼:

[java] view plaincopy

  1. private static Field sTouch_Mode_Field;  

  2.     static {  

  3.         try {  

  4.             sTouch_Mode_Field = AbsListView.class  

  5.                     .getDeclaredField("mTouchMode");  

  6.             sTouch_Mode_Field.setAccessible(true);  

  7.         } catch (NoSuchFieldException e) {  

  8.             e.printStackTrace();  

  9.         }  

  10.     }  

[java] view plaincopy

  1. void checkScrolling() {  

  2.     if (!isInScrolling) {  

  3.         return;  

  4.     }  

  5.     if(sTouch_Mode_Field == null){  

  6.         return;  

  7.     }  

  8.     int touchMode = 0;  

  9.     try {  

  10.         touchMode = sTouch_Mode_Field.getInt(this);  

  11.     } catch (IllegalAccessException e1) {  

  12.         e1.printStackTrace();  

  13.     } catch (IllegalArgumentException e1) {  

  14.         e1.printStackTrace();  

  15.     }  

  16.     if (DEUBG) {  

  17.         Log.d(TAG, "mTouchMode:" + touchMode);  

  18.     }  

  19.     if (touchMode == -1) {// touchMode==TOUCH_MODE_REST  

  20.         isInScrolling = false;  

  21.     }  

  22. }  


在SlideTouchListener的onInterceptTouchEvent方法中添加下面標紅的代碼:

                // don't allow swiping if this is on the header or footer or
                // IGNORE_ITEM_VIEW_TYPE or enabled is false on the adapter
                boolean allowSlide = mSlideListView.getAdapter().isEnabled(position) && mSlideListView.getAdapter().getItemViewType(position) >= 0;
                if (allowSlide) {
                    if (Build.VERSION.SDK_INT <= 11) {// below 3.0
                        mSlideListView.checkScrolling();
                    }

                    mDownPosition = position;
                    mActivePointerId = event.getPointerId(0);
                    mDownMotionX = (int) event.getX();
                    initOrResetVelocityTracker();
                    mVelocityTracker.addMovement(event);
                }




二、SlideItem不正常關閉(Adapter數據集的改變,action的改變、adapter的改變),致使Front  view不能響應事件的問題(2015-01-09)

解決辦法:將SlideListView的setAdapter方法改爲以下:

[java] view plaincopy

  1. @Override  

  2. public void setAdapter(ListAdapter adapter) {  

  3.     mAdapter = null;  

  4.     if (adapter != null && adapter instanceof SlideBaseAdapter) {  

  5.         mAdapter = (SlideBaseAdapter) adapter;  

  6.         mAdapter.setSlideMode(mSlideMode);  

  7.         mAdapter.setSlideLeftAction(mSlideLeftAction);  

  8.         mAdapter.setSlideRightAction(mSlideRightAction);  

  9.     }  

  10.     super.setAdapter(adapter);  

  11.   

  12.     if (mTouchListener.isOpend()) {  

  13.         mTouchListener.closeOpenedItem();  

  14.     } else {  

  15.         mTouchListener.reset();  

  16.     }  

  17.   

  18.     if (adapter != null) {  

  19.         adapter.registerDataSetObserver(new DataSetObserver() {  

  20.             @Override  

  21.             public void onChanged() {  

  22.                 super.onChanged();  

  23.                 if (DEUBG) {  

  24.                     Log.e(TAG, "Adapter data has changed");  

  25.                 }  

  26.                 if (mTouchListener.isOpend()) {  

  27.                     mTouchListener.closeOpenedItem();  

  28.                 } else {  

  29.                     mTouchListener.reset();  

  30.                 }  

  31.             }  

  32.         });  

  33.     }  

  34. }  



三、Android2.3.3如下使用Nineoldandroids第三方庫中屬性動畫的bug,致使Item劃開後,backview不能點擊。

這個問題找了好久,才終於發現是問題。屬性動畫是Android3.0之後才添加的,因此纔有人寫了Nineoldandroids,用於3.0如下方便的使用屬性動畫。可是看了Nineoldandroids的代碼,發現2.3.3如下並非真正能達到屬性動畫的效果。運行了Github上面的SwipeListView(它也使用了Nineoldandroids庫),發如今2.3.3如下也有這個BUG。

這個BUG已經解決,2.3.3如下可使用了,可是效果不算太好。

最新的版本已上傳到Github,雖然沒什麼人用,可是也算是本身的一番心血


Github:https://github.com/LonelyRoamer/SlideListView




四、回覆 mavenly 

主動打開和關閉你能夠參考下,我最近很忙,沒時間修改,可能這樣作還有問題,之後我會在本身把這個功能加上。

(1)、主動打開

在SlideTouchListener加入下面的方法

[java] view plaincopy

  1. void openDirect(int position, boolean left) {  

  2.     mDownPosition = position;  

  3.     mSlideItem = new SlideItem(position);  

  4.   

  5.     SlideMode slideMode = mSlideListView.getSlideAdapter().getSlideModeInPosition(mSlideItem.position - mSlideListView.getHeaderViewsCount());  

  6.     if (left) {// 要打開左邊  

  7.         if (slideMode == SlideMode.LEFT || slideMode == SlideMode.BOTH) {// SlideMode support left  

  8.             mSlideItem.offset = 1;  

  9.         }  

  10.     } else {  

  11.         if (slideMode == SlideMode.RIGHT || slideMode == SlideMode.BOTH) {// SlideMode support right  

  12.             mSlideItem.offset = -1;  

  13.         }  

  14.     }  

  15.     move(mSlideItem.offset);  

  16.     autoScroll(mSlideItem.offset, true);  

  17. }  


在SlideListView中調用便可主動打開指定位置

[java] view plaincopy

  1. public void openDirect(int position, boolean left) {  

  2.     int opendPosition = mTouchListener.getOpendPosition();  

  3.     if (opendPosition == position) {  

  4.         return;  

  5.     }  

  6.     closeDirect();  

  7.     mTouchListener.openDirect(position, left);  

  8. }  

(2)、主動關閉

將SlideListView中的 closeDirect() 改成public便可


(3)、滑動有時候會卡 具體緣由我也不清楚。我應用在個人項目中,沒有發現。你看下Item的佈局複雜程序。還有可能和手機的系統、性能也有關。我在模擬器Android3.0以上,都發現挺好的。

相關文章
相關標籤/搜索