val viewLocation = IntArray(2)
moveToView.getLocationOnScreen(viewLocation)
//坐标相减得到要滚动的距离
val moveViewY = viewLocation[1] - parentLocation[1]
//加上偏移坐标量,得到最终要滚动的距离
val needScrollY = (moveViewY - offset)
//如果是0,那就没必要滚动了,说明坐标已经重合了
if (moveViewY == 0) return
smoothScrollBy(0, needScrollY)
}
这里的offset
参数是滚动的额外偏移量。来保证滚动的时候预留一些额外空间。
//滚动到第一个View
fun scrollView1(view: View) {
viewBinding.scrollView.scrollToView(R.id.demo_view1)
}
//滚动到第二个View 上方偏移50像素
fun scrollView2Offset(view: View) {
viewBinding.scrollView.scrollToView(R.id.demo_view2,50)
}
现在已经可以滚动到指定的View位置了。接下来就是比较难的了。
锚点变化位置处理
现在只是能够滚动到指定的View了,但是这并不能完全满足业务需求。在UI上是要有一个Indicator指示器的,来指示当前已经滚动到哪个位置。
所以我们先增加一个集合,来保存滚动的锚点View。
val registerViews = mutableListOf()
并增加方法添加Views
fun addScrollView(vararg viewIds: Int) {
val views = Array(viewIds.size) { index ->
val view = findViewById(viewIds[index])
if (view == null) {
val missingId = rootView.resources.getResourceName(viewIds[index])
throw NoSuchElementException(“没有找到这个ViewId相关的View $missingId”)
}
view
}
registerViews.clear()
registerViews.addAll(views)
}
分析: 我们已经有了需要定位,需要监听变化的Views,当ScrollView滚动的时候,我们可以通过
OnScrollChangeListener
监听滚动,并获取注册的锚点View的位置改变信息。在onScrollChange
中计算滚动偏移和滚动到哪个View。
在注册OnScrollChangeListener
的时候我们也要保留外部的监听器使用。
init {
//调用父类的 不调用自身重写的
super.setOnScrollChangeListener(this)
}
//重写并保留外部的对象
override fun setOnScrollChangeListener(userListener: OnScrollChangeListener?) {
mUserListener = userListener
}
override fun onScrollChange(
v: NestedScrollView?,
scrollX: Int,
scrollY: Int,
oldScrollX: Int,
oldScrollY: Int
) {
//用户回调
mUserListener?.onScrollChange(v, scrollX, scrollY, oldScrollX, oldScrollY)
//计算逻辑
computeView()
}
我们接下来的所有操作都将会在computeView()
这个方法中进行
我们先封装一个数据体用于保存Vie