【Android 控件架构】详解Android控件架构与常用坐标系

前言

View在Android的世界中扮演着重要的角色,正是这些控件组成了一个又一个精美的App。View体系是Android界面编程的核心,虽然它不属于四大组件但是它的重要行却毫不逊色,这个系列我会陆续从View的滑动事件、View 的事件反馈、自定义View等多个方面逐步介绍Android View体系。如果能帮助到你,那是我莫大的荣幸。

Android控件框架

在Android的世界中View是所有控件的基类(祖宗),其中也包括ViewGroup在内。View是一个抽象的概念,特指界面中的某一个控件。而ViewGroup是代表着控件的集合,其中可以包含多个View控件,并管理他们。从某种角度上来讲Android中的控件可以分为两大类:View与ViewGroup。通过ViewGroup,整个界面的控件形成了一个树形结构,这也就是我们常说的控件树,上层的控件要负责测量与绘制下层的控件,并传递交互事件。我们在开发中常常使用到的findViewById()方法,就是在控件树中进行深度遍历来查找对应元素的。在每棵控件树的顶部都存在着一个ViewParent对象,它是整棵控件树的核心所在,所有的交互管理事件都由它来统一调度和分配,从而对整个视图进行整体控制。

View树结构图

在每一个Activity中都包含了一个Window,而这个Window通常上是由PhoneWindow实现的,而PhoneWindow又将DecorView设置为整个界面的根布局,DecorView作为根布局将要显示的具体内容呈现在PhoneWindow上,并提供了一些通用方法来操作界面。这里所有View的交互事件都由WindowManagerService(WMS)进行接收,并通过Activity回调相应的onClickListener。
UI界面架构图

在上面的视图上我们可以看到此时屏幕被分成了两部分:TitleView与ContentView。如图红色的区域就是ContentView,contentView是一个ID为content的Framelayou这也是我们通过布局文件可以控制的区域,实际上我们所有的布局都设置在这样的Fragmelayout中。

这也就是为什么Activity、Fragment中设置根布局的方法叫做setContentView了。

插播: requestWindowFeature(Window.FEATURE_NO_TITLE) 与 setContentView() 调用顺序的关系

在设置setContentView()方法之前我们可以通过requestWindowFeature(Window.FEATURE_NO_TITLE)方法设置标签来显示全屏。如果你看了Activity源码中的setContentVeiw()方法你会发现,当setContentView()一旦调用,ContentView布局与TitleView会同时被加载,加载之后在调用requestWindowFeature(Window.FEATURE_NO_TITLE)方法设置标签已经没有作用了。所以只有在setContentView()方法之前设置标签才能剔除TitleView达到ContentView占据全屏的效果。(更详细的说明请参见【Android View源码分析(一)】setContentView加载视图机制深度分析)

视图树
当Acitivity的生命周期中,当onCreate()方法中调用setContentView方法后,ActivityManagerService(AMS)会调用onResume()方法,此时系统才会把整个DecorView添加到PhoneWindow中显示出来,至此界面回执完成。

贴一张图汇总一下吧
总结

更详细的说明请参见【Android View源码分析(一)】setContentView加载视图机制深度分析

Android的常用坐标系

在Android的世界中我们最常用到的就是Android坐标系和视图坐标系了。对于一个控件而言,它在Android坐标系中的位置我们可以称之为:绝对坐标系;而在视图坐标系中,指示的就是它的相对位置了。下面我们就来分析一下他们吧

1,Android坐标系

在Android的世界中,屏幕的左上角定点作为Android坐标系的原点,从这个原点水平向右为X轴正方向,原点垂直向下为Y轴正反向。

一言不合就上图

Android系统中为我们提供了getLocationOnScreen(int[] location)方法来获取控件在整个屏幕的绝对坐标,此时要注意的是:该坐标是从屏幕的左上角(原点)开始获取的,所以也包括了状态栏的高度,如下图。

1.1,在Android坐标系中屏幕区域的划分

图片来自工匠若水博客
通过上图我们可以很直观的看到Android的屏幕区域是如何划分的。接下来我们就看看如何或者这些区域中的坐标和度量方法吧。

//获取屏幕区域的宽高等尺寸获取
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int widthPixels = metrics.widthPixels;
int heightPixels = metrics.heightPixels;
//应用程序App区域宽高等尺寸获取
Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
//获取状态栏高度
Rect rect= new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rectangle.top;
//View布局区域宽高等尺寸获取
Rect rect = new Rect();  
getWindow().findViewById(Window.ID_ANDROID_CONTENT).getDrawingRect(rect);  

注意:

这些方法最好都在Activity的onWindowFocusChanged() 方法之后调用,因为在Activity的声明周期中 onCreate、onStar、 onResume这些方法都不是界面visible的真正时刻,真正的visible是在onWindowFocusChanged()方法执行时才被执行的。(onWindowFocusChanged()是在onResume()之后调用的,所以有的文章也会说是onResume()之后调用,其实更加准确的是在onWindowFocusChanged()之后,此处我会在以后的文章中做详细介绍)

2,视图坐标系

在日常开发中我们接触最对的就是视图坐标系了,视图坐标系描述的是子控件在父控件中相对位置。贴一张图来说明一下

QQ截图20170801223001.png

所谓视图坐标系是以控件(例如图中的TextView)父视图(图中的ViewGroup))的左上角为坐标原点的(绿色部分),从原出发水平向右为x轴正方向,垂直向下为y轴正方向来表示控件的相对位置的。

那么这个相对位置到底如何表示呢,同样看图说话。

视图坐标方法概述

简单的总结一下:

View自身的相对坐标
通过如下方法可以获得View到其父控件(ViewGroup)的距离:

方法 解释
getTop() 获取View自身顶边到其父布局顶边的距离
getLeft() 获取View自身左边到其父布局左边的距离
getRight() 获取View自身右边到其父布局左边的距离
getBottom() 获取View自身底边到其父布局顶边的距离
getX() 返回值为getLeft()+getTranslationX(),当setTranslationX()时getLeft()不变,getX()变。
getY() 返回值为getTop()+getTranslationY(),当setTranslationY()时getTop()不变,getY()变。

MotionEvent提供的方法
我们看上图那个触摸点,我们知道无论是View还是ViewGroup,最终的点击事件都会由onTouchEvent(MotionEvent event)方法来处理,MotionEvent也提供了各种获取焦点坐标的方法:

方法 解释
getX() 获取点击事件距离控件左边的距离,即视图坐标
getY() 获取点击事件距离控件顶边的距离,即视图坐标
getRawX() 获取点击事件距离整个屏幕左边距离,即绝对坐标
getRawY() 获取点击事件距离整个屏幕顶边的的距离,即绝对坐标

注意:

View中的getX()getY()方法只是与MotionEvent中的getX()、getY()方法只是重名而已,并不是一个。

上面就解释了你在很多代码中看见各种getXXX方法进行数学逻辑运算判断的含义。不过上面只是说了一些相对静止的Android坐标点关系,下面我们来看看几个和上面方法紧密相关的View方法,此处在本篇文章中不是重点,我会在以后的文章中做详细讲解。:

View宽高方法 解释
getWidth() layout后有效,返回值是mRight-mLeft,一般会参考measure的宽度(measure可能没用),但不是必须的。
getHeight() layout后有效,返回值是mBottom-mTop,一般会参考measure的高度(measure可能没用),但不是必须的。
getMeasuredWidth() 返回measure过程得到的mMeasuredWidth值,供layout参考,或许没用。
getMeasuredHeight() 返回measure过程得到的mMeasuredHeight值,供layout参考,或许没用。

参考:

如果说我比别人看得更远些,那是因为我站在了巨人的肩上

《Android群英传第三章》
Android中的坐标系以及获取坐标的方法
[Android应用坐标系统全面详解](http://blog.csdn.net/yanbober/article/details/50419117)

猜你喜欢

转载自blog.csdn.net/qq_23191031/article/details/76561801
今日推荐