因为移动设备的屏幕限制,一屏幕的展示往往是不足够的,且在自定义view当中,掌握view的滑动可以使我们实现绚丽的效果,因此掌握view的滑动无论是在日常开发还是创造轮子都是重中之重。
View滑动的三种实现方式:
1,通过View本身提供的scrollTo/scrollBy方法来实现滑动
2,通过动画给View施加平移效果实现滑动
3,通过改变View的LayoutParams使得View重新布局从而实现滑动
一:使用scrollTo/scrollBy
通过查看View的源码,可以得知,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则实现了基于所有传递参数的绝对滑动,它会将当前记录的成员变量mScrollX和mScrollY记录下来,并将要滑动到的新坐标值x,y赋值给mScrollX和mScrollY,最后调用onScrollChanged(mScrollX, mScrollY, oldX, oldY),invalidateParentCaches()这个方法的作用是迫使父控件重建其显示列表,作用是清除父缓存和避免事件失效。
注意:滑动过程中mScrollX和mScrollY的改变规则(可通过getScrollX和getScrollY分别得到)
在滑动过程中,mScrollX总是等于View左边缘和View内容左边缘在水平方向的距离,而mScrollY的值总是等于View上边缘和View内容上边缘在竖直方式的距离。View边缘指的是View的位置,由四个顶点组成,而View内容边缘是指View中的内容的边缘,scrollBy和scrollTo只能改变View内容的位置而不能改变View在布局中的位置。
mScrollX和mScrollY的单位是像素,并且当View左边缘在View内容左边缘的右边时,mScrollX为正值,反之为负(左→右滑动为负值,反之为正);当View的上边缘在View内容上边缘下边时,mScrollY为正,反之为负(上→下 为负,反之为正)。
二:使用动画
通过动画可以对一个View进行平移,而平移就是一种滑动。使用动画来移动View,主要是操作View的translationX和translationY属性,既可以采用传统的View动画,也可以采用属性动画,如果使用属性动画需要考虑兼容3.0以下版本(采用开源动画库nineoldandroids)。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<translate
android:fromXDelta="0"
android:toXDelta="100"
android:fromYDelta="0"
android:toYDelta="100"
android:duration="100" />
</set>
ObjectAnimator.ofFloat(view, "translationX", 0, 100).setDuration(100).start();
注意:View动画是对View的影像做操作,它并不能真正改变View的位置参数,包括宽高,并且如果希望动画消失后的状态得以保留还必须将fillAfter属性设置为true,否则动画完成后其动画效果会消失。使用属性动画并不会出现上述问题,但是在android3.0以下无法使用属性动画,因此需要使用兼容库,但是通过兼容库实现的属性动画本质仍然是View动画。
因为View动画并不能真正改变View的位置,会带来一个严重的问题,如果我们将一个带点击事件的Button移动了,然后点击它新位置的区域,发现无法触发事件了,而点击原位置是可以触发事件,尽管Button已经不在原始位置了。因为不管Button怎么做变换,它的位置信息(四个顶点和宽/高)并没有随着动画而改变,因此在系统眼中,该Button并没有发生任何改变,它依旧处在原始位置,呈现的只是它的影像。从android3.0开始,使用属性动画可以解决上述问题。
三:改变布局参数
即改变LayoutParams。比如我们想把一个View向右平移100px,只需将该View的LayoutParams里的marginLeft参数的值增加100px即可。
MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
params.width += 100;
params.leftMargin += 100;
view.requestLayout();
//或者view.setLayoutParams(params);
上述是实现View滑动的三种实现方式,在开发当中应该根据不同情况去选择适合的方式。