time 
设为首页】【收藏本站
当前位置: 主页 > 电脑网络 > 操作系统 > 嵌入式 > Android > Android下使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果

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

时间:2014-01-26 12:15 点击:6730次 字体:[ ]




 

今天还是给大家带来自定义控件的编写,自定义一个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. 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.  

看过前一篇文章你会发现,这个自定义的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的点击事件



本文地址 : http://www.fengfly.com/plus/view-214545-1.html
标签: Android ListView NineOldAndroids实
------分隔线----------------------------
最新评论 查看所有评论
发表评论 查看所有评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
验证码: