ListView滑动删除实现之四——Scroller类与listview缓慢滑动

来源:互联网 发布:淘宝丝袜买家晒图 编辑:程序博客网 时间:2024/04/28 04:27

相关文章:

1、《 ListView滑动删除实现之一——merge标签与LayoutInflater.inflate()》

2、《ListView滑动删除实现之二——scrollTo、scrollBy详解》

3、《 ListView滑动删除实现之三——创建可滑动删除的ListView》

4、《ListView滑动删除实现之四——Scroller类与listview缓慢滑动》


上篇我们已经完成了利用scrollTo实现listview的滑动删除,但我们最后也提到了一个问题:当手指抬起时,scrollTo直接将ITEM滑动到了指定位置,中间没有缓冲过程,用户体验不好。我们想实现一个能缓慢滑动的listview:如下图所示:


从示图中明显可以看到,这里并不像上一章一样,一下子还原到初始化状态或者一下子到完全展开的状态了。而是有一个过程,它会慢慢点,一点点地还原或展开。这就是Scroller类所起的作用。下面我们先看看Scroller类的使用方法,然后再在前一篇的基础上加上Scroller。

一、Scroller类

首先,上篇我们也提到了,scrollTo()是没有办法添加运动的时间长度的,所以为了弥补这个问题,google就新增了一个类Scroller!一定要注意的是,Scroller类,并不能像scrollTo一样移动View的视角,而只能用来计算当前滑动的位置。看不懂没关系,继续往下看。

1、相关函数

(1)构造函数:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public Scroller (Context context)  
  2. public Scroller (Context context, Interpolator interpolator)  
如果我们用第一个构造函数,那么Scroller就会给我们传入一个默认的插值器;一般我们会选择第二个构造函数,传入一个我们想要的插值器。插值器的概念是从Animation来的,以前我曾把所有插值器的动画特效都列举了一遍,大家有兴趣可以参考:《Animation动画详解(二)——Interpolator插值器》 
Scroller构造完了以后,就可以调用startScroll()了,对于这个函数的作用先不表,但绝对不是像scrollTo一样移动视角的。 
(2)startScroll
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public void startScroll(int startX, int startY, int dx, int dy, int duration)  
  2. public void startScroll(int startX, int startY, int dx, int dy)  
我们先看第一个构造函数:
  • startX:开始移动的X坐标
  • startY开始移动的Y坐标
  • dx:沿X轴移动距离,可正可负,为正时,子控件向左移动;为负时,子控件向右移动
  • dy:沿Y轴移动距离,同样,为正时,子控件向上移动;为负时,子控件向下移动
  • duration:整个移动过程,所耗费时长 
第二个构造函数中,没有duration这个参数,系统会使用默认的时长:250毫秒

2、使用方法:

步骤一:初始化
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. private Scroller mScroller;  
  2. mScroller = new Scroller(context,new LinearInterpolator(context,null));  
步骤二:startScroll
在需要滚动的地方调用startScroll
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. mScroller.startScroll(0,0,200,0,500);  
  2. invalidate();  
步骤三: public void computeScroll()中处理
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. public void computeScroll() {  
  3.   
  4.     if (mScroller.computeScrollOffset()){  
  5.         itemRoot.scrollTo(mScroller.getCurrX(),mScroller.getCurrY());  
  6.     }  
  7.     invalidate();  
  8. }  
在这里注意一个地方,我们在computeScroll()中最终调用了itemRoot的scrollTo(),这里又涉及两个函数:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. mScroller.getCurrX()  
  2. mScroller.getCurrY()  
这两个是什么意思?看表面意思是获取当前scroller所在的X轴坐标和Y轴坐标。难道它的X轴坐标和Y轴坐标还都不一样吗?
原因是这样的:
我们前面说过,Scoller类是scrollTo的补充,他没有scrollTo的功能,它的功能就是根据传进去的参数计算位置的。我们再看一下的我们例子中的构造函数:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. mScroller.startScroll(0,0,200,0,500);  
这个代码就是说用500毫秒的时间从(0,0)的位置,沿X轴反方向移动200,Y轴不动;
但这个函数并不会移动,而是模拟计算,调用了这个函数之后,它就会在scroller内部用一个线程来计算从(0,0)的位置,沿X轴反方向移动200,Y轴不动;每一毫秒的位置,用户可以通过scroller.getCurrX()、scroller.getCurrY()来获取,当前应该在的位置。注意,我用的“应该”。因为scroller只是根据插值器,指定的时间,距离;算出当前所在的X轴坐标,Y轴坐标。但对图像并没有做任何操作!!!!!!要想移动图像,就必须使用scrollTo()!!!所以我们要每计算出一个新的位置就让View重绘一次。这就是为什么步骤二和步骤三都会调用invalidate()的原因。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //步骤二:  
  2.  mScroller.startScroll(0,0,200,0,500);  
  3.  invalidate();  
  4. ```  
  5. ``` java  
  6. @Override  
  7. public void computeScroll() {  
  8.     //步骤三:  
  9.     if (mScroller.computeScrollOffset()){  
  10.         itemRoot.scrollTo(mScroller.getCurrX(),mScroller.getCurrY());  
  11.         invalidate();  
  12.     }  
  13. }  
这里还要再说一个点:我们如何判断什么时候停止重绘呢。scroller给我们提供了一个函数:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Scroller.computeScrollOffset()  
当scroller还在移动时,就返回TRUE,如果scroller已经移动结束就返回FALSE;所以,我们可以在直接itemRoot.scrollTo(mScroller.getCurrX(),mScroller.getCurrY());前利用mScroller.computeScrollOffset()来判断当前scroller是不是已经结束了。
这里要插一句,computeScroll()函数,不是Scroller的函数,而是VIEW的函数,当调用invaidate或者postInvalidate重绘时就会调用computeScroll() 来重绘与scroller有关的View部分,而在View中的实现方式如下:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Called by a parent to request that a child update its values for mScrollX 
  3.  * and mScrollY if necessary. This will typically be done if the child is 
  4.  * animating a scroll using a {@link android.widget.Scroller Scroller} 
  5.  * object. 
  6.  */  
  7. public void computeScroll() {  
  8. }  
明显这里是一个空函数,所以,这就交由我们自己来实现,Scroller中的移动部分了,这也就是为什么我们会在computeScroll() 中调用 scrollTo来实现视图的移动了。

最后我们总结一下:Scroller在调用startScroll()之后,会自己根据移动距离和时间来计算每毫秒的移动目的坐标,用户可以通过scroller.getCurrX()和scroller.getCurrY()来获取。当VIEW在重绘时,会调用View的computeScroll()函数来处理与scroller有关的重绘操作。而由于View类并没有对computeScroll()做任何的实现(只是一个空函数),所以有关scroller的移动操作,就只能靠我们自己完成了。(重写computeScroll函数,调用)

二、实现缓慢滑动的ListView

这里我们就利用scroller类来实现文章开头时的效果,这篇文章依附上一篇的源码:《 ListView滑动删除实现之三——创建可滑动删除的ListView》 
由于我们要实现的缓慢滑动效果是在用户手指抬起后,缓慢移动到目标位置的,所以我们下面所有的代码都是在MyListView中来做的。

1、声明Scroller变量及初始化

代码如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class MyListView extends ListView {  
  2.       
  3.     private Context mContext;  
  4.     private Scroller mScroller;  
  5.   
  6.     public MyListView(Context context, AttributeSet attrs) {  
  7.         super(context, attrs);  
  8.         mContext = context;  
  9.         mScroller = new Scroller(context, new LinearInterpolator(context, null));  
  10.     }  
  11.     ……  
  12. }  
我们这里初始化mScroller时,使用线性插值器;大家也可以用其它插值器,每一个插值器的运动效果都是不一样的。

2、用户抬起时,调用startScroll()

在初始化完scroller以后,下面就是在用户抬起手指时,调用startScroll()来开始计算每毫秒所在的目的坐标。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public boolean onTouchEvent(MotionEvent event) {  
  2.     int maxLength = dipToPx(mContext, MAX_WIDTH);  
  3.     int x = (int) event.getX();  
  4.     int y = (int) event.getY();  
  5.     switch (event.getAction()) {  
  6.         …………  
  7.         case MotionEvent.ACTION_UP: {  
  8.             int scrollX = itemRoot.getScrollX();  
  9.             int newScrollX = scrollX + mlastX - x;  
  10.             if (scrollX > maxLength / 2) {  
  11.                 newScrollX = maxLength;  
  12.             } else {  
  13.                 newScrollX = 0;  
  14.             }  
  15.             mScroller.startScroll(scrollX,0,newScrollX - scrollX,0);  
  16.             invalidate();  
  17.         }  
  18.         break;  
  19.     }  
  20.   
  21.     mlastX = x;  
  22.     return super.onTouchEvent(event);  
  23. }  
我们把核心代码摘出来:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int scrollX = itemRoot.getScrollX();  
  2. int newScrollX = scrollX + mlastX - x;  
  3. if (scrollX > maxLength / 2) {  
  4.     newScrollX = maxLength;  
  5. else {  
  6.     newScrollX = 0;  
  7. }  
  8. mScroller.startScroll(scrollX,0,newScrollX - scrollX,0);  
  9. invalidate();  
首先跟上篇一样,根据当前手指的位置(如果超过黄色块1/2就将newScrollX设置到最大值;如果小于黄色块1/2,那就还原到初始化状态),然后是调用
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. mScroller.startScroll(scrollX,0,newScrollX - scrollX,0);  
首先是移动距离,大家一定要注意的是移动距离不一定是正数,它只是表示在当前方向上的移动数,X轴为正:视角向右移动,即其中的子控件向左移动。为负:视角向左移动,即其中的子控件所右移动;Y轴同理。所以这里的第三个参数dx:就等于目的坐标减去原坐标。这就是第三个参数为什么是newScrollX - scrollX的原因。 
最后调用invalidate()来重绘,由于我们这里有Scroller,所以在重绘时就会走到VIEW的computeScroll()函数中,上面我们也说了VIEW中的computeScroll()只是一个空函数,VIEW并没有对它进行任何实现。所以我们下面这样来实现它:

3、实现computeScroll()函数

先上代码:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public void computeScroll() {  
  2.   
  3.     if (mScroller.computeScrollOffset()){  
  4.         itemRoot.scrollTo(mScroller.getCurrX(),mScroller.getCurrY());  
  5.         invalidate();  
  6.     }  
  7. }  
可以看到,首先是利用mScroller.computeScrollOffset()来判断当前Scroller的状态——是否已经计算移动位置结束,如果结束返回FALSE,如果还在计算就返回TRUE。 
然后,如果还在计算,就说明还在移动过程中,那么就调用Scroller.getCurrX()和mScroller.getCurrY()来提取当前计算出来的应该移动的X轴坐标和Y轴坐标。然后调用itemRoot.scrollTo();来移动其中的子控件。这一次结束了,下一毫秒该怎么办呢?所以我们还要调用invalidate();函数,让它再次重绘,重新走进computeScroll() 函数,找到最新的Scroller位置并移动。最终当移动结束,mScroller.computeScrollOffset()就会返回FALSE,最终结束重绘。

三、优化

1、初步优化

用过这个控件的同学应该都知道,一般情况下的实现方式是,如果有一个ITEM被划出来,其它的ITEM都应该收缩回去。就如下面的这个效果: 

1、首先,当另一个ITEM滑出来的时候,上一个ITEM缩回去。
2、但最后大家会发现问题:当我连续点击一个ITEM之后,上一个ITEM就会停止滑动。这个效果不是我们想要的,我们先完成第一个效果,后面再解决这个问题。
由于我们要让上一个ITEM滑动,所以我们要在MyListView中另外定义一个变量来保存上次滑动的VIEW

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. private LinearLayout mPreScrollView;  
然后额外添加一个SCROLLER,用来计算mPreScrollView的滚动坐标:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. private Scroller mPreScroller;  
  2.   
  3. public MyListView(Context context, AttributeSet attrs) {  
  4.     super(context, attrs);  
  5.     mContext = context;  
  6.     mScroller = new Scroller(context, new LinearInterpolator(context, null));  
  7.     mPreScroller = new Scroller(context, new LinearInterpolator(context, null));  
  8. }  
然后是对mPreScrollView赋值和滚动了。我们在下按一个ITEM的时候,就要把前一个ITEM还原。当我们手指上抬的时候,那这个ITEM就已经滚动结束了,那么它就是下一次的mPreScrollView。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public boolean onTouchEvent(MotionEvent event) {  
  2.     switch (event.getAction()) {  
  3.         case MotionEvent.ACTION_DOWN: {  
  4.   
  5.             if (mPreScrollView != null){  
  6.                 int preScrollX = mPreScrollView.getScrollX();  
  7.                 mPreScroller.startScroll(preScrollX,0,0-preScrollX,0);  
  8.             }  
  9.         }  
  10.         break;  
  11.         case MotionEvent.ACTION_UP: {  
  12.             …………  
  13.             mPreScrollView = itemRoot;  
  14.             …………  
  15.             invalidate();  
  16.         }  
  17.         break;  
  18.     }  
  19.     mlastX = x;  
  20.     return super.onTouchEvent(event);  
  21. }  
最后是在computeScroll()中移动mPreScrollView
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public void computeScroll() {  
  2.     …………  
  3.     if (mPreScroller.computeScrollOffset()){  
  4.         mPreScrollView.scrollTo(mPreScroller.getCurrX(),mPreScroller.getCurrY());  
  5.     }  
  6.     invalidate();  
  7.   
  8. }  
到这里就结束了,mPreScrollView在下一次滑动ITEM时就会自己收缩了,但这里有个问题,也就是效果图中的第二点问题。当我们对一个ITEM点击两下的时候,它的上一个ITEM就会停止滑动。导致不能完全收缩回去。这是为什么呢?主要是因为我们在MotionEvent.ACTION_UP中对mPreScrollView = itemRoot;进行的赋值。如果我们抬起的太快,而上一个ITEM还没有滑动结束,那么mPreScrollView还没结束就已经被赋值给最新的ITEM了,这时候的computeScroll(),调用的mPreScrollView.scrollTo(mPreScroller.getCurrX(),mPreScroller.getCurrY());就已经是最新的ITEM了,所以上一个ITEM就会停止滑动。 
源码在文章底部给出 

2、最终优化

关于上面的问题,我们唯一的解决办法就是让每个ITEM负责自己的滑动,只需要上层给它一个通知,它自己负责自己的滑动,这样就不会冲突了。 
所以我们要对ITEM的根布局lin_root进行重写。新定义一个LinearLayout派生类MyLInearLayout。然后在其中做操作。 

(1)改造:派生MyLInearLayout类

我们单独把ITEM的根布局拿出来,在其中做所有有关的对ITEM的移动操作,代码如下:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class MyLinearLayout extends LinearLayout {  
  2.     private int mlastX = 0;  
  3.     private final int MAX_WIDTH = 200;  
  4.     private Context mContext;  
  5.     private Scroller mScroller;  
  6.     public MyLinearLayout(Context context, AttributeSet attrs) {  
  7.         super(context, attrs);  
  8.         mContext = context;  
  9.         mScroller = new Scroller(context, new LinearInterpolator(context, null));  
  10.     }  
  11.     public void disPatchTouchEvent(MotionEvent event){  
  12.         int maxLength = dipToPx(mContext, MAX_WIDTH);  
  13.   
  14.         int x = (int) event.getX();  
  15.         int y = (int) event.getY();  
  16.   
  17.         switch (event.getAction()) {  
  18.             case MotionEvent.ACTION_DOWN: {  
  19.             }  
  20.             break;  
  21.             case MotionEvent.ACTION_MOVE: {  
  22.                 int scrollX = this.getScrollX();  
  23.                 int newScrollX = scrollX + mlastX - x;  
  24.                 if (newScrollX < 0) {  
  25.                     newScrollX = 0;  
  26.                 } else if (newScrollX > maxLength) {  
  27.                     newScrollX = maxLength;  
  28.                 }  
  29.                 this.scrollTo(newScrollX, 0);  
  30.             }  
  31.             break;  
  32.             case MotionEvent.ACTION_UP: {  
  33.                 int scrollX = this.getScrollX();  
  34.                 int newScrollX = scrollX + mlastX - x;  
  35.                 if (scrollX > maxLength / 2) {  
  36.                     newScrollX = maxLength;  
  37.                 } else {  
  38.                     newScrollX = 0;  
  39.                 }  
  40.                 mScroller.startScroll(scrollX, 0, newScrollX - scrollX, 0);  
  41.                 invalidate();  
  42.             }  
  43.             break;  
  44.         }  
  45.         mlastX = x;  
  46.     }  
  47.   
  48.     @Override  
  49.     public void computeScroll() {  
  50.         if (mScroller.computeScrollOffset()) {  
  51.             this.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());  
  52.         }  
  53.         invalidate();  
  54.   
  55.     }  
  56.   
  57.     private int dipToPx(Context context, int dip) {  
  58.         return (int) (dip * context.getResources().getDisplayMetrics().density + 0.5f);  
  59.     }  
  60. }  
这里就是集成了所有对ITEM跟随手指移动的操作。

(2)、变更ITEM布局——使用MyLinearLayout

然后就是更改原来ITEM的布局,把根结点的LinearLayout改成MyLinearLayout:
[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <com.harvic.com.blog3_4_mylinearlayout.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:id="@+id/lin_root"  
  3.     android:orientation="horizontal"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:minHeight="120dp">  
  7.   
  8.     <TextView  
  9.         android:id="@+id/title"  
  10.         android:layout_width="fill_parent"  
  11.         android:layout_height="fill_parent"  
  12.         android:background="#0000ff"  
  13.         android:gravity="center"  
  14.         android:textSize="25dp" />  
  15.   
  16.     <TextView  
  17.         android:id="@+id/del"  
  18.         android:layout_width="200dp"  
  19.         android:layout_height="fill_parent"  
  20.         android:background="#ffff00"  
  21.         android:text="删除"  
  22.         android:textSize="25dp"  
  23.         android:textColor="#ffffff"  
  24.         android:gravity="center" />  
  25.   
  26. </com.harvic.com.blog3_4_mylinearlayout.MyLinearLayout>  

(3)、最后是在MyListView中分发MotionEvent事件:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class MyListView extends ListView {  
  2.   
  3.     private MyLinearLayout mCurView;  
  4.     public MyListView(Context context, AttributeSet attrs) {  
  5.         super(context, attrs);  
  6.     }  
  7.   
  8.     @Override  
  9.     public boolean onTouchEvent(MotionEvent event) {  
  10.         int x = (int) event.getX();  
  11.         int y = (int) event.getY();  
  12.   
  13.         switch (event.getAction()) {  
  14.             case MotionEvent.ACTION_DOWN: {  
  15.                 //我们想知道当前点击了哪一行  
  16.                 int position = pointToPosition(x, y);  
  17.                 if (position != INVALID_POSITION) {  
  18.                     DataHolder data = (DataHolder) getItemAtPosition(position);  
  19.                     mCurView = data.rootView;  
  20.                 }  
  21.             }  
  22.             break;  
  23.             default:  
  24.                 break;  
  25.         }  
  26.         if (mCurView != null){  
  27.             mCurView.disPatchTouchEvent(event);  
  28.         }  
  29.         return super.onTouchEvent(event);  
  30.     }  
  31. }  
首先根据位置找到所有ITEM的VIEW,然后把事件传给对应的MyLinearLayout自己处理。
到这里改造就完成了,然后就是实现自己滑动缩回去的步骤了。


下面的内容可能比较绕,大家可能加上我的讲解,大家可以还要自己理解一下了
首先,我们有几个点先列举一下:
1、我们需要在一个ITEM滑出来的时候,通知到上一个ITEM,让它收缩
2、每一个ITEM都应该具有监听功能,当上一个ITEM滑出来时,它就能对应的收缩回去

(4)、自动收缩之——监听

首先,我们要明白的一点是,我们要监听!!!监听当前ITEM是否全部拉了出来,所以我们在MyLinearLayout中需要加一个回调函数来做监听:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class MyLinearLayout extends LinearLayout {  
  2.     private OnScrollListener mScrollListener;  
  3.   
  4.     public static interface OnScrollListener {  
  5.         public void OnScroll(MyLinearLayout view);  
  6.     }  
  7.   
  8.     public void disPatchTouchEvent(MotionEvent event) {  
  9.   
  10.         switch (event.getAction()) {  
  11.             …………  
  12.             case MotionEvent.ACTION_UP: {  
  13.                 int scrollX = this.getScrollX();  
  14.                 int newScrollX = scrollX + mlastX - x;  
  15.                 if (scrollX > maxLength / 2) {  
  16.                     newScrollX = maxLength;  
  17.                     //当完全展开时,通知出去  
  18.                     mScrollListener.OnScroll(this);  
  19.                 } else {  
  20.                     newScrollX = 0;  
  21.                 }  
  22.                 mScroller.startScroll(scrollX, 0, newScrollX - scrollX, 0);  
  23.                 invalidate();  
  24.             }  
  25.             break;  
  26.         }  
  27.         mlastX = x;  
  28.     }  
  29.   
  30.     …………  
  31. }  
上面的代码,总共做了两件事:
1、创建回调函数
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. private OnScrollListener mScrollListener;  
  2. public static interface OnScrollListener{  
  3.     public void OnScroll(MyLinearLayout view);  
  4. }  
由于我们后面要保存当前的VIEW来做自动收缩,所以我们要把当前伸展出来的ITEM的VIEW传出去,供对方保存。当下一个ITEM出来的时候,让这个VIEW缩回去
2、使用回调函数

当用户手指抬起来,而且ITEM完全展开的时候,调用onScroll()通知出去

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. case MotionEvent.ACTION_UP: {  
  2.     int scrollX = this.getScrollX();  
  3.     int newScrollX = scrollX + mlastX - x;  
  4.     if (scrollX > maxLength / 2) {  
  5.         //当完全展开时,通知出去  
  6.         newScrollX = maxLength;  
  7.         mScrollListener.OnScroll(this);  
  8.     } else {  
  9.         newScrollX = 0;  
  10.     }  
  11.     mScroller.startScroll(scrollX, 0, newScrollX - scrollX, 0);  
  12.     invalidate();  
  13.   
  14. }  
  15. break;  
然后,还有两个额外的函数:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //设置监听器  
  2. public void setOnScrollListener(OnScrollListener scrollListener) {  
  3.     mScrollListener = scrollListener;  
  4. }  
  5. //缓慢将ITEM滚动到指定位置  
  6. public void smoothScrollTo(int destX, int destY) {  
  7.     int scrollX = getScrollX();  
  8.     int delta = destX - scrollX;  
  9.     mScroller.startScroll(scrollX, 0, delta, 0);  
  10.     invalidate();  
  11. }  

(5)、自动收缩之——为每一个ITEM设置监听

由于我们每一个ITEM都需要监听,所以把监听函数设在Adapter中最合适。所以在MergeListAdapter中为每一个Item的根结点MyLinearLayout添加监听函数:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public View getView(final int position, View convertView, ViewGroup parent) {  
  2.     …………  
  3.     DataHolder item = mDataList.get(position);  
  4.     holder.title.setText(item.title);  
  5.   
  6.     item.rootView = (MyLinearLayout) convertView.findViewById(R.id.lin_root);  
  7.     item.rootView.scrollTo(00);  
  8.     item.rootView.setOnScrollListener(mScrollListener);  
  9.     …………  
  10.     return convertView;  
  11. }  
而Adapter中是没有办法做监听的,所以它的监听函数也只有从MainActivity传过来:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class MergeListAdapter extends BaseAdapter {  
  2.     private View.OnClickListener mDelClickListener;  
  3.     private MyLinearLayout.OnScrollListener mScrollListener;  
  4.   
  5.     public MergeListAdapter(Context context, List<DataHolder> dataList, View.OnClickListener delClickListener, MyLinearLayout.OnScrollListener listener) {  
  6.         …………  
  7.         mDelClickListener = delClickListener;  
  8.         mScrollListener = listener;  
  9.     }  
  10.     …………  
  11. }  

(6)自动收缩之——MainActivity中处理监听

代码如下:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends Activity implements View.OnClickListener,MyLinearLayout.OnScrollListener{  
  2.     private MyLinearLayout mLastScrollView;  
  3.     protected void onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         setContentView(R.layout.activity_main);  
  6.         …………  
  7.         adapter = new MergeListAdapter(this,items,this,this);  
  8.         listView.setAdapter(adapter);  
  9.     }  
  10.     @Override  
  11.     public void OnScroll(MyLinearLayout view) {  
  12.         if (mLastScrollView != null){  
  13.             mLastScrollView.smoothScrollTo(0,0);  
  14.         }  
  15.         mLastScrollView = view;  
  16.     }  
  17. }  
首先初始化adapter,当一个ITEM完全展开时,我们的OnScroll()函数就会得到调用。用一个变量:MyLinearLayout mLastScrollView来保存上一次完全展开的ITEM;当一个ITEM完全展开时,就先调用mLastScrollView.smoothScrollTo(0,0);将上一个ITEM收缩回去,然后再将这个VIEW赋值给mLastScrollView;
最终的效果图如下:

源码在文章底部给出

四、存在问题:不能将listview的布局参数设为wrap_content

自定义listview的参数必须是layout_width:match_parent/fill_parent,layout_height:match_parent/fill_parent

即下面的代码:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     tools:context=".MainActivity">  
  6.   
  7.     <com.harvic.com.blog3_4_mylinearlayout.MyListView  
  8.         android:id="@+id/listview"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="match_parent"/>  
  11.   
  12. </RelativeLayout>  

如果给Mylistview使用wrap_content,将导致scrollTo无效,具体什么原因我也不知道,所以如果listView底部还有什么界面的话,建议大家使用listview::addFooterView()来添加底部布局的方式来做。一定要避免使用wrap_content,不然就真的会滑动无效,大家可以尝试一下,反正我是找不到原因了……


好了,到这里有关滑动删除的所有东东都讲完了,这篇文章容量有点大,大家可能要根据源码仔细理解一下了。逻辑确实有点太绕,大家多看看源码应该问题不大。


最后,最终优化部分的代码是我们这个系列建议使用的代码结构,即ITEM的根布局使用自定义的MyLinearLayout,由它自己来负责自己的滚动。对应的源码名称为:《ScrollViewUltimate》


源码内容:

1、《ScrollView_Scroller》:第二部分:《实现缓慢滑动的ListView》对应源码

2、《ScrollViewUltimate》:第三部分:《优化》的最终代码,也是本系统的最终可用代码


如果本文有帮到你,记得加关注哦

源码下载地址:http://download.csdn.net/detail/harvic880925/8642751

请大家尊重原创者版权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/45317951 谢谢

0 0