android自定义ui电池,Android自定义控件-UI绘制流程

一、布局加载

一、探秘setContentView(activity屏幕安装的位置)

4ae563d8088aca38e4caf9cc7e2ac765.png

其实就是调用了Window中的setContentView

getWindow()方法获取到的Window实现类是PhoneWindow

21010713cce2c783390532634c024cfb.png

最终调用的还是PhoneWindow中的setContentView方法

二、Window(电子屏幕)

Window的类型

6161dec306911b1557bb0a511a8c80c1.png

三、PhoneWindow(手机屏幕)

ed78b7064cfe106ebe6e67b38c3fe41d.png

mContentParent为空的时候会调用如下方法

b2cf6df385cb14789bc41659ea42fbfc.png

installDecor的作用:就是创建一个DecorView,并将窗体布局添加到DecorView中,然后返回ID为content的帧布局

初始化一个DecorView

b653b93b2abedd5482211aa916d03133.png

然后调用generateLayout方法

464c57370a38aad84cf53123a1b19f75.png

generateLayout方法的作用就是设置一些窗体的属性值,然后窗体布局添加到DecorView中,并返回窗体布局中ID为content的帧布局

四、DecorView(屏幕显示的内容)

DecorView是PhoneWindow中的内部类,继承帧布局

717af3ad877a554dde98c1ff9a4b7b62.png

五、关系图

7e3e007a06d4d2a0bb65430ff56bda1a.png

二、UI绘制

UI绘制的起始点

在PhoneWindow中调用setContentView的时候调用了mContentParent的addView方法,其实调用就是ViewGroup中的addView方法。

d4189614039d135b4bc83a88d17d9331.png

1、requestLayout

3b771f5a9987c54bce80b5e6ee0c144b.png

最终调用的ViewRootImpl中的requestLayout方法

7c9b5ae22c9b94d579b96f2445666af0.png

最终调用了scheduleTraversals方法

2、invalidate

ViewGroup中的invalidate方法调用了View中的invalidate方法,该方法必须在UI线程中被调用

View中invalidate方法如下

6a5704bbae1415f82feed0d21e4983c3.png

invalidateInternal关键代码如下

0bb37fd9560bb0d2c8ed245bcce452e5.png

上述关键代码中最终调用的是ViewGroup中的invalidateChild方法

ViewGroup中invalidateChild方法的关键代码如下

e6e1eeb7cb569ec5afdd8545988363ed.png

通过do-while循环找到根布局(ViewRootImpl),并调用其中的invalidateChildInParent

3、ViewRootImpl

1b8396f5e7d45bc861b6809367ff6d46.png

516e6ddf52eb3f06ca811a1485e79bf9.png

fd538cb9148c66c9dc872d79699d852e.png

b6d7f10a6f56f67e75caeafe428b1e57.png

500cf6da0bc0f354cca20c71033076ec.png

performTraversals关键代码

bc859c17b110eb42a331c00c27f99270.png

5310c04527027e1738d30b2201d8f374.png

292b64428cf2a058082f58c89f68e6b4.png

07a5705ff9a7379e40b1dab84cb43510.png

performLayout关键代码

a083f6d55c00949fdec5210acaa8b8aa.png

0eee3eb36ba5a80a008f14156c7bb602.png

performDraw关键代码

8c6151ffd1cb80748022662d82ec0e56.png

4、流程图

c3638500f4c430e8d425f3049b41d7a2.png

5、masure

420cf5935d68ca4295b9d405f17a83ea.png

5.1、view的测量

7d6908f7e9cf6489713a60481e26f068.png

方法中的两个参数是由父容器传递进来的,是测量规则。

测量规则由两部分组成,高2位是MODE,低30位是size

测量规则由三种类型

EXACTLY: 精确的。

AT_MOST: 最大

UNSPECIFIED: 不确定

onMeasure方法代码如下

45330182a9bc9e6f34f1fdc6314880d5.png

5.2、ViewGroup的测量

341c53700f73ae19584fd302675bb90b.png

作用:获取子view的LayoutParams,通过LayoutParams获取到子view的尺寸,然后通过父容器的测量规则和子view的尺寸通过getChildMeasureSpec方法生成一个测量规则传递给子view进行测量

getChildMeasureSpec方法如下

17ec9d7e05677a6029cf7529e466d613.png

6、layout

6.1、View的layout

d1f9a9b5a5a73d445847ee54438648d0.png

在View中onLayout是空实现

d21c1e31dc3491a7a502c82b4eb84ee9.png

留给子类自己需要的时候去实现

6.2、ViewGroup的layout

fdec697ad028fc0e70fcf0f3b43f5f16.png

实际上ViewGroup中的layout方法调用的是父类View的layout方法

ViewGroup中的onLayout方法是一个抽象类,子类必须要去实现

36eef65069f51d564a57f042a5a9d512.png

RelativeLayout中的onLayout方法如下

36dc5c73fe08188fd5b3a571ca08aa04.png

最终还是调用子view的layout方法

7、draw

在ViewRootImpl类中的drawSoftware方法中创建了canvas并调用了view.draw(canvas)

View中的draw方法如下

8cc76c01d54d051bae4130a894993f96.png

第一步:绘制背景

54633beed37a5699cd38234fcb9da0b4.png

d1d2a30e4710b5ecb28eed943217a2b9.png

第二步:

第三步:绘制内容

ee360d02ae38e9d53323dca79d924c79.png

769562f7057213985eb566a0d3f3f8fb.png

因为每个View内容不同,所以onDraw留给子类实现

第四步:绘制子view

bc4b26577412cac6919f15c7d069e30e.png

36e9dd84450f4b149a2d502068ef0acf.png

View类中dispatchDraw是空实现,如果有子view再去复写该方法,如ViewGroup

通过源码我们可以看到ViewGroup中通过for循环调用了drawchild方法

bf6989d6918859109345ca32f0b39c96.png

第五步:

第六步:绘制滚动条

94572fc48fd49b644feb1f080640a351.png

3ff88a1795a979cec6c15f720f9a59de.png

8、postInvalidate

通过该方法注释可以知道这个方法是在子线程中被调用

5954c006cf45bb4f0bfe8439b6aab71b.png

0d5a08657e2bd8cfc9cc8aa3ff5a6285.png

最终调用了ViewRootImpl中的dispatchInvalidateDelayed方法

0b9a3ea87e2b8d176a10523809da2494.png

1ce21e1a973e5c33ac7048f8b5441382.png

2bb9f6558dd8f253561056d253588a19.png

最终还是通过handle调用了在主线程中invalidate方法

三、套路自定义控件

继承View

重新onMeasure方法测量自己的宽高,然后调用setMeasuredDimension设置宽高

a833e21a57a7ca44c83004ea9a99bc68.png

具体可以参考TextView源码中的onMeasure()方法的写法,其中就是按照上述顺序完成的

继承ViewGroup

在onMeasure方法中需要测量子控件大小,还需要测量自身大小,然后调用setMeasuredDimension设置自身的宽高,在测量子view是可用使用ViewGrou提供的测量子view的方法,也可以自定义测量规则,最后在onLayout方法中对子view进行布局

dcaf3c44714de06fdab706c3047ddc92.png

具体可以参考LinearLayout或RelativeLayout中的onMeasure()方法,其中子view的获取都是通过for循环完成了

四、问题

如何让一个ScrollView里面的ListView全部展开?

通常的方法是继承ListView,重写onMeasure方法:

294a8d57be99bb222570521a1522b11a.png

为什么要这么做?

1.设置mode为什么是 MeasureSpec.AT_MOST?

2.value为什么是Integer.MAX_VALUE >> 2?

五、TODO

自定义控件:高仿华为应用市场的标签页

标签:控件,调用,自定义,ViewGroup,测量,UI,View,方法,view

来源: https://blog.csdn.net/JavaKam/article/details/100919296

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值