之前在面试一家大厂时问过我这个问题 :移动一个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
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)
}
}
}
一开始,红色快为一个ViewGroup,绿色块为TextView,所以通过 ViewGroup.scroll() 移动的是 TextView(绿色)
这个原始的 坐标(0,0),后面的一些列通过 ScrollTo 或 ScrollBy移动内容,都是这个点为基准的。
点击 -- 调用方法 ScrollTo(-100,-100) 看下效果图
看到这,估计有人会有问题,不是负数就是往xy坐标反方向吗,
(-100,-100)按道理,应该是放 左上方去移动的,怎么会是右下方呢?
先别急,我后面会解释,反正先记住,传进去的参数是和坐标方向相反的
点击 -- 调用方法 ScrollTo(100,100) 看下效果图
很明显,整个TextView是往左上去移动,此时左上角的坐标为(-100,-100)
注意,草绿色那一块我是为了显示超出父布局而补上去的,事实上是不存在的。
仅仅是想给出内容超出父布局的那一部分而已。
点击 -- 调用方法 ScrollTo(0,0) 看下效果图
很明显,和第一张效果图是一样的。
到这里,ScrollTo(x,y) 基本上演示完。
下面说ScrollBy(x,y)
看下其源码:
简单明了。
都是把原来的偏移量 mScrollX 和 mScrollY 加上,然后再调用 ScrollTo()
所以:
1> ScrollBy是一个基于当前位置(mScrollX,mScrollY)的相对移动。
2> ScrollTo是一个基于原始位置坐标(0,0)的绝对移动。
先恢复到原始位置,然后点击 -- 调用方法 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> 通过动画
上面代码亲测没问题,如有问题请留言指正,谢谢!