转载请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/18311877),请尊重他人的辛勤劳动成果,谢谢!

今天还是给大家带来自定义控件的编写,自定义一个ListView的左右滑动删除Item的效果,这个效果之前已经实现过了,有兴趣的可以看下Android 使用Scroller实现绚丽的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库中,这是一个兼容库,接下来我们就来看看这个效果的具体实现吧

实现该效果的主要思路

  1. 先根据手指触摸的点来获取点击的是ListView的哪一个Item
  2. 当手指在屏幕上面滑动的时候,我们要使得Item跟随手指的滑动而滑动
  3. 当我们抬起手指的时候,我们根据滑动的距离或者手指在屏幕上面的速度来判断Item是滑出屏幕还是滑动至其实位置
  4. Item滑出屏幕时,使ListView的其他item产生向上挤压或者向下挤压的效果

大致的思路这是这四步,其中的一些细节接下来我会一一为大家解答的,接下来我们就用代码来实现这种效果吧

首先我们新建一个工程,叫Swipedismisslistview,我们需要将Nine Old Androids这个库引入到工程,大家可以去https://github.com/JakeWharton/NineOldAndroids下载,可以使用Jar包,也可以使用工程库的形式引入到我们自己的工程,我们还需要自定义一个ListView,我们先看代码然后给大家讲解下具体的功能实现

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

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

替换成

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

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

1 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 ViewHelper.setAlpha(dismissView, 1f);
2                 ViewHelper.setTranslationX(dismissView, 0);
3                 ViewGroup.LayoutParams lp = dismissView.getLayoutParams();
4                 lp.height = originalHeight;
5                 dismissView.setLayoutParams(lp);

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

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

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

很简单,一个RelativeLayout包裹我们自定义的ListView控件,接下来就是主界面的代码编写,跟平常的ListView使用一样,但是我们需要设置OnDismissCallback()监听,在

onDismiss()中删除该位置对于的数据,刷新ListView

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

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

好 了,今天的讲解到这里结束了,有了NineOldAndroids我们可以在2.x的手机上面实现许多复杂的动画效果,文章也介绍了关于开源库 NineOldAndroids使用的一些知识,文章有点长,希望读者还是先将文章看下,然后自己看看能不能自己实现出来,有什么不明白的地方请在下面留 言,我会为大家解答的!

项目源码,点击下载

转载于:https://www.cnblogs.com/liangstudyhome/p/3920737.html

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

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

    本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17539199) 我在上一篇文章中Android 带你从源码的角度解析 ...

  2. Android ListView 横向滑动删除 Item

    注册ListView的onTouchListener() 具体代码如下: listview.setOnTouchListener(new OnTouchListener() {float x,y,ux ...

  3. Android RecyclerViewSwipeDismiss:水平、垂直方向的拖曳删除item

     Android RecyclerViewSwipeDismiss:水平.垂直方向的拖曳删除item RecyclerViewSwipeDismiss是一种支持RecyclerView的水平.垂直 ...

  4. android.support.v7 fragme,打造最强RecyclerView侧滑菜单,长按拖拽Item,滑动删除Item

    前几天写了一片关于RecyclerView滑动删除Item,RecyclerView长按拖拽Item的博客,本来很简单一个使用,阅读量还挺高的,原博客传送门. 今天介绍一个RecyclerView I ...

  5. android 继承ListView实现滑动删除功能.

    在一些用户体验较好的应用上,可以经常遇见   在ListView中  向左或向右滑动便可删除那一项列表. 具体实现  则是继承ListView实现特定功能即可. (1). 新建 delete_butt ...

  6. 自定义listView添加滑动删除功能

    今天研究了一下android里面的手势,结合昨天学习的自定义View,做了一个自定义的listview,继承自listView,添加了条目的滑动手势操作,滑动后出现一个删除按钮,点击删除按钮,触发一个 ...

  7. 基于ListView的滑动删除、添加、修改

    在很多App应用当中我们都看见过各式各样的listview,当然,我们自己对于这个控件肯定也是相当熟悉,那么为什么要在listview中嵌套进一个侧滑删除等这些细节性的功能呢? 其实原因很简单,就是用 ...

  8. 仿QQ侧滑删除,Listview上下滑动,Listview的iteam的点击事件等bug的解决

         网上ListView横向滑动删除Item这样的介绍也很多,但实用性不强,没有解决横向滑动和item的点击事件的冲突,废话少说,有图有真相,下面直接上代码 1:侧滑 2:侧滑的点击事件 3:i ...

  9. android listview左滑删除

    之前,自己使用listview一直是长按删除,不过发现qq的消息和ios的都是侧滑删除,觉得效果很好,于是自己就想做一个侧滑删除.在网上找了些资料,有很多不是我理想的侧滑删除,最后还是找到了一个不错的 ...

最新文章

  1. java servlet 路径_JavaServlet 路径书写总结
  2. thinkphp框架开启页面gzip压缩
  3. 基于CentOS 搭建 Seafile 专属网盘
  4. vue class绑定方式
  5. You can't specify target table 'TS_AUTH_ADMIN' for update in FROM clause记录
  6. 1650显卡能带动144hz吗_GTX1660显卡能称得起最甜吗?最猛GTX1660显卡性能测评
  7. 保研面试 算法题_面试挂在了一道 LRU 缓存算法设计题
  8. oracle10g无监听配置文件,关于监听配置文件listener.ora的问题
  9. C++ STL 学习笔记__(6)优先级队列priority_queue基本操作
  10. 【论文串讲】从BERT和XLNet到MPNet
  11. 数据外推算法 c语言,[原创]如何进行内插法和外推法的计算
  12. [ZT]COMPAQ PROLIANT 8500上手动安装NetWare 4.11
  13. CROSS APPLY 和 OUTER APPLY 函数
  14. 最新发现----葵花宝典的创始人居然是段誉!!!
  15. 第一周学习前端html的知识总结与感悟
  16. ids for this class must be manually assigned before calling save()报错解决方法
  17. windows下搭建ITS运行环境
  18. 爱奇艺qsv格式视频无损转换为MP4
  19. Win7电脑黑屏按什么键恢复
  20. 实现一个简单的神经网络

热门文章

  1. 序列化的 serialVersionUID 到底有什么用?
  2. 揭秘!Greenplum并行执行引擎到底是如何工作的?
  3. 天天写业务代码?写业务代码中的成长机会!
  4. 中国爬虫违法违规案例汇总
  5. 这简历一看就是包装过的
  6. 浅谈高性能数据库集群 —— 读写分离
  7. 【Python】青少年蓝桥杯_每日一题_7.19_电梯用量
  8. 【Python】青少年蓝桥杯_每日一题_4.03_求偶数
  9. mysql聚集索引可以多列吗_MySQL使用单列索引和多列索引
  10. ygbook和ptcms哪个好_YGBOOK小说系统安装使用教程