转载(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上面搜索了下,发现很多开源库都有这个效果,比如ListViewAnimationsandroid-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,我们先看代码然后给大家讲解下具体的功能实现

**[java]** [view plain](http://blog.csdn.net/xiaanming/article/details/18311877#)[copy](http://blog.csdn.net/xiaanming/article/details/18311877#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/159353)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/159353/fork)
  <div>
    <embed id="ZeroClipboardMovie_1" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_1">
    </embed>
  </div>
</div>
- <span class="keyword">package</span> com.example.swipedismisslistview;

- 
- <span class="keyword">import</span> <span class="keyword">static</span> com.nineoldandroids.view.ViewHelper.setAlpha;

- <span class="keyword">import</span> <span class="keyword">static</span> com.nineoldandroids.view.ViewHelper.setTranslationX;

- <span class="keyword">import</span> android.content.Context;

- <span class="keyword">import</span> android.util.AttributeSet;

- <span class="keyword">import</span> android.view.MotionEvent;

- <span class="keyword">import</span> android.view.VelocityTracker;

- <span class="keyword">import</span> android.view.View;

- <span class="keyword">import</span> android.view.ViewConfiguration;

- <span class="keyword">import</span> android.view.ViewGroup;

- <span class="keyword">import</span> android.widget.AdapterView;

- <span class="keyword">import</span> android.widget.ListView;

- 
- <span class="keyword">import</span> com.nineoldandroids.animation.Animator;

- <span class="keyword">import</span> com.nineoldandroids.animation.AnimatorListenerAdapter;

- <span class="keyword">import</span> com.nineoldandroids.animation.ValueAnimator;

- <span class="keyword">import</span> com.nineoldandroids.view.ViewHelper;

- <span class="keyword">import</span> com.nineoldandroids.view.ViewPropertyAnimator;

- <span class="comment">/**</span>

- <span class="comment"> * @blog http://blog.csdn.net/xiaanming</span>

- <span class="comment"> * </span>

- <span class="comment"> * @author xiaanming</span>

- <span class="comment"> *</span>

- <span class="comment"> */</span>

- <span class="keyword">public</span> <span class="keyword">class</span> SwipeDismissListView <span class="keyword">extends</span> ListView {

- <span class="comment">/**</span>

- <span class="comment">     * 认为是用户滑动的最小距离</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> <span class="keyword">int</span> mSlop;

- <span class="comment">/**</span>

- <span class="comment">     * 滑动的最小速度</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> <span class="keyword">int</span> mMinFlingVelocity;

- <span class="comment">/**</span>

- <span class="comment">     * 滑动的最大速度</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> <span class="keyword">int</span> mMaxFlingVelocity;

- <span class="comment">/**</span>

- <span class="comment">     * 执行动画的时间</span>

- <span class="comment">     */</span>

- <span class="keyword">protected</span> <span class="keyword">long</span> mAnimationTime = <span class="number">150</span>;

- <span class="comment">/**</span>

- <span class="comment">     * 用来标记用户是否正在滑动中</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> <span class="keyword">boolean</span> mSwiping;

- <span class="comment">/**</span>

- <span class="comment">     * 滑动速度检测类</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> VelocityTracker mVelocityTracker;

- <span class="comment">/**</span>

- <span class="comment">     * 手指按下的position</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> <span class="keyword">int</span> mDownPosition;

- <span class="comment">/**</span>

- <span class="comment">     * 按下的item对应的View</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> View mDownView;

- <span class="keyword">private</span> <span class="keyword">float</span> mDownX;

- <span class="keyword">private</span> <span class="keyword">float</span> mDownY;

- <span class="comment">/**</span>

- <span class="comment">     * item的宽度</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> <span class="keyword">int</span> mViewWidth;

- <span class="comment">/**</span>

- <span class="comment">     * 当ListView的Item滑出界面回调的接口</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> OnDismissCallback onDismissCallback;

- 
- <span class="comment">/**</span>

- <span class="comment">     * 设置动画时间</span>

- <span class="comment">     * </span>

- <span class="comment">     * @param mAnimationTime</span>

- <span class="comment">     */</span>

- <span class="keyword">public</span> <span class="keyword">void</span> setmAnimationTime(<span class="keyword">long</span> mAnimationTime) {

- <span class="keyword">this</span>.mAnimationTime = mAnimationTime;

- }

- 
- <span class="comment">/**</span>

- <span class="comment">     * 设置删除回调接口</span>

- <span class="comment">     * </span>

- <span class="comment">     * @param onDismissCallback</span>

- <span class="comment">     */</span>

- <span class="keyword">public</span> <span class="keyword">void</span> setOnDismissCallback(OnDismissCallback onDismissCallback) {

- <span class="keyword">this</span>.onDismissCallback = onDismissCallback;

- }

- 
- <span class="keyword">public</span> SwipeDismissListView(Context context) {

- <span class="keyword">this</span>(context, <span class="keyword">null</span>);

- }

- 
- <span class="keyword">public</span> SwipeDismissListView(Context context, AttributeSet attrs) {

- <span class="keyword">this</span>(context, attrs, <span class="number"></span>);

- }

- 
- <span class="keyword">public</span> SwipeDismissListView(Context context, AttributeSet attrs,

- <span class="keyword">int</span> defStyle) {

- <span class="keyword">super</span>(context, attrs, defStyle);

- 
- ViewConfiguration vc = ViewConfiguration.get(context);

- mSlop = vc.getScaledTouchSlop();

- mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * <span class="number">8</span>; <span class="comment">//获取滑动的最小速度</span>

- mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();  <span class="comment">//获取滑动的最大速度</span>

- }

- 
- 
- <span class="annotation">@Override</span>

- <span class="keyword">public</span> <span class="keyword">boolean</span> onTouchEvent(MotionEvent ev) {

- <span class="keyword">switch</span> (ev.getAction()) {

- <span class="keyword">case</span> MotionEvent.ACTION_DOWN:

- handleActionDown(ev);

- <span class="keyword">break</span>;

- <span class="keyword">case</span> MotionEvent.ACTION_MOVE:

- <span class="keyword">return</span> handleActionMove(ev);

- <span class="keyword">case</span> MotionEvent.ACTION_UP:

- handleActionUp(ev);

- <span class="keyword">break</span>;

- }

- <span class="keyword">return</span> <span class="keyword">super</span>.onTouchEvent(ev);

- }

- 
- <span class="comment">/**</span>

- <span class="comment">     * 按下事件处理</span>

- <span class="comment">     * </span>

- <span class="comment">     * @param ev</span>

- <span class="comment">     * @return</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> <span class="keyword">void</span> handleActionDown(MotionEvent ev) {

- mDownX = ev.getX();

- mDownY = ev.getY();

- 
- mDownPosition = pointToPosition((<span class="keyword">int</span>) mDownX, (<span class="keyword">int</span>) mDownY);

- 
- <span class="keyword">if</span> (mDownPosition == AdapterView.INVALID_POSITION) {

- <span class="keyword">return</span>;

- }

- 
- mDownView = getChildAt(mDownPosition &#8211; getFirstVisiblePosition());

- 
- <span class="keyword">if</span> (mDownView != <span class="keyword">null</span>) {

- mViewWidth = mDownView.getWidth();

- }

- 
- <span class="comment">//加入速度检测</span>

- mVelocityTracker = VelocityTracker.obtain();

- mVelocityTracker.addMovement(ev);

- }

- 
- 
- <span class="comment">/**</span>

- <span class="comment">     * 处理手指滑动的方法</span>

- <span class="comment">     * </span>

- <span class="comment">     * @param ev</span>

- <span class="comment">     * @return</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> <span class="keyword">boolean</span> handleActionMove(MotionEvent ev) {

- <span class="keyword">if</span> (mVelocityTracker == <span class="keyword">null</span> || mDownView == <span class="keyword">null</span>) {

- <span class="keyword">return</span> <span class="keyword">super</span>.onTouchEvent(ev);

- }

- 
- <span class="comment">// 获取X方向滑动的距离</span>

- <span class="keyword">float</span> deltaX = ev.getX() &#8211; mDownX;

- <span class="keyword">float</span> deltaY = ev.getY() &#8211; mDownY;

- 
- <span class="comment">// X方向滑动的距离大于mSlop并且Y方向滑动的距离小于mSlop,表示可以滑动</span>

- <span class="keyword">if</span> (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < mSlop) {

- mSwiping = <span class="keyword">true</span>;

- 
- <span class="comment">//当手指滑动item,取消item的点击事件,不然我们滑动Item也伴随着item点击事件的发生</span>

- MotionEvent cancelEvent = MotionEvent.obtain(ev);

- cancelEvent.setAction(MotionEvent.ACTION_CANCEL |

- (ev.getActionIndex()<< MotionEvent.ACTION_POINTER_INDEX_SHIFT));

- onTouchEvent(cancelEvent);

- }

- 
- <span class="keyword">if</span> (mSwiping) {

- <span class="comment">// 跟谁手指移动item</span>

- ViewHelper.setTranslationX(mDownView, deltaX);

- <span class="comment">// 透明度渐变</span>

- ViewHelper.setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f &#8211; 2f * Math.abs(deltaX)/ mViewWidth)));

- 
- <span class="comment">// 手指滑动的时候,返回true,表示SwipeDismissListView自己处理onTouchEvent,其他的就交给父类来处理</span>

- <span class="keyword">return</span> <span class="keyword">true</span>;

- }

- 
- <span class="keyword">return</span> <span class="keyword">super</span>.onTouchEvent(ev);

- 
- }

- 
- <span class="comment">/**</span>

- <span class="comment">     * 手指抬起的事件处理</span>

- <span class="comment">     * @param ev</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> <span class="keyword">void</span> handleActionUp(MotionEvent ev) {

- <span class="keyword">if</span> (mVelocityTracker == <span class="keyword">null</span> || mDownView == <span class="keyword">null</span>|| !mSwiping) {

- <span class="keyword">return</span>;

- }

- 
- <span class="keyword">float</span> deltaX = ev.getX() &#8211; mDownX;

- 
- <span class="comment">//通过滑动的距离计算出X,Y方向的速度</span>

- mVelocityTracker.computeCurrentVelocity(<span class="number">1000</span>);

- <span class="keyword">float</span> velocityX = Math.abs(mVelocityTracker.getXVelocity());

- <span class="keyword">float</span> velocityY = Math.abs(mVelocityTracker.getYVelocity());

- 
- <span class="keyword">boolean</span> dismiss = <span class="keyword">false</span>; <span class="comment">//item是否要滑出屏幕</span>

- <span class="keyword">boolean</span> dismissRight = <span class="keyword">false</span>;<span class="comment">//是否往右边删除</span>

- 
- <span class="comment">//当拖动item的距离大于item的一半,item滑出屏幕</span>

- <span class="keyword">if</span> (Math.abs(deltaX) > mViewWidth / <span class="number">2</span>) {

- dismiss = <span class="keyword">true</span>;

- dismissRight = deltaX > <span class="number"></span>;

- 
- <span class="comment">//手指在屏幕滑动的速度在某个范围内,也使得item滑出屏幕</span>

- } <span class="keyword">else</span> <span class="keyword">if</span> (mMinFlingVelocity <= velocityX

- && velocityX <= mMaxFlingVelocity && velocityY < velocityX) {

- dismiss = <span class="keyword">true</span>;

- dismissRight = mVelocityTracker.getXVelocity() > <span class="number"></span>;

- }

- 
- <span class="keyword">if</span> (dismiss) {

- ViewPropertyAnimator.animate(mDownView)

- .translationX(dismissRight ? mViewWidth : -mViewWidth)<span class="comment">//X轴方向的移动距离</span>

- .alpha(<span class="number"></span>)

- .setDuration(mAnimationTime)

- .setListener(<span class="keyword">new</span> AnimatorListenerAdapter() {

- <span class="annotation">@Override</span>

- <span class="keyword">public</span> <span class="keyword">void</span> onAnimationEnd(Animator animation) {

- <span class="comment">//Item滑出界面之后执行删除</span>

- performDismiss(mDownView, mDownPosition);

- }

- });

- } <span class="keyword">else</span> {

- <span class="comment">//将item滑动至开始位置</span>

- ViewPropertyAnimator.animate(mDownView)

- .translationX(<span class="number"></span>)

- .alpha(<span class="number">1</span>)

- .setDuration(mAnimationTime).setListener(<span class="keyword">null</span>);

- }

- 
- <span class="comment">//移除速度检测</span>

- <span class="keyword">if</span>(mVelocityTracker != <span class="keyword">null</span>){

- mVelocityTracker.recycle();

- mVelocityTracker = <span class="keyword">null</span>;

- }

- 
- mSwiping = <span class="keyword">false</span>;

- }

- 
- 
- 
- <span class="comment">/**</span>

- <span class="comment">     * 在此方法中执行item删除之后,其他的item向上或者向下滚动的动画,并且将position回调到方法onDismiss()中</span>

- <span class="comment">     * @param dismissView</span>

- <span class="comment">     * @param dismissPosition</span>

- <span class="comment">     */</span>

- <span class="keyword">private</span> <span class="keyword">void</span> performDismiss(<span class="keyword">final</span> View dismissView, <span class="keyword">final</span> <span class="keyword">int</span> dismissPosition) {

- <span class="keyword">final</span> ViewGroup.LayoutParams lp = dismissView.getLayoutParams();<span class="comment">//获取item的布局参数</span>

- <span class="keyword">final</span> <span class="keyword">int</span> originalHeight = dismissView.getHeight();<span class="comment">//item的高度</span>

- 
- ValueAnimator animator = ValueAnimator.ofInt(originalHeight, <span class="number"></span>).setDuration(mAnimationTime);

- animator.start();

- 
- animator.addListener(<span class="keyword">new</span> AnimatorListenerAdapter() {

- <span class="annotation">@Override</span>

- <span class="keyword">public</span> <span class="keyword">void</span> onAnimationEnd(Animator animation) {

- <span class="keyword">if</span> (onDismissCallback != <span class="keyword">null</span>) {

- onDismissCallback.onDismiss(dismissPosition);

- }

- 
- <span class="comment">//这段代码很重要,因为我们并没有将item从ListView中移除,而是将item的高度设置为0</span>

- <span class="comment">//所以我们在动画执行完毕之后将item设置回来</span>

- ViewHelper.setAlpha(dismissView, 1f);

- ViewHelper.setTranslationX(dismissView, <span class="number"></span>);

- ViewGroup.LayoutParams lp = dismissView.getLayoutParams();

- lp.height = originalHeight;

- dismissView.setLayoutParams(lp);

- 
- }

- });

- 
- animator.addUpdateListener(<span class="keyword">new</span> ValueAnimator.AnimatorUpdateListener() {

- <span class="annotation">@Override</span>

- <span class="keyword">public</span> <span class="keyword">void</span> onAnimationUpdate(ValueAnimator valueAnimator) {

- <span class="comment">//这段代码的效果是ListView删除某item之后,其他的item向上滑动的效果</span>

- lp.height = (Integer) valueAnimator.getAnimatedValue();

- dismissView.setLayoutParams(lp);

- }

- });

- 
- }

- 
- <span class="comment">/**</span>

- <span class="comment">     * 删除的回调接口</span>

- <span class="comment">     * </span>

- <span class="comment">     * @author xiaanming</span>

- <span class="comment">     * </span>

- <span class="comment">     */</span>

- <span class="keyword">public</span> <span class="keyword">interface</span> OnDismissCallback {

- <span class="keyword">public</span> <span class="keyword">void</span> onDismiss(<span class="keyword">int</span> 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上的多个同时进行的动画效果,例如我们可以将

**[java]** [view plain](http://blog.csdn.net/xiaanming/article/details/18311877#)[copy](http://blog.csdn.net/xiaanming/article/details/18311877#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/159353)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/159353/fork)
  <div>
    <embed id="ZeroClipboardMovie_2" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_2">
    </embed>
  </div>
</div>
- ViewPropertyAnimator.animate(mDownView)

- .translationX(dismissRight ? mViewWidth : -mViewWidth)<span class="comment">//X轴方向的移动距离</span>

- .alpha(<span class="number"></span>)

- .setDuration(mAnimationTime)

- .setListener(<span class="keyword">new</span> AnimatorListenerAdapter() {

- <span class="annotation">@Override</span>

- <span class="keyword">public</span> <span class="keyword">void</span> onAnimationEnd(Animator animation) {

- <span class="comment">//Item滑出界面之后执行删除</span>

- performDismiss(mDownView, mDownPosition);

- }

- });

替换成

**[java]** [view plain](http://blog.csdn.net/xiaanming/article/details/18311877#)[copy](http://blog.csdn.net/xiaanming/article/details/18311877#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/159353)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/159353/fork)
  <div>
    <embed id="ZeroClipboardMovie_3" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_3">
    </embed>
  </div>
</div>
- AnimatorSet set = <span class="keyword">new</span> AnimatorSet();

- set.playTogether(ObjectAnimator.ofFloat(mDownView, <span class="string">&#8220;translationX&#8221;</span>, dismissRight ? mViewWidth : -mViewWidth),

- ObjectAnimator.ofFloat(mDownView, <span class="string">&#8220;alpha&#8221;</span>, <span class="number"></span>));

- set.setDuration(mAnimationTime).start();

- set.addListener(<span class="keyword">new</span> AnimatorListenerAdapter() {

- <span class="annotation">@Override</span>

- <span class="keyword">public</span> <span class="keyword">void</span> onAnimationEnd(Animator animation) {

- <span class="comment">//Item滑出界面之后执行删除</span>

- performDismiss(mDownView, mDownPosition);

- }

- });

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

**[java]** [view plain](http://blog.csdn.net/xiaanming/article/details/18311877#)[copy](http://blog.csdn.net/xiaanming/article/details/18311877#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/159353)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/159353/fork)
  <div>
    <embed id="ZeroClipboardMovie_4" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_4">
    </embed>
  </div>
</div>
- ObjectAnimator.ofInt(mDownView, <span class="string">&#8220;scaleX&#8221;</span>, <span class="number"></span> ,<span class="number">100</span> ,<span class="number"></span>, <span class="number">100</span>).setDuration(<span class="number">100</span>).start()

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

  1. 这里我们使用的是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,看看下面这段代码

**[java]** [view plain](http://blog.csdn.net/xiaanming/article/details/18311877#)[copy](http://blog.csdn.net/xiaanming/article/details/18311877#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/159353)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/159353/fork)
  <div>
    <embed id="ZeroClipboardMovie_5" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_5">
    </embed>
  </div>
</div>
- ViewHelper.setAlpha(dismissView, 1f);

- ViewHelper.setTranslationX(dismissView, <span class="number"></span>);

- ViewGroup.LayoutParams lp = dismissView.getLayoutParams();

- lp.height = originalHeight;

- dismissView.setLayoutParams(lp);

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

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

**[java]** [view plain](http://blog.csdn.net/xiaanming/article/details/18311877#)[copy](http://blog.csdn.net/xiaanming/article/details/18311877#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/159353)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/159353/fork)
  <div>
    <embed id="ZeroClipboardMovie_6" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_6">
    </embed>
  </div>
</div>
- <RelativeLayout xmlns:android=<span class="string">&#8220;http://schemas.android.com/apk/res/android&#8221;</span>

- xmlns:tools=<span class="string">&#8220;http://schemas.android.com/tools&#8221;</span>

- android:layout_width=<span class="string">&#8220;match_parent&#8221;</span>

- android:layout_height=<span class="string">&#8220;match_parent&#8221;</span>>

- 
- <com.example.swipedismisslistview.SwipeDismissListView

- android:id=<span class="string">&#8220;@+id/swipeDismissListView&#8221;</span>

- android:layout_width=<span class="string">&#8220;match_parent&#8221;</span>

- android:layout_height=<span class="string">&#8220;match_parent&#8221;</span>

- android:listSelector=<span class="string">&#8220;@android:color/transparent&#8221;</span>

- android:cacheColorHint=<span class="string">&#8220;@android:color/transparent&#8221;</span>>

- </com.example.swipedismisslistview.SwipeDismissListView>

- 
- </RelativeLayout>

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

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

**[java]** [view plain](http://blog.csdn.net/xiaanming/article/details/18311877#)[copy](http://blog.csdn.net/xiaanming/article/details/18311877#)[![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)](https://code.csdn.net/snippets/159353)[![派生到我的代码片](https://code.csdn.net/assets/ico_fork.svg)](https://code.csdn.net/snippets/159353/fork)
  <div>
    <embed id="ZeroClipboardMovie_7" src="http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf" type="application/x-shockwave-flash" width="18" height="18" align="middle" name="ZeroClipboardMovie_7">
    </embed>
  </div>
</div>
- <span class="keyword">package</span> com.example.swipedismisslistview;

- 
- <span class="keyword">import</span> java.util.ArrayList;

- <span class="keyword">import</span> java.util.List;

- 
- <span class="keyword">import</span> android.app.Activity;

- <span class="keyword">import</span> android.os.Bundle;

- <span class="keyword">import</span> android.view.View;

- <span class="keyword">import</span> android.widget.AdapterView;

- <span class="keyword">import</span> android.widget.AdapterView.OnItemClickListener;

- <span class="keyword">import</span> android.widget.ArrayAdapter;

- <span class="keyword">import</span> android.widget.Toast;

- 
- <span class="keyword">import</span> com.example.swipedismisslistview.SwipeDismissListView.OnDismissCallback;

- 
- <span class="keyword">public</span> <span class="keyword">class</span> SwipeActivity <span class="keyword">extends</span> Activity {

- <span class="keyword">private</span> SwipeDismissListView swipeDismissListView;

- <span class="keyword">private</span> ArrayAdapter<String> adapter;

- <span class="keyword">private</span> List<String> dataSourceList = <span class="keyword">new</span> ArrayList<String>();

- 
- <span class="annotation">@Override</span>

- <span class="keyword">protected</span> <span class="keyword">void</span> onCreate(Bundle savedInstanceState) {

- <span class="keyword">super</span>.onCreate(savedInstanceState);

- setContentView(R.layout.activity_swipe);

- init();

- }

- 
- <span class="keyword">private</span> <span class="keyword">void</span> init() {

- swipeDismissListView = (SwipeDismissListView) findViewById(R.id.swipeDismissListView);

- <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number"></span>; i < <span class="number">20</span>; i++) {

- dataSourceList.add(<span class="string">&#8220;滑动删除&#8221;</span> + i);

- }

- 
- adapter = <span class="keyword">new</span> ArrayAdapter<String>(<span class="keyword">this</span>,

- android.R.layout.simple_list_item_1,

- android.R.id.text1, dataSourceList);

- 
- swipeDismissListView.setAdapter(adapter);

- 
- swipeDismissListView.setOnDismissCallback(<span class="keyword">new</span> OnDismissCallback() {

- 
- <span class="annotation">@Override</span>

- <span class="keyword">public</span> <span class="keyword">void</span> onDismiss(<span class="keyword">int</span> dismissPosition) {

- adapter.remove(adapter.getItem(dismissPosition));

- }

- });

- 
- 
- swipeDismissListView.setOnItemClickListener(<span class="keyword">new</span> OnItemClickListener() {

- 
- <span class="annotation">@Override</span>

- <span class="keyword">public</span> <span class="keyword">void</span> onItemClick(AdapterView<?> parent, View view,

- <span class="keyword">int</span> position, <span class="keyword">long</span> id) {

- Toast.makeText(SwipeActivity.<span class="keyword">this</span>, adapter.getItem(position), Toast.LENGTH_SHORT).show();

- }

- });

- 
- }

- 
- }

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

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

项目源码,点击下载

ps: 下载源码的时候运行出错,是因为我加入了NineOldAndroids的Jar包,然后又加入了NineOldAndroids工程库,主要是我写DEMO的时候为了方便看源码就导入了NineOldAndroids工程库,大家删除一个JAR包或者NineOldAndroids工程库 就能解决了

💬 评论