View基础(二)之滑动
在Android设备上,滑动几乎是应用的标配,不管是下拉刷新还是SlidingMenu,它们的基础都是滑动。从另外一方面来说,Android手机由于屏幕比较小,为了给用户呈现跟多的内容,就需要使用滑动来隐藏和显示一些内容。基于上述两点,可以知道滑动在Android开发中具有很重要的作用。
View的滑动有三种实现方式:
- 通过
View本身提供的scrollTo/scrollBy方法。 - 通过动画给
View施加平移效果来实现滑动。 - 通过改变
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则实现了基于所传递参数的绝对滑动。scrollTo和scrollBy都是改变通过View内部的两个属性mScrollX和mScrollY来实现滑动的,这两个属性可以通过getScrollX和getScrollY方法得到,下面先简要概括一下:在滑动过程中,mScrollX的值总是等于View的左边缘和View内容左边缘在水平方向的距离,而mScrollY的值总是等于View上边缘和View内容上边缘在垂直方向的距离。
View边缘是指View的位置,由四个顶点组成,而View内容边缘是指View中的内容的边缘,scrollTo和scrollBy只能改变View内容的位置而不能改变View在布局中的位置。mScrollX和mScrollY的单位为像素,并且当View左边缘在View内容左边缘的右边时,mScrollX为正值,反之为负值。换句话说,如果从左向右滑动,那么mScrollX为负值,反之为正值;如果从上往下滑动,那么mScrollY为负值,反之则为正值。
为了更好的理解这个问题,下面举个例子,如下图所示(蓝色表示View的内容)。假设图中水平和竖直方向的滑动距离都为100像素,针对图中各种滑动情况,都给出了对应的mScrollX和mScrollY的值。
mScrollX和mScrollY的变化规律示意
使用动画
通过动画我们能够让一个View进行平移,而平移就是一种滑动。使用动画来移动View,主要是操作View的translationX和translationY属性,既可以采用传统的View动画,也可以采用属性动画。
采用View动画的代码如下所示。此动画可以在100ms内将一个View从原始位置向右下角移动100个像素。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<translate
android:duration="100"
android:fromXDelta="0"
android:fromYDelta="0"
android:interpolator="@android:anim/linear_interpolator"
android:toXDelta="100"
android:toYDelta="100"/>
</set>
如果采用属性动画的话,就更简单了,以下代码可以将一个View在100ms内从原始位置向右平移100像素。
ObjectAnimator.ofFloat(targetView,"translationX",0,100)
.setDuration(100).start();
上面简单介绍了通过动画来移动View的方法,关于动画可以跳转至我的另一篇文章:Android动画(一)之View动画。使用动画来做View的滑动需要注意一点,View动画是对View的影像做操作,它并不能改变View的位置参数,包括宽/高,并且如果希望动画后的状态得以保留还必须将fillAfter属性设置为true,否则动画完成后其动画结果会消失,但是使用属性动画则不会存在这个问题。
上面提到了View动画并不能真正改变View的位置,这会带来一个很严重的问题。比如我们通过View动画将一个Button向右移动100px,并且这个View设置有点击事件,那么当点击原始位置时,它的onclick事件会被触发,单击新位置却不会触发,因为它的位置信息并不会随着动画而改变。如果想要解决这个问题可以改为使用属性动画(Android 3.0之后的版本),或者在新位置创建一个的和原view一样的view,并且默认隐藏,当view滑动之后再将其显示出来并隐藏原来的那个view。
改变布局参数
改变布局参数,即改变LayoutParams。例如我们想要将一个Button向右平移100px,我们只需要将这个Button的LayoutParams里的marginLeft参数的值增加100px即可。下面是示例代码:
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) button.getLayoutParams();
params.leftMargin += 100;
button.requestLayout();
//或者 button.setLayoutParams(params);
三种滑动方式的对比
先看scrollTo/scrollBy,它是View提供的原生方法,专门用于View的滑动,但是它只能滑动View的内容,并不能滑动View本身。
再看动画,View动画无法改变View本身的属性,所以如果动画元素不需要响应用户的交互,那么使用动画是比较合适的。且动画还有一个很明显的优点:一些复杂的效果必须要通过动画才能实现。
最后来看改变布局方式,它除了使用起来麻烦点外,也明显的区别,它的主要适用对象是一些具有交互性的View。
针对以上分析再做一下总结:
scrollTo/scrollBy:操作简单,适合对View内容的滑动。- 动画:操作简单,主要适用于没有交互的
View和实现复杂的动画效果。- 改变布局参数:操作稍微复杂,适用于有交互的
View。