listview左右滑动

转载自http://blog.csdn.net/lonelyroamer/article/details/42439875

以前做成功过,最近再次研究也没成功,因为项目是eclipse的,现在用studio开发,留作以后有时间去研究

项目需要ListView滑动删除的效果,首先肯定是拿来主义,在网上搜了一遍,发现这样的东西真不少,比较有名的Github上的SwipeListView。但是个人尝试了一下,发现它的bug不少,并且达不到我想要的效果。于是又尝试了一下其他的例子,发现基本效果都有,但是都有不少问题。要么事件冲突,要么OnItemListView或者某个Button响应不了。没办法,只能自己着手写一个。

最开始,写了一个最简单的,只支持右边的侧滑,并且只支持一种滑动的模式,先在项目里面用上。然后想将这个项目完善起来,断断续续的写了一段时间。期间Android5.0发布了,新出的控件RecyclerView 将要取代ListView的地位。顿时觉得自己做了无用功,然后就中断了。

但是后面发现RecyclerView毕竟是新出的控件,很多基于ListView的第三方库,RecyclerView并没有(用的人少,自然没人去写)。于是又把这个捡起来重新写,说不定以后会用得到。


先看一下运行的demo效果,



实现的具体细节就不描述了,后面会上传完整的代码。说一下使用的问题。

1、在你的layout文件中添加改控件:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <com.roamer.slidelistview.SlideListView  
  2.     xmlns:slide="http://schemas.android.com/apk/res-auto"  
  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" />  

2、继承SlideBaseAdapter,重写里面的getFrontViewId(),getLeftBackViewId(),getRightBackViewId()方法。

[java]  view plain  copy
  在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 plain  copy
  在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的值


3、SlideAction表示侧滑展开的方式,目前有两种SCROLL和REVEAL。效果分别如下:

SCROLL:


REVEAL:


4、如果你的front view 和back view组合起来有多种视图,那么你还必须重写getItemViewType()和getViewTypeCount()方法,否则视图重用的时候会混乱

[java]  view plain  copy
  在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

1、OnScrollListener不正确回调导致的问题(2015-01-06 10点)

Android3.0以下,ListView的OnScrollListener回调的状态会不正确,参考http://blog.csdn.net/ygc87/article/details/8078798.

这样就会导致当滑动到顶部或者底部,然后在继续滑动的时候,isInScrolling得不到正确的状态值。下一次滑动的时候会导致Item不能滑动。

[cpp]  view plain  copy
  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 plain  copy
  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 plain  copy
  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);
                }




2、SlideItem不正常关闭(Adapter数据集的改变,action的改变、adapter的改变),导致Front  view不能响应事件的问题(2015-01-09)

解决办法:将SlideListView的setAdapter方法改成如下:

[java]  view plain  copy
  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. }  


3、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




4、回复 mavenly 

主动打开和关闭你可以参考下,我最近很忙,没时间修改,可能这样做还有问题,以后我会在自己把这个功能加上。

(1)、主动打开

在SlideTouchListener加入下面的方法

[java]  view plain  copy
  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 plain  copy
  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以上,都发现挺好的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值