Android 分析ScrollTo 和 ScrollBy

之前在面试一家大厂时问过我这个问题 :移动一个View,有几种办法,ScrollTo 和 ScrollBy区别是什么?

我特么没说出后者来。。。。擦,,,,,,遂研究学习下。。。。

首先,看下 ScrollTo 的源码,它是一个 view层的方法

翻译一下源码注释:

设置你的View的滑动位置,这会调用  onScrollChanged() 方法,然后你的View会被重新绘制

x : 滑动到X

y : 滑动到Y

注意:这个方法里,每次都记录下,当前的偏移量  mScrollX 和 mScrollY 。

然后: mScrollX 和 mScrollY 呢? 

很清晰,mScrollX 和 mScrollY  是View 内容 的偏移量,单位为像素。

而且每一次 调用ScrollTo () 移动,都会 保存当前 XY轴的偏移量。

划重点!!!           是   内容     的偏移量 

也就是用这个 ScrollTo(),并不会改变View本身的位置,只会改变View  内容  的位置

什么是内容呢?比如:TextView 里面的 text 就是内容,一个 ViewGroup  里面放了一个Button ,Button就是内容。

再比如我下面的例子:

ConstraintLayout 里面 嵌套一个 TextView ,通过 ConstraintLayout.ScrollTo()  即可移动 TextView 

在这之前,我先说一下,View的XY轴坐标是怎么定义的,如下图。

1>  左上角  为 (0,0)

2>  水平往右为正 反之为负。

3> 竖直往下为正,反之为负。

废话少说,先看Demo:

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".ui.MainActivity">

    <TextView
            android:id="@+id/button_down"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="往右下"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            android:background="@color/color_00a3f3"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@id/button_up"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintHorizontal_chainStyle="spread"/>

    <TextView
            android:id="@+id/button_up"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/color_00a3f3"
            android:text="往左上"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            app:layout_constraintLeft_toRightOf="@id/button_down"
            app:layout_constraintRight_toLeftOf="@id/button_reset"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_chainStyle="spread"/>

    <TextView
            android:id="@+id/button_reset"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/color_00a3f3"
            android:text="回到原始位置"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            app:layout_constraintLeft_toRightOf="@id/button_up"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toLeftOf="@id/button_by_down"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintHorizontal_chainStyle="spread"/>

    <TextView
            android:id="@+id/button_by_down"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/color_00a3f3"
            android:text="在现在位置基础上叠加右下"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            app:layout_constraintLeft_toRightOf="@id/button_reset"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintHorizontal_chainStyle="spread"/>

    <android.support.constraint.ConstraintLayout
            android:id="@+id/cl_contain"
            android:layout_width="0dp"
            android:layout_height="200dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:background="@color/colorAccent">
        <TextView
                android:id="@+id/scroll"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                android:background="@color/colorPrimary"
                android:padding="80dp"
                android:textSize="18sp"
                android:textColor="@color/color_239efe"
                android:gravity="center"
                android:text="scroll"/>

    </android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
MainActivity
package com.leo.dicaprio.myutilapp.ui

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.leo.dicaprio.myutilapp.R
import com.leo.dicaprio.myutilapp.thred.TestThread
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private var move: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button_down.setOnClickListener {
            cl_contain.scrollTo(-100, -100)
        }
        button_up.setOnClickListener {
            cl_contain.scrollTo(100, 100)
        }
        button_reset.setOnClickListener {
            cl_contain.scrollTo(0, 0)
        }
        button_by_down.setOnClickListener {
            cl_contain.scrollBy(-50, -50)
        }
    }
}

原始位置1

一开始,红色快为一个ViewGroup,绿色块为TextView,所以通过 ViewGroup.scroll()  移动的是 TextView(绿色)

这个原始的 坐标(0,0),后面的一些列通过 ScrollTo 或 ScrollBy移动内容,都是这个点为基准的。

往右下移动 ScrollTo(-100,-100)

点击 -- 调用方法 ScrollTo(-100,-100) 看下效果图

看到这,估计有人会有问题,不是负数就是往xy坐标反方向吗,

(-100,-100)按道理,应该是放  左上方去移动的,怎么会是右下方呢?

先别急,我后面会解释,反正先记住,传进去的参数是和坐标方向相反的

往左上方移动 ScrollTo(100,100)

点击 -- 调用方法 ScrollTo(100,100) 看下效果图

很明显,整个TextView是往左上去移动,此时左上角的坐标为(-100,-100)

注意,草绿色那一块我是为了显示超出父布局而补上去的,事实上是不存在的。

仅仅是想给出内容超出父布局的那一部分而已。

回到初始位置 ScrollTo(0,0)

点击 -- 调用方法 ScrollTo(0,0) 看下效果图

很明显,和第一张效果图是一样的。

 

到这里,ScrollTo(x,y)  基本上演示完。

下面说ScrollBy(x,y) 

看下其源码:

简单明了。

都是把原来的偏移量  mScrollX 和 mScrollY  加上,然后再调用 ScrollTo()

所以:

1> ScrollBy是一个基于当前位置(mScrollX,mScrollY)的相对移动。

2> ScrollTo是一个基于原始位置坐标(0,0)的绝对移动。

 

多次 调用ScrollBy(-50,-50)

先恢复到原始位置,然后点击 -- 调用方法 ScrollTo(-50,-50) 看下效果图   往右下 移动了 50像素 第1次

点击 -- 调用方法 ScrollTo(-50,-50) 看下效果图    第2次     在上图基础上 再往右下 移动了 50像素

第3次....

第4次....

第5次....

第6次....

.......

知道第8次      

所以  ScrollBy  是在 现在位置基础上去偏移,ScrollTo是基于原始位置的偏移。

为什么传进去的参数  和坐标轴方向相反的????

其实通过 onScrollChanged(去重新绘制)到最终的 到 invalidate()这个方法里

有这么这样的一个方法:   tmpr.set(l - scrollX, t - scrollY, r - scrollX, b - scrollY)

绘制的目标指定位置是减去  你的偏移量。

所以和XY轴的方向是相反的。

具体跟踪的源码就不贴上来了,基本上记得这个理论即可。

最后说一下最开始的问题:

问题1:移动一个View,有几种办法?

1>   通过 ScrollTo  或 ScrollBy

2>   通过LayouParams  设置与父布局的间距

3>   通过动画

上面代码亲测没问题,如有问题请留言指正,谢谢!

猜你喜欢

转载自blog.csdn.net/Leo_Liang_jie/article/details/90717191
今日推荐