Android中常见的实现View的滑动的三种方式:第一种是经过View自己提供的scrollTo/scrollBy方法来实现滑动;第二种是经过动画给View施加平移效果来实现滑动;第三种是经过改变View的LayoutParams使得View从新布局从而实现滑动。java
为了实现View的滑动,View提供了专门的方法来实现这个功能,那就是scrollTo和scrollBy。根据源码,scrollBy实际上也是调用了scrollTo方法,它实现了基于当前位置的相对滑动,而scrollTo则实现了基于所传递参数的绝对滑动。
在滑动过程当中View内部有两个属性mScrollX和mScrollY其改变规则:在滑动过程当中,mScrollX的值老是等于View左边缘和View内容左边缘在水平方向的距离,而mScrollY的值老是等于View上边缘和View内容上边缘在垂直方向上的距离。
须要注意的一点是,mScrollX和mScrollY的单位是像素,而且当View左边缘在View内容左边缘右边时,mScrollX为正值;当View上边缘在View内容下边时,mScrollY为正值。
通过上述的描述,能够发现使用scrollTo和scrollBy来实现View的滑动,只能将View的内容进行移动,而不能将View自己进行移动。而且,scrollTo和scrollBy的滑动过程是瞬间完成的。
android
使用动画来移动View,主要是操做View的translationX和translationY属性,既能够采用传统的补间动画,也能够采用属性动画,若是采用属性动画的话,为了可以兼容3.0如下版本,须要采用开源动画库nineoldandroids。以下为使用属性动画实现View滑动的方式。web
// 使用属性动画实现View的滑动——平移 ObjectAnimator.ofFloat(view, "translationX", 0f, 100f) .setDuration(100) .start();
关于动画的使用方法参看笔记:Android进阶知识(M):帧动画、补间动画和属性动画。ide
View的第三种实现滑动的方式是改变布局参数,即改变LayoutParams。经过改变LayoutParams的方式去实现View的滑动是一种很灵活的方法,须要根据不一样的状况去作不一样的处理。实现方式为设置LayoutParams里的marginLeft和marginTop参数。svg
// 经过改变布局参数使得View滑动 ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams(); layoutParams.leftMargin = x + (rawX - startX); layoutParams.topMargin = y + (rawY - startY); view.requestLayout(); // 或者 view.setLayoutParams(layoutParams);
scrollTo/scrollBy方式:View提供的原生方法,操做简单而且不影响内部元素的单击事件,可是只能滑动View的内容,不能滑动View自己。适合对View内容的滑动。
动画:采用属性动画来滑动没有任何缺点,可是使用补间动画时其没法改变View自己的属性。操做简单,主要适用于没有交互的Veiw和实现复杂的动画效果。
改变布局方式:操做稍微复杂,适用于有交互的View。
布局
对于非弹性滑动,其没法实现渐近式滑动(弹性滑动),用户体验极差,好比scrollTo/scrollBy以及改变布局参数的方法,因为其瞬时性,所以属于非弹性滑动。实现弹性滑动的方式有不少,可是其共同的思想是:将一次大的滑动分红若干次小的滑动并在一个时间段内完成。弹性滑动的实现方式有:Scroller,动画以及延迟策略等。post
Scroller自己没法让View弹性滑动,它须要和View的computeScroll方法配合使用才能共同完成这个功能,使用方式以下。动画
Scroller mScroller = new Scroller(mContext); // 缓慢滚动到指定位置 private void smoothScrollTo(int destX, int destY) { int scrollX = getScrollX(); int deltaX = destX - scrollX; // 1000ms内滑向destX,效果就是慢慢滑动 mScroller.startScroll(scrollx, 0, deltaX, 0, 1000); invalidate(); } @Override public void computeScroll() { if(mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } }
调用startScroll方法后,Scroller内部其实什么都没有作,它只是保存了传递的参数。须要注意的是这里的滑动指的是View内容的滑动而非View自己位置的改变。正在使得View弹性滑动的是invalidate方法,该方法会致使View的重绘,在View中draw方法又会去调用computeScroll方法,而该方法经过调用Scroller的方法进行滑动,在经过调用postInvalidate方法进行二次重绘,以此类推。
而computeScrollOffset会根据时间的流逝来计算当前的scrollX和scrollY的值。
总结一下Scroller的工做原理:Scroller配合View的computerScroll方法完成弹性滑动效果,它不断地让View重绘,而每次重绘距滑动起始时间会有一个时间间隔,经过这个间隔Scroller就能够得出View当前的滑动位置,并经过scrollTo进滑动。
spa
动画自己就是一种渐近的过程,所以经过它来实现的滑动自然就具备弹性效果。
在上一个话题中已经介绍了动画的使用方式,这里很少作介绍。
.net
延时策略的核心思想是经过发送一系列延时消息从而达到一种渐进式的效果,具体来讲可使用Handler或View的postDelayed方法,也可使用线程的sleep方法。使用Handler实现的一种延时策略以下所示。
private static final int MESSAGE_SCROLL_TO = 0; private int mCount = 0; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch(msg.what) { case MSSAGE_SCROLL_TO: mCount ++; if (mCount <= 30) { float fraction = mCount / 30f; int scrollX = (int) (fraction * 100); mImageView.scrollTo(scrollX, 0); mHandler.sentEmptyMessageDelayed(MESSAGE_SCROLL_TO, 33); } break; } } }
参考资料:《Android开发艺术探索》