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

【本文出自大圣代的技术专栏 http://blog.csdn.net/qq_23191031
【转载烦请注明出处,尊重他人劳动成果就是对您自己的尊重】

前言

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占据全屏的效果。

视图树

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

贴一张图汇总一下吧

总结

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

Android的常用坐标系

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

1,世界坐标系

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

一言不合就上图

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

1.1,世界坐标系中屏幕区域的划分

图片来自工匠若水博客

通过上图我们可以很直观的看到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方法进行数学逻辑运算判断的含义。不过上面只是说了一些相对静止的世界坐标点关系,下面我们来看看几个和上面方法紧密相关的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应用坐标系统全面详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,418评论 25 707
  • Android控件架构与自定义控件(一) (本文并非原创文章,整理摘抄方便自己查看,原文地址为Android控件架...
    b5e7a6386c84阅读 960评论 0 6
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,351评论 0 17
  • 提到自由两个字总是会引起许多的的遐想,而二十岁又恰好是一个富有朝气,又不失理智的年龄。这个年龄的我们应该做些什么?...
    丿肆悦阅读 702评论 9 5
  • 老街对于我们来说还有另一个名字我,那就是美食一条街。街上从头到尾处处是小卖部,基本上每走五六米就能看到一家,并且越...
    ___hh阅读 393评论 0 3