上一篇中讲解了一些View的基础知识,这一篇来说说View的滑动。滑动基本上是每个应用都会用到的,比如下拉刷新、SlidingMenu等等。因为手机屏幕小,所以需要通过滑动来显示和隐藏一些内容。
常见的滑动方式有三种:一、通过View本身的scrollTo/scrollBy方法来实现;二、通过动画给View施加平移效果来实现;三、通过改变View的LayoutParams使得View重新布局从而实现滑动。
我们首先来看看scrollTo/scrollBy方法的源码
/**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the x position to scroll to
* @param y the y position to scroll to
*/
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
/**
* Move the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the amount of pixels to scroll by horizontally
* @param y the amount of pixels to scroll by vertically
*/
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
从源码我们可以看出,scrollBy实际上也是调用了scrollTo方法,它实现了基于当前位置的相对滑动,而scrollTo则实现了基于所传递参数的绝对滑动。需要注意的是:scrollBy和scrollTo只能改变View内容的位置而不能改变View在布局中的位置。
通过动画来实现View的滑动主要是操作View的translationX和translationY属性(上一篇View的基础知识中介绍过的)。使用动画来实现View的移动可以采用传统的View动画,也可以采用属性动画,如果采用属性动画,则需要采用开源动画库nineoldandroids来兼容3.0以下的版本。
如何使用动画来移动View这里就不说了,这个会在后面的篇章中说明。
在使用动画来做View的移动的时候需要注意的是:View动画是对View的影像做操作,它并不能真正改变View的位置参数,包括宽高,并且如果希望动画后的状态得以保留还必须将fillAfter属性设置为true,否则动画完成后会消失。在使用属性动画却不会出现上述问题,但是在Android 3.0以下无法使用属性动画。
View动画并不能真正改变View的位置,这会带来一个问题:动画完成后View的位置在布局中是不会变的,即使在那个位置什么也看不见,但是当点击那个位置的时候还是能够实现点击事件的。而属性动画却没有这个问题。如果应用不需要兼容到3.0以下系统的话可以使用属性动画。
通过改变View的LayoutParams使得View重新布局从而实现滑动就是改变布局参数来实现滑动。比如把一个button向右移动100px,我们只需要将这个button的LayoutParams里的marginLeft参数值增加100px即可。
第一种scrollTo/scrollBy是View提供的原生方法,它可以比较方便的实现滑动效果并且不会影响内部元素的单击事件,其缺点是只能滑动View的内容,并不能滑动View本身。
通过动画实现View的滑动,如果不考虑版本兼容问题,那么属性动画是没什么明显的缺点;如果使用View动画或者需要兼容Android 3.0以下系统,均不能改变View本身的属性。
而通过改变布局参数来实现滑动除了麻烦一点以外也没有明显的缺点,这个主要适用对象是一些具有交互性的View。
总结一下:
scrollTo/scrollBy:操作简单,适合对View内容的滑动
动画:操作简单,主要适用于没有交互的View和实现复杂的动画效果;
改变布局参数:操作复杂,适用于交互的View。
在上一篇中我们知道scrollTo/scrollBy是瞬间就完成了滑动,这样的体验是非常不好的,所以需要增加一个缓冲时间,即弹性滑动。而实现弹性滑动的大概思想就是:把一次大的滑动分割成若干次小的滑动。下面介绍几种常用的弹性滑动方式。
Scroller在上一篇中已经介绍过了
Scroller scroller = new Scroller(context);
//缓慢滚动到指定位置
private void smoothScrollTo(int destX, int destY){
int scrollX = getScrollX();
int delta = destX - scrollX;
//1000ms内滑向destX
scroller.starScroll(scrollX, 0, delta, 0, 1000);
invalidate();
}
@Override
public void computeScroll(){
if(scroller.computeScrollOffset()){
scrollTo(scroller.getCurrX(), scroller.getCurrY());
postInvalidate();
}
}
从代码中我们以为真正实现的部分是starScroll方法,其实它里面什么都没做,只是保存了我们传递是那几个参数。
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
}
从源码我们已经知道了这个方法仅仅只是保存数据而已,那真正的实现是在哪里呢?答案就是startScroll方法下面的invalidate方法。因为invalidate会导致View进行重绘,在View的draw方法中又会调用computeScroll,而computeScroll又会去想Scroller获取当前的scrollX和scrollY;然后通过scrollTo方法实现滑动;接着又调用postInvalidate方法进行第二次重绘,然后就如此反复,直到整个滑动过程结束。所以Scroller需要配合computeScroll使用才能够实现动画效果。
动画本身是一个渐进的过程,因此通过动画实现滑动本身就具有弹性效果。所以这里就不介绍了。
通过发送一系列延时消息从而达到一种渐进的效果,具体来说就是通过Handler或者View的postDelayed方法,也可以使用线程的sleep方法。这个会使用Handler和postDelayed的就会,这里就不说了。