提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、View与ViewGroup
- 1.View
- 2.ViewGroup
- 二、坐标系
- 1.Android坐标系
- 2.View坐标系
- 三、View滑动
- 1.layout
- 2.offsetLeftAndRight()与offsetTopandBottom()方法
- 3. LayoutParams 改变布局参数的方式
- 4.通过动画的方式:新建 xml类型的文件,创建好文件后,在MainActivity中引用
- 5.scrollTo 和scrollBy
- 6.Scroller
- 总结
前言
本篇内容包括介绍View体系,坐标系以及View的滑动方式。本篇内容为Android进阶之光的个人学习总结,博主利用Kotlin对书本中的教学进行复构。
一、View与ViewGroup
1.View
我们知道Android有许多原生控件,比如最常用的TextView和ImageView,这些控件都和View有关。有的控件不直接继承View,但是间接继承View。这些控件我们点进源码会更好的理解。
如图TextView继承自View,而Button继承自TextView,从而间接实现继承View。发现了吗?常用的控件都是继承View的。
2.ViewGroup
如图,我们常用的布局如LinearLayout,它继承自ViewGroup。我们将控件写在布局中,布局类似一个大箩筐或者说是容器,里面放入我们需要的控件。那么布局的父类ViewGroup也类似一个大箩筐,这个大箩筐比较特殊,这个ViewGroup大箩筐中只装两种东西。一个是VIew,一个是ViewGroup。 便于你的理解,我用面向对象的思想以生活中的事物类比一下。我们将ViewGroup比作你上学时期背的书包,书包中包含课本(View)不能做容器的里面装不了东西的,和笔盒(ViewGroup)。
View树如下。
二、坐标系
1.Android坐标系
以屏幕的左上角为原点,向右为X轴正方向,向下为Y轴正方向。在触控事件中,通过**getRawX
方法和getRawY
**方法获得的坐标系也是Android坐标系。
2.View坐标系
如图,View的宽度 width = getRight() - getLeft()
求高与其原理相同。但是Android其实为我们提供了其封装方法 getHeight()
和 getWidth()
内部原理就是上面的方法。
假设蓝色点为我们第一次触摸屏幕的点。View与ViewGroup的的点击事件都会由onTouchEvent(event: MotionEvent)
来处理。
getX() //获取点击事件距离控件左边的距离,即视图坐标
getY() //获取点击事件距离控件**顶边**的距离,即视图坐标
getRawX() //获取点击事件距离整个屏幕左边的距离,即绝对坐标
getRawY() //获取点击事件距离整个屏幕**顶边**的距离,即绝对坐标
上方注意一下,涉及到Y的,都是获得与顶边的距离。
三、View滑动
View滑动这里,主要介绍6种滑动方法。所有例子来源于Android进阶之光,没有学习过的朋友可以过去学习看看。
方法分别是:layout方法、offsetLeftAndRight()
与offsetTopandBottom()
方法、LayoutParams、动画、scrollTo和scrollBy,以及Scroller。
还记得我们刚才说了View的点击实践都在OnTouchEvent(event: MotionEvent)处理吗?
我们新建一个CustomView类,并在onTouchEvent()方法中获取触摸点的坐标。
Kotlin版本的代码如下:
class CustomView @JvmOverloads
constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
val mScroller = Scroller(context)
//初始化触摸屏幕瞬间的坐标
var lastX = 0
var lastY = 0
override fun onTouchEvent(event: MotionEvent) : Boolean{
//记录随着点击移动 变化的 x y坐标
val x = event.x.toInt()
val y = event.y.toInt()
when(event.action){
MotionEvent.ACTION_DOWN -> {
lastX = x
lastY = y
}
MotionEvent.ACTION_MOVE -> {
//计算偏移量
val offsetX = x - lastX
val offsetY = y - lastY
//1.调用layout方法重新绘制界面UI
//layout(left + offsetX, top + offsetY, right + offsetX, bottom + offsetY)
//2.调用ofsetLeftAndRight和offsetTopAndBottom
// offsetLeftAndRight(offsetX)
// offsetTopAndBottom(offsetY)
//3.LayoutParams 改变布局参数方式
// var layoutParams: LinearLayout.LayoutParams = layoutParams as LinearLayout.LayoutParams
// layoutParams.leftMargin = left + offsetX
// layoutParams.topMargin = top + offsetY
// setLayoutParams(layoutParams)
//4.动画 新建 xml类型的文件,创建好文件后 在MainActivity中引用
//5.scroolTo 和scrollBy
// (parent as View).scrollBy(-offsetX,-offsetY)
//6.Scroller
}
}
return true
}
}
这里对Kotlin语言不太熟悉的伙伴,可以参考第一行代码第三版。这里相对于Java的继承,需要熟悉一下Kotlin的继承写法,以及主从构造函数相关知识。
1.layout
我们可以通过layout方法,为其传入left、top、right、bottom参数完成View的绘制。
layout(left + offsetX, top + offsetY, right + offsetX, bottom + offsetY)
2.offsetLeftAndRight()与offsetTopandBottom()方法
这个方法传入偏移的参数。
offsetLeftAndRight(offsetX)
offsetTopAndBottom(offsetY)
3. LayoutParams 改变布局参数的方式
LayoutParams 主要保存了一个View的布局参数,这里我们可以首先获得View的布局参数,再通过setLayoutParams()方法改变布局。
var layoutParams: LinearLayout.LayoutParams = layoutParams as LinearLayout.LayoutParams
layoutParams.leftMargin = left + offsetX
layoutParams.topMargin = top + offsetY
setLayoutParams(layoutParams)
4.通过动画的方式:新建 xml类型的文件,创建好文件后,在MainActivity中引用
MainActivity中引用
//4.动画 新建 xml类型的文件
binding.customview.animation = AnimationUtils.loadAnimation(this,R.anim.translate)
5.scrollTo 和scrollBy
scrollTo()方法表示具体移到那个坐标位置,而scrollBy()表示移动多少距离。
这个是瞬间完成没有过度动画,不是很好看。除非你有特殊的要求。比如打地鼠?
(parent as View).scrollBy(-offsetX,-offsetY)
这里你可以看到,这里面传入的参数为负数,为什么传入参数偏移为负的呢?
还记得初中学习过的物理知识吗?相对运动!假设A的上方放了一个B,我现在向左移动B,以B为参照物,请问A是向哪里运动的呢? 答案是向右的!那么我们把A当做我们想要改变的View,把B当做我们的屏幕。
6.Scroller
相比上个方法,这个方法的交互效果就要好很多了。但是它的内部也是通过不断地去scrollTo实现的。感兴趣的小伙伴可以查看源码。
首先我们要在类中初始化Scroller:val mScroller = Scroller(context)
并重写他的computeScroll方法。
override fun computeScroll() {
super.computeScroll()
if (mScroller.computeScrollOffset()){
(parent as View).scrollTo(mScroller.currX,mScroller.currY)
invalidate()
}
}
我们写一个用来调用Scroller的startScroll的封装
fun smoothScrollTo(desX: Int, desY: Int){
val scrollX = scrollX
val scrollY = scrollY
val delta = desX - scrollX
mScroller.startScroll(scrollX,0,delta,0,2000);
//invalidate()不断的重新绘制视图,这样就会重新调用computeScroll方法
invalidate()
}
然后我们可以在MainActivity中调用 binding.customview.smoothScrollTo(-400,0)
这里设置让view向右移动400像素。