详解Android中实现ListView左右滑动删除条目的方法

使用Scroller实现绚丽的ListView左右滑动删除Item效果
这里来给大家带来使用Scroller的小例子,同时也能用来帮助初步解除的读者更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我们就能实现很多滑动的效果。例如侧滑菜单,launcher,ListView的下拉刷新等等效果,我今天实现的是ListView的item的左右滑动删除item的效果,现在很多朋友看到这个效果应该是在Android的通知栏下拉中看到这个滑动删除的效果吧,我看到这个效果是在我之前的三星手机上左右滑动打电话发短信的效果,感觉很棒,不过现在很多手机联系人滑动都不是我之前那台手机的效果啦,网上很多朋友也写了关于滑动删除ListView的item的例子,有些是滑动手指离开之后然后给item加向左或者向右的移动动画,我觉得这样子的用户体验不是很好,所以今天自己也写了一个关于ListView左右滑动删除Item的小例子,ListView的item会随着手指在屏幕上的滑动而滑动,手指离开屏幕的时候item会根据判断向左或者向右划出屏幕,就是跟通知栏的效果差不多,接下来就带大家来实现这个效果。
先说下实现该效果的主要思路
先根据手指触摸的点来获取点击的是ListView的哪一个item
手指在屏幕中滑动我们利用scrollBy()来使该item跟随手指一起滑动
手指放开的时候,我们判断手指拖动的距离来判断item到底是滑出屏幕还是回到开始位置
主要思路就是上面这三步,接下来我们就用代码来实现吧,首先我们新建一个项目,叫SlideCutListView
根据需求我们需要自己自定义一个ListView来实现该功能,接下来先贴出代码再讲解具体的实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
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 {
   /**
    * 当前滑动的ListView position
    */
   private int slidePosition;
   /**
    * 手指按下X的坐标
    */
   private int downY;
   /**
    * 手指按下Y的坐标
    */
   private int downX;
   /**
    * 屏幕宽度
    */
   private int screenWidth;
   /**
    * ListView的item
    */
   private View itemView;
   /**
    * 滑动类
    */
   private Scroller scroller;
   private static final int SNAP_VELOCITY = 600 ;
   /**
    * 速度追踪对象
    */
   private VelocityTracker velocityTracker;
   /**
    * 是否响应滑动,默认为不响应
    */
   private boolean isSlide = false ;
   /**
    * 认为是用户滑动的最小距离
    */
   private int mTouchSlop;
   /**
    * 移除item后的回调接口
    */
   private RemoveListener mRemoveListener;
   /**
    * 用来指示item滑出屏幕的方向,向左或者向右,用一个枚举值来标记
    */
   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();
   }
    
   /**
    * 设置滑动删除的回调接口
    * @param removeListener
    */
   public void setRemoveListener(RemoveListener removeListener) {
     this .mRemoveListener = removeListener;
   }
  
   /**
    * 分发事件,主要做的是判断点击的是那个item, 以及通过postDelayed来设置响应左右滑动事件
    */
   @Override
   public boolean dispatchTouchEvent(MotionEvent event) {
     switch (event.getAction()) {
     case MotionEvent.ACTION_DOWN: {
       addVelocityTracker(event);
  
       // 假如scroller滚动还没有结束,我们直接返回
       if (!scroller.isFinished()) {
         return super .dispatchTouchEvent(event);
       }
       downX = ( int ) event.getX();
       downY = ( int ) event.getY();
  
       slidePosition = pointToPosition(downX, downY);
  
       // 无效的position, 不做任何处理
       if (slidePosition == AdapterView.INVALID_POSITION) {
         return super .dispatchTouchEvent(event);
       }
  
       // 获取我们点击的item view
       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);
   }
  
   /**
    * 往右滑动,getScrollX()返回的是左边缘的距离,就是以View左边缘为原点到开始滑动的距离,所以向右边滑动为负值
    */
   private void scrollRight() {
     removeDirection = RemoveDirection.RIGHT;
     final int delta = (screenWidth + itemView.getScrollX());
     // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
     scroller.startScroll(itemView.getScrollX(), 0 , -delta, 0 ,
         Math.abs(delta));
     postInvalidate(); // 刷新itemView
   }
  
   /**
    * 向左滑动,根据上面我们知道向左滑动为正值
    */
   private void scrollLeft() {
     removeDirection = RemoveDirection.LEFT;
     final int delta = (screenWidth - itemView.getScrollX());
     // 调用startScroll方法来设置一些滚动的参数,我们在computeScroll()方法中调用scrollTo来滚动item
     scroller.startScroll(itemView.getScrollX(), 0 , delta, 0 ,
         Math.abs(delta));
     postInvalidate(); // 刷新itemView
   }
  
   /**
    * 根据手指滚动itemView的距离来判断是滚动到开始位置还是向左或者向右滚动
    */
   private void scrollByDistanceX() {
     // 如果向左滚动的距离大于屏幕的二分之一,就让其删除
     if (itemView.getScrollX() >= screenWidth / 2 ) {
       scrollLeft();
     } else if (itemView.getScrollX() <= -screenWidth / 2 ) {
       scrollRight();
     } else {
       // 滚回到原始位置,为了偷下懒这里是直接调用scrollTo滚动
       itemView.scrollTo( 0 , 0 );
     }
  
   }
  
   /**
    * 处理我们拖动ListView item的逻辑
    */
   @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滚动, deltaX大于0向左滚动,小于0向右滚
         itemView.scrollBy(deltaX, 0 );
          
         return true ; //拖动的时候ListView不滚动
       case MotionEvent.ACTION_UP:
         int velocityX = getScrollVelocity();
         if (velocityX > SNAP_VELOCITY) {
           scrollRight();
         } else if (velocityX < -SNAP_VELOCITY) {
           scrollLeft();
         } else {
           scrollByDistanceX();
         }
          
         recycleVelocityTracker();
         // 手指离开的时候就不响应左右滚动
         isSlide = false ;
         break ;
       }
     }
  
     //否则直接交给ListView来处理onTouchEvent事件
     return super .onTouchEvent(ev);
   }
  
   @Override
   public void computeScroll() {
     // 调用startScroll的时候scroller.computeScrollOffset()返回true,
     if (scroller.computeScrollOffset()) {
       // 让ListView item根据当前的滚动偏移量进行滚动
       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);
       }
     }
   }
  
   /**
    * 添加用户的速度跟踪器
    *
    * @param event
    */
   private void addVelocityTracker(MotionEvent event) {
     if (velocityTracker == null ) {
       velocityTracker = VelocityTracker.obtain();
     }
  
     velocityTracker.addMovement(event);
   }
  
   /**
    * 移除用户速度跟踪器
    */
   private void recycleVelocityTracker() {
     if (velocityTracker != null ) {
       velocityTracker.recycle();
       velocityTracker = null ;
     }
   }
  
   /**
    * 获取X方向的滑动速度,大于0向右滑动,反之向左
    *
    * @return
    */
   private int getScrollVelocity() {
     velocityTracker.computeCurrentVelocity( 1000 );
     int velocity = ( int ) velocityTracker.getXVelocity();
     return velocity;
   }
  
   /**
    *
    * 当ListView item滑出屏幕,回调这个接口
    * 我们需要在回调方法removeItem()中移除该Item,然后刷新ListView
    *
    * @author xiaanming
    *
    */
   public interface RemoveListener {
     public void removeItem(RemoveDirection direction, int position);
   }
  
}

首先我们重写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()里面的这段代码

?
1
itemView.scrollTo( 0 , 0 );

我们需要将该item滚动在(0, 0 )这个点,因为我们只是将ListView的Item滚动出屏幕而已,并没有将该item移除,而且我们不能手动调用removeView()来从ListView中移除该item,我们只能通过改变ListView的数据,然后通过notifyDataSetChanged()来刷新ListView,所以我们需要将其滚动至(0, 0),这里比较关键。
定义好了我们左右滑动的ListView,接下来就来使用它,布局很简单,一个RelativeLayout包裹我们自定义的ListView

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
< 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的布局

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<? 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 >

还记得我在上一篇文章中提到过调用scrollTo()方法是对里面的子View进行滚动的,而不是对整个布局进行滚动的,所以我们用LinearLayout来套住我们的item的布局,这点需要注意一下,不然滚动的只是TextView。
主页面MainActivity里面的代码比较简单,里面使用的也是ArrayAdapter,相信大家都能看懂

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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( "滑动删除" + 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();
       }
     });
   }
  
    
   //滑动删除之后的回调方法
   @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设置RemoveListener,然后我们在回调方法removeItem(RemoveDirection direction, int position)中删除该position的数据,在调用notifyDataSetChanged()刷新ListView,我这里用的是ArrayAdatper,直接调用remove()就可以了。
所有的代码就编写完了,我们来运行下程序看看效果吧

 

 

 

 

使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果
再给大家来一个ListView左右滑动删除Item效果的例子,上面使用的是滑动类Scroller来实现的,但是看了下通知栏的左右滑动删除效果,确实很棒,当我们滑动Item超过一半的时候,item的透明度就变成了0,我们就知道抬起手指的时候item就被删除了,当item的透明度不为0的时候,我们抬起手指Item会回到起始位置,这样我们就知道拖动到什么位置item会删除,什么位置Item不删除,用户体验更好了,还有一个效果,就是我们滑动删除了item的时候,ListView的其他item会出现向上或者向下滚动的效果,感觉效果很棒,所以在GitHub上面搜索了下,发现很多开源库都有这个效果,比如ListViewAnimations, android-swipelistview等等,我看了下实现原理,使用的是Jake Wharton的动画开源库NineOldAndroids,这个库究竟是干嘛的呢?在API3.0(Honeycomb), SDK新增了一个android.animation包,里面的类是实现动画效果相关的类,通过Honeycomb API,能够实现非常复杂的动画效果,但是如果开发者想在3.0以下使用这一套API, 则需要使用开源框架Nine Old Androids,在这个库中会根据我们运行的机器判断其SDK版本,如果是API3.0以上则使用Android自带的动画类,否则就使用Nine Old Androids库中,这是一个兼容库,接下来我们就来看看这个效果的具体实现吧
实现该效果的主要思路
先根据手指触摸的点来获取点击的是ListView的哪一个Item
当手指在屏幕上面滑动的时候,我们要使得Item跟随手指的滑动而滑动
当我们抬起手指的时候,我们根据滑动的距离或者手指在屏幕上面的速度来判断Item是滑出屏幕还是滑动至其实位置
Item滑出屏幕时,使ListView的其他item产生向上挤压或者向下挤压的效果
大致的思路这是这四步,其中的一些细节接下来我会一一为大家解答的,接下来我们就用代码来实现这种效果吧
首先我们新建一个工程,叫Swipedismisslistview,我们需要将Nine Old Androids这个库引入到工程,大家可以去https://github.com/JakeWharton/NineOldAndroids下载,可以使用Jar包,也可以使用工程库的形式引入到我们自己的工程,我们还需要自定义一个ListView,我们先看代码然后给大家讲解下具体的功能实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
package com.example.swipedismisslistview;
  
import static com.nineoldandroids.view.ViewHelper.setAlpha;
import static com.nineoldandroids.view.ViewHelper.setTranslationX;
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.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
  
import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.AnimatorListenerAdapter;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.view.ViewHelper;
import com.nineoldandroids.view.ViewPropertyAnimator;
 
public class SwipeDismissListView extends ListView {
   /**
    * 认为是用户滑动的最小距离
    */
   private int mSlop;
   /**
    * 滑动的最小速度
    */
   private int mMinFlingVelocity;
   /**
    * 滑动的最大速度
    */
   private int mMaxFlingVelocity;
   /**
    * 执行动画的时间
    */
   protected long mAnimationTime = 150 ;
   /**
    * 用来标记用户是否正在滑动中
    */
   private boolean mSwiping;
   /**
    * 滑动速度检测类
    */
   private VelocityTracker mVelocityTracker;
   /**
    * 手指按下的position
    */
   private int mDownPosition;
   /**
    * 按下的item对应的View
    */
   private View mDownView;
   private float mDownX;
   private float mDownY;
   /**
    * item的宽度
    */
   private int mViewWidth;
   /**
    * 当ListView的Item滑出界面回调的接口
    */
   private OnDismissCallback onDismissCallback;
  
   /**
    * 设置动画时间
    *
    * @param mAnimationTime
    */
   public void setmAnimationTime( long mAnimationTime) {
     this .mAnimationTime = mAnimationTime;
   }
  
   /**
    * 设置删除回调接口
    *
    * @param onDismissCallback
    */
   public void setOnDismissCallback(OnDismissCallback onDismissCallback) {
     this .onDismissCallback = onDismissCallback;
   }
  
   public SwipeDismissListView(Context context) {
     this (context, null );
   }
  
   public SwipeDismissListView(Context context, AttributeSet attrs) {
     this (context, attrs, 0 );
   }
  
   public SwipeDismissListView(Context context, AttributeSet attrs,
       int defStyle) {
     super (context, attrs, defStyle);
  
     ViewConfiguration vc = ViewConfiguration.get(context);
     mSlop = vc.getScaledTouchSlop();
     mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 8 ; //获取滑动的最小速度
     mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); //获取滑动的最大速度
   }
  
    
   @Override
   public boolean onTouchEvent(MotionEvent ev) {
     switch (ev.getAction()) {
     case MotionEvent.ACTION_DOWN:
       handleActionDown(ev);
       break ;
     case MotionEvent.ACTION_MOVE:
       return handleActionMove(ev);
     case MotionEvent.ACTION_UP:
       handleActionUp(ev);
       break ;
     }
     return super .onTouchEvent(ev);
   }
  
   /**
    * 按下事件处理
    *
    * @param ev
    * @return
    */
   private void handleActionDown(MotionEvent ev) {
     mDownX = ev.getX();
     mDownY = ev.getY();
      
     mDownPosition = pointToPosition(( int ) mDownX, ( int ) mDownY);
  
     if (mDownPosition == AdapterView.INVALID_POSITION) {
       return ;
     }
  
     mDownView = getChildAt(mDownPosition - getFirstVisiblePosition());
  
     if (mDownView != null ) {
       mViewWidth = mDownView.getWidth();
     }
  
     //加入速度检测
     mVelocityTracker = VelocityTracker.obtain();
     mVelocityTracker.addMovement(ev);
   }
    
  
   /**
    * 处理手指滑动的方法
    *
    * @param ev
    * @return
    */
   private boolean handleActionMove(MotionEvent ev) {
     if (mVelocityTracker == null || mDownView == null ) {
       return super .onTouchEvent(ev);
     }
  
     // 获取X方向滑动的距离
     float deltaX = ev.getX() - mDownX;
     float deltaY = ev.getY() - mDownY;
  
     // X方向滑动的距离大于mSlop并且Y方向滑动的距离小于mSlop,表示可以滑动
     if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < mSlop) {
       mSwiping = true ;
        
       //当手指滑动item,取消item的点击事件,不然我们滑动Item也伴随着item点击事件的发生
       MotionEvent cancelEvent = MotionEvent.obtain(ev);
       cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
             (ev.getActionIndex()<< MotionEvent.ACTION_POINTER_INDEX_SHIFT));
       onTouchEvent(cancelEvent);
     }
  
     if (mSwiping) {
       // 跟谁手指移动item
       ViewHelper.setTranslationX(mDownView, deltaX);
       // 透明度渐变
       ViewHelper.setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX)/ mViewWidth)));
  
       // 手指滑动的时候,返回true,表示SwipeDismissListView自己处理onTouchEvent,其他的就交给父类来处理
       return true ;
     }
  
     return super .onTouchEvent(ev);
  
   }
  
   /**
    * 手指抬起的事件处理
    * @param ev
    */
   private void handleActionUp(MotionEvent ev) {
     if (mVelocityTracker == null || mDownView == null || !mSwiping) {
       return ;
     }
  
     float deltaX = ev.getX() - mDownX;
      
     //通过滑动的距离计算出X,Y方向的速度
     mVelocityTracker.computeCurrentVelocity( 1000 );
     float velocityX = Math.abs(mVelocityTracker.getXVelocity());
     float velocityY = Math.abs(mVelocityTracker.getYVelocity());
      
     boolean dismiss = false ; //item是否要滑出屏幕
     boolean dismissRight = false ; //是否往右边删除
      
     //当拖动item的距离大于item的一半,item滑出屏幕
     if (Math.abs(deltaX) > mViewWidth / 2 ) {
       dismiss = true ;
       dismissRight = deltaX > 0 ;
        
       //手指在屏幕滑动的速度在某个范围内,也使得item滑出屏幕
     } else if (mMinFlingVelocity <= velocityX
         && velocityX <= mMaxFlingVelocity && velocityY < velocityX) {
       dismiss = true ;
       dismissRight = mVelocityTracker.getXVelocity() > 0 ;
     }
  
     if (dismiss) {
       ViewPropertyAnimator.animate(mDownView)
           .translationX(dismissRight ? mViewWidth : -mViewWidth) //X轴方向的移动距离
           .alpha( 0 )
           .setDuration(mAnimationTime)
           .setListener( new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
               //Item滑出界面之后执行删除
               performDismiss(mDownView, mDownPosition);
             }
           });
     } else {
       //将item滑动至开始位置
       ViewPropertyAnimator.animate(mDownView)
       .translationX( 0 )
       .alpha( 1 )  
       .setDuration(mAnimationTime).setListener( null );
     }
      
     //移除速度检测
     if (mVelocityTracker != null ){
       mVelocityTracker.recycle();
       mVelocityTracker = null ;
     }
      
     mSwiping = false ;
   }
    
  
    
   /**
    * 在此方法中执行item删除之后,其他的item向上或者向下滚动的动画,并且将position回调到方法onDismiss()中
    * @param dismissView
    * @param dismissPosition
    */
   private void performDismiss( final View dismissView, final int dismissPosition) {
     final ViewGroup.LayoutParams lp = dismissView.getLayoutParams(); //获取item的布局参数
     final int originalHeight = dismissView.getHeight(); //item的高度
  
     ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 0 ).setDuration(mAnimationTime);
     animator.start();
  
     animator.addListener( new AnimatorListenerAdapter() {
       @Override
       public void onAnimationEnd(Animator animation) {
         if (onDismissCallback != null ) {
           onDismissCallback.onDismiss(dismissPosition);
         }
  
         //这段代码很重要,因为我们并没有将item从ListView中移除,而是将item的高度设置为0
         //所以我们在动画执行完毕之后将item设置回来
         ViewHelper.setAlpha(dismissView, 1f);
         ViewHelper.setTranslationX(dismissView, 0 );
         ViewGroup.LayoutParams lp = dismissView.getLayoutParams();
         lp.height = originalHeight;
         dismissView.setLayoutParams(lp);
  
       }
     });
  
     animator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() {
       @Override
       public void onAnimationUpdate(ValueAnimator valueAnimator) {
         //这段代码的效果是ListView删除某item之后,其他的item向上滑动的效果
         lp.height = (Integer) valueAnimator.getAnimatedValue();
         dismissView.setLayoutParams(lp);
       }
     });
  
   }
  
   /**
    * 删除的回调接口
    *
    * @author xiaanming
    *
    */
   public interface OnDismissCallback {
     public void onDismiss( int dismissPosition);
   }
  
}

看过Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果你会发现,这个自定义的SwipeDismissListView只重写了onTouchEvent()方法,其实我们重写这一个方法就能实现我们需要的效果
1. 我们先看手指按下屏幕的处理方法handleActionDown();该方法里面根据我们手指按下的点根据pointToPosition()方法来获取我们点击的position,然后利用getChildAt()来获取我们按下的item的View对象,并且加入手指在屏幕滑动的速度检查,这一步相对来说还是比较简单

2. 接下来就是手指在屏幕上面滑动的处理方法handleActionMove(),这个方法就稍微的复杂些,我们需要根据手指在X轴的滑动距离和Y轴的滑动距离来判断是ListView item的水平滑动还是ListView的上下滑动,当满足Math.abs(deltaX) > mSlop && Math.abs(deltaY) < mSlop这个条件时候,我们用一个布尔值mSwiping来标记Item现在处于水平滑动的状态,这时候我们需要处理Item跟随手指的滑动而滑动的逻辑,我们使用ViewHelper来处理Item的滑动逻辑,这个类会根据机器的SDK版本来判断使用Android系统的API还是NineOldandroids中自己实现的API使得View滑动的效果,NineOldandroids中主要使用Camera(可以实现各种复杂动画效果的类),我们直接使用ViewHelper的setTranslationX()和setAlpha()就实现了item滑动和透明度渐变的效果,为了使得我们在滑动item的时候,ListView不上下滚动,我们必须返回true来屏蔽ListView的上下滚动,这里需要我们要非常熟悉Android的事件分发机制,这里我就不说明了,大家不了解的去网上找找相关的文章看看
还有一个问题,就是当我们滑动ListView的item的时候,会伴随着item的点击事件,这不是我们想要的效果,所以当Item滑动的时候我们需要取消ListView Item的点击事件

3. 在看手指抬起的时候的处理方法handleActionUp(),这里面需要根据手指的滑动速度或者Item移动的距离来判断Item是滑出屏幕还是滑动至起始位置,并且要判断item向左还是向右滑出屏幕等等逻辑,具体的逻辑可以看代码,相信大家都看得懂.
我这里要说说ViewPropertyAnimator类,这个类能更好的实现一个View同时进行多个动画的功能,当然我们也可以使用ObjectAnimator利用AnimatorSet来实现一个View上的多个同时进行的动画效果,例如我们可以将

?
1
2
3
4
5
6
7
8
9
10
11
ViewPropertyAnimator.animate(mDownView)
     .translationX(dismissRight ? mViewWidth : -mViewWidth) //X轴方向的移动距离
     .alpha( 0 )
     .setDuration(mAnimationTime)
     .setListener( new AnimatorListenerAdapter() {
       @Override
       public void onAnimationEnd(Animator animation) {
         //Item滑出界面之后执行删除
         performDismiss(mDownView, mDownPosition);
       }
     });

替换成

?
1
2
3
4
5
6
7
8
9
10
11
AnimatorSet set = new AnimatorSet();
       set.playTogether(ObjectAnimator.ofFloat(mDownView, "translationX" , dismissRight ? mViewWidth : -mViewWidth), 
               ObjectAnimator.ofFloat(mDownView, "alpha" , 0 ));
       set.setDuration(mAnimationTime).start();
       set.addListener( new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
               //Item滑出界面之后执行删除
               performDismiss(mDownView, mDownPosition);
             }
           });

在效果上面是一样的,但是ViewPropertyAnimator在性能上要比使用ObjectAnimator来实现多个同时进行的动画要高的多,举个例子,假如要对View使用移动和透明度的动画,使用ViewPropertyAnimator的话,某个时间点上我们只需要调用一次invalidate()方法刷新界面就行了,而使用ObjectAnimator的话,移动的动画需要调用invalidate(),透明度的动画也需要调用invalidate()方法,在性能上使用AnimationSet比ViewPropertyAnimator要低,但是有的时候我们还是需要使用ObjectAnimator,比如,在某个时间内,我们需要将View先变大在变小在变大等复杂情况,这时候ObjectAnimator就派上用场了,例如

ObjectAnimator.ofInt(mDownView, "scaleX", 0 ,100 ,0, 100).setDuration(100).start()  
通过上面的几步我们就实现了ListView的左右滑动删除item的效果啦,但是还有一个效果,item删除之后,ListView的其他item向上或者向下缓缓滑动的效果,实现这个也很容易,就是动态设置item的高度,item高度逐渐变小,这样其他的item就会出现向上或者向下挤压的效果啦!

4. 这里我们使用的是ValueAnimator这个类,这个类并不是针对View作用的动画,而是对某个值作用的动画,他默认使用的Interpolator(插补器)是AccelerateDecelerateInterpolator(开始和结束的时候慢,中间快) , 举个很简单的例子,我们在10秒内使用ValueAnimator将某个值从0变化到100,如果使用LinearInterpolator(线性插补器,匀速变化)在第2秒的时候,这个值变成了20,而是用AccelerateDecelerateInterpolator,可能在第二秒的时候这个值为15或者13,所以我们在ValueAnimator变化的时候设置值动画变化的监听器AnimatorUpdateListener就知道某个时间这个值变成了多少,从而对View的某个属性进行设置(例如大小),所以ValueAnimator是间接的对View设置动画的
了解了ValueAnimator的使用原理,我们就可以现实上面的动画效果了,我们使用ValueAnimator将item的高度变成0,设置ValueAnimator变化的监听,我们在回调函数onAnimationUpdate()中动态的设置item的高度, 然后添加AnimatorListener监听动画的状态(例如动画开始,结束,重复等)监听,在动画结束的回调函数onAnimationEnd()中删除该item的数据,调用notifyDataSetChanged刷新ListView,看看下面这段代码

?
1
2
3
4
5
ViewHelper.setAlpha(dismissView, 1f);
         ViewHelper.setTranslationX(dismissView, 0 );
         ViewGroup.LayoutParams lp = dismissView.getLayoutParams();
         lp.height = originalHeight;
         dismissView.setLayoutParams(lp);

我们使用动画只是将item移动出了屏幕,并且将item的高度设置为了0,并没有将item的View从ListView中Remove掉,况且ListView也不能直接Remove掉Item的,只能将数据源删除,在调用notifyDataSetChanged()刷新,所以我们需要将刚刚滑出屏幕高度设置为0的Item恢复回来

自定义控件的代码我们已经编写完了,接下来我们就要使用它了,先看界面的布局代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
< 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"
   
   < com.example.swipedismisslistview.SwipeDismissListView
     android:id = "@+id/swipeDismissListView" 
     android:layout_width = "match_parent" 
     android:layout_height = "match_parent" 
     android:listSelector = "@android:color/transparent"
     android:cacheColorHint = "@android:color/transparent"
   </ com.example.swipedismisslistview.SwipeDismissListView >
   
</ RelativeLayout

很简单,一个RelativeLayout包裹我们自定义的ListView控件,接下来就是主界面的代码编写,跟平常的ListView使用一样,但是我们需要设置OnDismissCallback()监听,在
onDismiss()中删除该位置对于的数据,刷新ListView

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.example.swipedismisslistview;
  
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.swipedismisslistview.SwipeDismissListView.OnDismissCallback;
  
public class SwipeActivity extends Activity {
   private SwipeDismissListView swipeDismissListView;
   private ArrayAdapter<String> adapter;
   private List<String> dataSourceList = new ArrayList<String>();
  
   @Override
   protected void onCreate(Bundle savedInstanceState) {
     super .onCreate(savedInstanceState);
     setContentView(R.layout.activity_swipe);
     init();
   }
  
   private void init() {
     swipeDismissListView = (SwipeDismissListView) findViewById(R.id.swipeDismissListView);
     for ( int i = 0 ; i < 20 ; i++) {
       dataSourceList.add( "滑动删除" + i);
     }
  
     adapter = new ArrayAdapter<String>( this ,
         android.R.layout.simple_list_item_1,
         android.R.id.text1, dataSourceList);
      
     swipeDismissListView.setAdapter(adapter);
      
     swipeDismissListView.setOnDismissCallback( new OnDismissCallback() {
        
       @Override
       public void onDismiss( int dismissPosition) {
          adapter.remove(adapter.getItem(dismissPosition)); 
       }
     });
      
      
     swipeDismissListView.setOnItemClickListener( new OnItemClickListener() {
  
       @Override
       public void onItemClick(AdapterView<?> parent, View view,
           int position, long id) {
         Toast.makeText(SwipeActivity. this , adapter.getItem(position), Toast.LENGTH_SHORT).show();
       }
     });
  
   }
  
}

所有的代码都已经编写完毕了,接下来就是运行工程,看看具体的效果是不是我们想要的

201645144001733.gif (411×698)

转载于:https://www.cnblogs.com/jingzhi/p/5602874.html

android listview item 左右滑动 android listview 左右滑动 实现 滑动删除 立即下载

相关推荐

Listview左右滑动实现

实现listview左右滑动。当您的界面里面含有listview的时候,你仍然可以使用手势监听器,滑动切换activity

listview左右滑动item效果

这部分内容可以结合上一部分滑动删除的部分,做一个更好的滑动删除效果。 功能:在listview上,左右滑动,对应的item有左右滑动效果 主要实现思路: 检测listview的touch操作,down时,记录位置,move时,做item滑动,up时,回滚到原始位置。 实现代码: mListView.setOnTouchListener(new OnTouchLis

胡老哈的学习记录 2684

Android ListView 中item的左右滑动动画效果的实现

公司有个项目要用到类似手机QQ聊天记录列表ListView左右滑动后改变item的效果,网上没找到好的代码,偶然在安卓巴士的开源站http://d.apkbus.com/里面找到了SwipeToDismiss的源码http://d.apkbus.com/android/523ebc8f6803fab747000002顿时觉得可以修改一下dismiss的效果,改成自己的显示另一个view的效果就行了,于是下载了源码,源码还是比较少的,稍微花了点时间修改了源码,基本达到了我自己的要求,共享一下给大家,对于初学者Java代码阅读可能比较吃力,如果以后有致力于Android开发的朋友欢迎加我,大家互相学习交流一起进步!

RecyclerView(或ListView)添加右侧侧边滑动滚动条进度条ScrollBar

首先,我们需要先画出它的shape——打开res资源文件夹底下的drawable文件夹,右击——New——Drawable Resource File,输入文件名,例如my_scrollBar_shape,其余默认。这个东西叫ScrollBar,具体的动画形式是当用户上下或者左右滑动界面时,这个滑块会在需要显示的位置滑动。如代码,这个滑块我们已经做好了,可以在xml文件的design里面看到缩略图。这个文件的作用就是画出图片中的灰色渐变长矩形,也就是我们的滑块。

qq_40694393的博客 3885

ListView Item左右滑动

import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfigura

mikesno1的专栏 827

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

我在上一篇文章中Android 带你从源码的角度解析Scroller的滚动实现原理从源码的角度介绍了Scroller的滚动实现原理,相信大家对Scroller的使用有一定的了解,这篇文章就给大家带来使用Scroller的小例子,来帮助大家更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我们就能实现很多滑动的效果。例如侧滑菜单,launcher,ListView的下拉刷新等等效果,我今天实现的是ListView的item的左右滑动删除item的效果,现在很多朋友看到这个效果应该是在Andr

Mobile Internet developer 13万+

android滑动删除编辑,Android实现ListView左右滑动删除和编辑

有时候,为了实现项目中的需求,完成设计好的用户交互体验,不的不把这些View重新改造成自己想要的效果。Android原生的ListView是不支持左右滑动的,但是看到微信电话本上,联系人可以左右滑动进行操作的,就通过自己的设想和思路,并加以实现了。思路:1.获取到手指放到屏幕时的x,y位置,并判断点击的处于ListView的那个position。2.判断滑动的方向,如果是上下方向,touch事件就...

weixin_39628864的博客 828

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

今天还是给大家带来自定义控件的编写,自定义一个ListView左右滑动删除Item的效果,这个效果之前已经实现过了,有兴趣的可以看下Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果,之前使用的是滑动类Scroller来实现的,但是看了下通知栏的左右滑动删除效果,确实很棒,当我们滑动Item超过一半的时候,item的透明度就变成了0,我们就知道抬起手指的时候item就被删除了,当item的透明度不为0的时候,我们抬起手指Item会回到起始位置,这样我们就知道拖动到什么位

Mobile Internet developer 11万+

Android SwipeToDismiss:左右滑动删除ListView条目Item

Android的SwipeToDismiss是github上一个第三方开源框架(github上的项目链接地址:https://github.com/romannurik/Android-SwipeToDismiss )。该开源项目旨在:使得一个ListView的item在用户的手指在屏幕上左滑或者右滑时候,删除当前的这个ListView Item。  此种特效在新版的Android中应用不少

McDull的博客 2250

android 实现Listview左右滑动删除Item

Listview左右滑动,显示一个删除按钮,点击按钮,删除相应的item

3万+

Android SwipeToDismiss:左滑/右滑删除ListView条目Item

 《Android SwipeToDismiss:左右滑动删除ListView条目Item》 Android的SwipeToDismiss是github上一个第三方开源框架(github上的项目链接地址:https://github.com/romannurik/Androi...

chuoshao2508的博客 173

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

转帖请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17539199),请尊重他人的辛勤劳动成果,谢谢! 我在上一篇文章中Android 带你从源码的角度解析Scroller的滚动实现原理从源码的角度介绍了Scroller的滚动实现原理,相信大家对Scroller的使用有一定的了解,这篇文章就给大家带...

dijue6306的博客 176
上一篇: 创业笔记2
下一篇: Beta阶段项目总结
weixin_30498921
博客等级 码龄11年 113粉丝 0原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值