1. 使用 scrollTo()/scrollBy() 实现滑动
scrollTo()
和scrollBy()
方法实现1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/**
* 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(); 老版源码
invalidate(true); // 通知 View 进行重绘
}
}
}1
2
3
4
5
6
7
8
9
10/**
* 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);
}scrollTo()
方法实现了基于所传递参数的绝对滑动;scrollBy()
方法实现了基于当前位置的相对滑动- 滑动过程中,View 内部的两个属性
mScrollX
和mScrollY
可以分别通过方法getScrollX()
和getScrollY()
得到 - 滑动过程中,
mScrollX
的值总是等于 View 左边缘和 View 内容左边缘在水平方向的距离;mScrollY
的值总是等于 View 上边缘和 View 内容上边缘在竖直方向的距离
- 滑动过程中,View 内部的两个属性
mScrollX
和mScrollY
的单位为像素- 当 View 左边缘在 View 内容左边缘的右边时,
mScrollX
为正值,反之为负值。即,内容往左移动mScrollX
则为正 - 当 View 上边缘在 View 内容上边缘的下边时,
mScrollY
为正值,反之为负值。即,内容往上移动mScrollY
则为正 - 方法
scrollTo()
参数与滑动方向相反的原因可以通过源码查到:invalidate(true) -> draw(canvas) -> onDrawScrollBars(canvas) -> invalidate(left, top, right, bottom) -> 倒数第 5 行:tmpr.set(l-scrollX, t-scrollY, r-scrollX, b-scrollY);
- 当 View 左边缘在 View 内容左边缘的右边时,
scrollTo()
和scrollBy()
只能改变 View 内容的位置而不能改变 View 在布局中的位置mScrollX
和mScrollY
的变换规律示意图
2. 使用动画实现滑动
通过动画能够让一个 View 进行平移,平移就是一种滑动
- 使用动画来移动 View,主要是操作 View 的
translationX
和translationY
属性 - 既可以采用传统的 View 动画,也可以采用属性动画(为了兼容 3.0 以下版本,需要采用开源动画库 nineoldandroids)
- 使用动画来移动 View,主要是操作 View 的
Demo:
View 动画:在 100 ms 内将一个 View 从原始位置向右下角移动 100 个像素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true"
android:zAdjustment="normal" >
<translate
android:duration="100"
android:fromXDelta="0"
android:fromYDelta="0"
android:interpolator="@android:anim/linear_interpolator"
android:toXDelta="100"
android:toYDelta="100" />
</set>- View 动画是对 View 的影像做操作,它并不能真正的改变 View 的位置参数,包括宽高
- 如果希望动画后的状态得以保留必须将 fillAfter 属性设置为 true,否则动画完成后其动画结果会消失会瞬间恢复到动画前的状态
属性动画:将一个 View 在 100 ms 内从原始位置向右平移 100 像素
1
ObjectAnimator.ofFloat(targetView, "translationX", 0, 100).setDuration(100).start();
- 属性动画不存在 View 动画的 fillAfter 问题
- 尽管如此,在 Android 3.0 以下的手机上通过 nineoldandroids 来实现的属性动画本质上仍然是 View 动画
3. 改变布局参数实现滑动
View 的布局参数即
LayoutParams
,改变LayoutParams
实现 View 滑动是一种很灵活的方法Demo: 重新设置一个 View 的
LayoutParams
1
2
3
4MarginLayoutParams params = (MarginLayoutParams) mButton.getLayoutParams();
params.width += 100;
params.leftMargin += 100;
mButton.requestLayout(); // 或者 mButton.setLayoutParams(params);还可以在目标 View 的周围放置空 View,通过改变空 View 的大小使目标 View 滑动(不推荐)
4. 三种 View 滑动方式的对比
简单总结
scrollTo()/scrollBy()
: 操作简单,适合对 View 内容的滑动- 动画:操作简单,适用于没有交互的 View 和实现复杂的动画效果
- 改变布局参数:操作稍微复杂但灵活,适用于有交互的 View
Demo: 实现自定义 View 跟随手指全屏滑动的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getRawX();
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
int deltaX = x - mLastX;
int deltaY = y - mLastY;
Log.d(TAG, "move, deltaX: " + deltaX + " deltaY: " + deltaY);
int translationX = (int) ViewHelper.getTranslationX(this) + deltaX;
int translationY = (int) ViewHelper.getTranslationY(this) + deltaY;
ViewHelper.setTranslationX(this, translationX);
ViewHelper.setTranslationY(this, translationY);
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
mLastX = x;
mLastY = y;
return true;
}