Android View的scrollTo()、scrollBy() 和Scroller类总结

Android View的scrollTo()、scrollBy() 和Scroller类总结

  1. ViewscrollTo()scrollBy()使用介绍
  2. scrollTo()scrollBy()实践,自定义滑动效果
  3. Scroller滑动辅助类,startScroll()computeScrollOffset()使用介绍
  4. Scroller实践,使用示例
  5. 总结

一、View的scrollTo()、scrollBy()

1.1 scrollTo scrollBy 使用说明

  • scrollTo()scrollBy()方法是View中的,因此任何的View都可以通过这两种方法进行移动。首先要明白的是,scrollTo、scrollBy滑动的是View中的内容(而且还是整体滑动),而不是View本身。我们的滑动控件如SrollView可以限定宽、高大小,以及在布局中的位置,但是滑动控件中的内容(或者里面的childView)可以是无限长、宽的,我们调用View的scrollTo、scrollBy方法,相当于是移动滑动控件中的画布Canvas,然后进行重绘,屏幕上也就显示相应的内容。

1.2 scrollTo使用介绍

  • 调用scrollTo(100,0)表示将View中的内容移动到距离内容初始显示位置的x=100,y=0的地方,效果如下图:Android View的scrollTo()、scrollBy() 和Scroller类总结_第1张图片
  • 调用scrollTo(0,100)效果如下图:Android View的scrollTo()、scrollBy() 和Scroller类总结_第2张图片
  • 调用scrollTo(100,100)效果如下图:
    Android View的scrollTo()、scrollBy() 和Scroller类总结_第3张图片

1.3 scrollBy使用介绍

  • 调用scrollBy(0,100)表示将View中的内容移动到原内容基础上x=x + 0,y=y + 100的地方,所以scrollBy是在原来的基础上移动的,效果如下图:Android View的scrollTo()、scrollBy() 和Scroller类总结_第4张图片
  • scrollBy()的源码中其实也非常简单调用的就是scrollTo(),只不过是加上了当前的mScrollX或者 mScrollY
	public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }

二、scrollTo和scrollBy实践,自定义滑动效果

  • 通过上面几个图,可以清楚看到scrollTo和scrollBy的作用和滑动坐标系的关系。在实际使用中,我们一般是在onTouchEvent()方法中处理滑动事件,在MotionEvent.ACTION_MOVE时调用scrollTo(int x,int y)进行滑动,在调用scrollTo(int x,int y)前,我们先要计算出两个参数值,即水平和垂直方向需要滑动的距离,如下:
public class ScrollActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scroll);
    }

    private int mLastX;
    private int mLastY;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int y = (int) event.getY();
        int x = (int) event.getX();
        int action = event.getAction();
        View decorView = getWindow().getDecorView();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastY = y;
                mLastX = x;
                break;
            case MotionEvent.ACTION_MOVE:
                int dy = mLastY - y;//本次手势滑动了多大距离
                int dx = mLastX - x;
                int oldScrollY = decorView.getScrollY();//先计算之前已经偏移了多少距离
                int oldScrollX = decorView.getScrollX();
                int scrollY = oldScrollY + dy;//本次需要偏移的距离=之前已经偏移的距离+本次手势滑动了多大距离
                int scrollX = oldScrollX + dx;
                decorView.scrollTo(scrollX, scrollY);
                mLastY = y;
                mLastX = x;
                break;
        }
        return true;
    }
}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorPrimary"
    android:gravity="center">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:padding="10dp"
        android:text="scroll..."
        android:textSize="24sp"
        android:textStyle="bold" />

LinearLayout>
  • 效果图如下:

三、Scroller滑动辅助类

3.1 Scroller使用说明

  • 根据我们上面的分析,可知View的scrollTo()scrollBy()是瞬间完成的,当我们的手指在屏幕上移动时,内容会跟着手指滑动,但是当我们手指一抬起时,滑动就会停止,如果我们想要有一种惯性的滚动过程效果和回弹效果,此时就需要使用Scroller辅助类。
    但是注意的是,Scroller本身不会去移动View,它只是一个移动计算辅助类,用于跟踪控件滑动的轨迹,只相当于一个滚动轨迹记录工具,最终还是通过ViewscrollToscrollBy方法完成View的移动的。
    在使用Scroller类之前,先了解其重要的两个方法:

3.2 startScroll使用介绍

  • startScroll(),开始一个动画控制,由(startX , startY)duration时间内前进(dx,dy)个单位,即到达偏移坐标为(startX+dx , startY+dy)处。
	public void startScroll(int startX, int startY, int dx, int dy) {
        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
    }

3.3 computeScrollOffset使用介绍

  • computeScrollOffset(),滑动过程中,根据当前已经消逝的时间计算当前偏移的坐标点,保存在mCurrXmCurrY值中。
	public boolean computeScrollOffset() {
        if (mFinished) {
            return false;
        }
        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
        if (timePassed < mDuration) {
            ...
        }
        else {
            mCurrX = mFinalX;
            mCurrY = mFinalY;
            mFinished = true;
        }
        return true;
    }

3.4 Scroller使用总结

  • Scroller类中最重要的两个方法就是startScroll()computeScrollOffset(),但是Scroller类只是一个滑动计算辅助类,它的 startScroll()computeScrollOffset()方法中也只是对一些轨迹参数进行设置和计算,真正需要进行滑动还是得通过ViewscrollTo()scrollBy()方法 。为此,View中提供了computeScroll()方法来控制这个滑动流程。computeScroll()方法会在绘制子视图的时候进行调用。其源码如下:是一个空方法。
public void computeScroll() {}
  • 因此Scroller类的基本使用流程可以总结如下:
  1. 首先通过Scroller类的startScroll()开始一个滑动动画控制,里面进行了一些轨迹参数的设置和计算;
  2. 在调用startScroll()的后面调用invalidate();引起视图的重绘操作,从而触发ViewGroup中的computeScroll()被调用;
  3. computeScroll()方法中,先调用Scroller类中的computeScrollOffset()方法,里面根据当前消耗时间进行轨迹坐标的计算,然后取得计算出的当前滑动的偏移坐标,调用ViewscrollTo()方法进行滑动控制,最后也需要调用invalidate();进行重绘。
    如下的一个简单代码示例:

3.5 Scroller使用示例

public class ScrollLinearLayout extends LinearLayout {

    public ScrollLinearLayout(Context context) {
        super(context);
        init();
    }

    public ScrollLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ScrollLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private int mLastX;
    private int mLastY;
    private Scroller mScroller;

    private void init() {
        mScroller = new Scroller(getContext());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int y = (int) event.getY();
        int x = (int) event.getX();
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                mLastY = y;
                mLastX = x;
                break;
            case MotionEvent.ACTION_MOVE:
                int dy = mLastY - y;//本次手势滑动了多大距离
                int dx = mLastX - x;
                mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy, 0);//第一步
                invalidate();
                mLastY = y;
                mLastX = x;
                break;
            default:
        }
        return true;
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {//第二步
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());//第三步
            invalidate();
        }
    }
}
public class ScrollActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scroll);
    }

}

<com.example.mtest.ScrollLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorPrimary"
    android:gravity="center">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:padding="10dp"
        android:text="scroll..."
        android:textSize="24sp"
        android:textStyle="bold" />

com.example.mtest.ScrollLinearLayout>

Android View的scrollTo()、scrollBy() 和Scroller类总结_第5张图片

四、总结

  • Scroller类能够帮助我们实现高级的滑动功能,如手指抬起后的惯性滑动功能。使用流程为,首先通过Scroller类的startScroll()+invalidate()触发ViewcomputeScroll(),在computeScroll()中让Scroller类去计算最新的坐标信息,拿到最新的坐标偏移信息后还是要调用ViewscrollTo来实现滑动。可以看到,使用Scroller的整个流程比较简单,关键的是控制滑动的一些逻辑计算。
  • Android后面推出了OverScroller类,OverScroller在整体功能上和Scroller类似,使用也相同。OverScroller类可以完全代替Scroller,相比ScrollerOverScroller主要是增加了对滑动到边界的一些控制,如增加一些回弹效果等,功能更加强大。

本文参考:
Android Scroller详解
Android源码角度分析View的scrollBy()和scrollTo()的参数正负问题
ListView滑动删除实现之二——scrollTo、scrollBy详解

你可能感兴趣的:(Android知识点总结,android)