目前View滑动的实现方式主要有三种:
1.通过View自身提供的ScrollTo和ScrollBy。
2.通过动画来使View平移来实滑动。
3.第三种是通过改变View的LayoutParams,使View重新布局实现滑动。
这几天在看View的滑动,虽然ScrollTo和ScrollBy在实际的开发过程中用到的很少,特别是属性动画的广泛运用后,既然存在,必有价值。
所以在这里将ScrollTo和ScrollBy简单分析一下:
先看一下ScrollTo的源码:
/**
* 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();
}
}
}
再看看ScrollBy的源码:
/**
* 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呢,他们的区别就在于ScrollBy方法中调用ScrollTo时将传递的参数加上了当前 View内容到View边缘位置的距离 (mScrollX、mScrollY)。
关于mScrollTo和mScrollBy这里我是这样理解的:
mScrollX 是View左边缘与View内容左边缘的水平方向上的距离,理解为View左边缘X坐标减去View内容左边缘X坐标。
mScrollY 是View顶部与View内容上边缘的在竖直方向上的距离,理解为View顶部Y坐标减去View内容顶部Y坐标。
我们用两幅图来表示:绿色部分为View的内容,框就是View本身。
这里大概能将mScrollX和mScrollY的取值表述清楚了。
下面用一个Demo演示一下ScrollTo和ScrollBy的区别:
Activity:
package com.example.xuzhenhao.myapplication.ui;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.example.xuzhenhao.myapplication.R;
public class TestActivity extends AppCompatActivity {
private RelativeLayout rl;
private TextView tvTo;
private TextView tvBy;
private int x = -5;
private int y = -5;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initView();
}
/**
* 初始化控件
*/
private void initView() {
rl = (RelativeLayout) findViewById(R.id.scroll_rl);
tvTo = (TextView) findViewById(R.id.scroll_tv);
tvTo.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
rl.scrollTo(x,y);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return false;
}
});
tvBy = (TextView) findViewById(R.id.scroll_by_tv);
tvBy.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action){
case MotionEvent.ACTION_DOWN:
rl.scrollBy(x,y);
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return false;
}
});
}
}
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scroll_rl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.xuzhenhao.myapplication.ui.TestActivity">
<TextView
android:id="@+id/scroll_to_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/dark"
android:textSize="16sp"
android:text="ScrollTo"/>
<TextView
android:id="@+id/scroll_by_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/scroll_to_tv"
android:textColor="@color/dark"
android:textSize="16sp"
android:text="ScrollBy"/>
</RelativeLayout>
运行起来如下图所示:
我们发现ScrollTo只能移动一次,再次点击就不能移动了,而点击ScrollBy每次都能移动,玄机在哪里??刚刚放出的源码里面已经写的很清楚了。
ScrollTo第一次传参时将传入的形参分别赋值给mScrollX和mScrollY,而每次都会去判断mScrollX和mScrollY与传入的形参是否相等,如果相等就不会去移动。
ScrollBy方法内部调用ScrollTo,在调用ScrollTo时传入的参数为当前View内容的左边缘和顶部到View的左边缘和顶部的距离加上传入的形参,就相当于每次都实现了基于当前位置的相对滑动
注意:
ScroolTo和ScrollBy都只能移动View的内容,View本身是不会移动的,上图中两个View之所以跑了是因为我们是使用最外层布局RelativeLayout的ScrollTo和ScrollBy
感谢!
在此任玉刚的《Android开发艺术探索》第3章第二节View的滑动