Android渲染系统中的View
对于初级开发者而言,在面试中,经常会和面试官聊到Android四大组件,聊到Activity,其中回答最纯熟的一句话就是:
Activity的作用是应用与用户之间交互的重要组件
那么Activity
之交互功能又是怎么实现的呢?答案不言而喻-View
我们在Activity
onCreate
的时候调用setContentView
方法为Activity
设置布局,实际上就相当于对用户开放了多个交互接口,用户可以通过布局中一个一个的View
进行自己想要的操作,我们也可以通过View
来向用户展示结果信息,那么View
在Android渲染系统中长啥样呢?
首先我们打开系统的开发者选项( 通过多次点击 系统设置 > 关于本机中的版本号开启),找到显示布局边界选项并打开(如下图所示),
打开后,我们可以看到界面上多了很多不同颜色的线框以及矩形蒙版,这些线框圈中的部分就代表着一个View
或者ViewGroup
,阴影部分显示的是距父布局的Margin
或者Padding
(如下图所示)。
从上面两张图我们可以看到Android
渲染系统中以点阵为基本元素,用一个矩形区域来渲染View
,同时在这个矩形区域内响应用户交互,从Android
渲染系统角度来看,View就是点阵里面的一块矩形区域。
Android源码中的View
不论是Java
还是Kotlin
,其语言特性就是面向对象,那么View
作为Android
体系中的一部分,毫无疑问,它是一个对象,那么这个对象有什么特性呢?我们来看下官方源码中的说明:
This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling. View is the base class for widgets, which are used to create interactive UI components (buttons, text fields, etc.). The
{@link android.view.ViewGroup} subclass is the base class for layouts, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties.
上面这句话主要阐述了如下几个要点:
View
的形状以及作用:View
是屏幕上用于进行数据渲染展示和处理用户事件的矩形区域,这一点我们通过第一部分渲染系统已充分认识到。View
的顶级父类特性:View
是所有组件及Layout
的直接或间接父类,这一点我们可以通过绘制各组件类继承关系认识到(如下图)。
看了源码中关于
View
的定义,同学们是否对View
认识更加清晰了呢?
同学们都知道,Android
中有两种布局管理方式,通过Java
代码或者xml
布局文件(两者经常组合使用,这里我们不单独列出),我们来看下一个典型的java
代码控制布局的例子,代码如下图:
这里我们通过new
关键词生成了TextView
对象,并将其设置在Activity
的布局上,使用xml
布局方式同样能打开上图右侧运行效果,但是TextView
对象又是什么时候生成的呢?这就涉及到Android
中View
的管理-View树,了解View
树的创建过程,有利于我们更好的理解Android
中的View
管理机制,后续文章或小册中我们将做更详细的阐述。
Android系统坐标系及View坐标系
View
作为屏幕上一块用于用户交互和数据展示的矩形区域,那么这块矩形区域的渲染分工是怎么的呢?又是怎么确定这块矩形区域绘制在哪里呢?联想数学中,我们在绘制图形之前,首先会确立什么?没错,坐标系。在Android
中也有坐标系的概念,与View
相关的主要有Android
屏幕坐标系和View
坐标系,Android
屏幕坐标系用于系统确认该在什么位置绘制多大的矩形区域以展示View
,View
坐标系用于View
内部进行自我绘制时确认绘制方向和大小。
Android
屏幕坐标系如下图所示,以屏幕左上角为坐标原点,向右为X
正方向,向下为Y
正方向。
View
坐标系与Android
屏幕坐标系类似,如下图所示,只不过其坐标原点位于View
所在矩形区域的左上角,向右为X
正向,向下为Y
正向。
从上图中我们可以看出,所有的View
及Layout
内部的坐标系均是View
坐标系,同时也可以看到getTop()
,getLeft()
,getRight()
以及getBottom()
这四个函数具体所对应的距离含义。当我们在View内部进行测量绘制时,经常需要用到这四个函数,通过这四个函数我们可以手动计算该View
在界面上矩形区域的宽度和高度(注意这里描述的是界面上矩形区域,即View
的用户可见部分,在View
内部是没有边界可言的,理论上讲,一个View
的内部可以无限绘制)。
可能有同学要问了,你讲了View
坐标系,但是我需要知道View
在屏幕坐标系中的位置怎么搞呢?也很简单,我们可以通过View
的getBoundsOnScreen(Rect outRect)
获取View
在屏幕上的矩形区域,那么View
在屏幕坐标系中的位置就显而易见了。
也许有同学见过重写onTouchListener
,自己拦截事件的操作,这种情况下我们就需要获取当前触摸点的位置以用于判断是否进行业务逻辑处理,具体说明如下图:
从上图我们可以看出getRawX()
,getRawY()
是触摸点距离屏幕坐标系坐标原点的距离,getX()
,getY()
是触摸点距离View
坐标系坐标原点的距离,通常情况下,如果是View
整体的触屏事件处理,例如拖动View
,点击View
,我们选用getRawX()
和getRawY()
,如果是View
内部的事件处理,例如进行View
***内容***的滚动,我们选用getX()
和getY()
。