View的绘制

2016-12-15  本文已影响31人  dafaycoding

有过自定义View经验的同学都清楚,自定义View要处理好两件事:View的绘制和事件的处理。这一篇简单总结了一下自己对View绘制的理解,关于事件处理请戳这里Android事件分发

View和ViewGroup的几个主要方法如下图

图1-1.jpg

1.每个View都只有一个父View

2.整个视图只有一个根View即DocorView DocorView类继承了Freamlayout定义在phoneWindow.java里

3.我们通过setContentView设置的View并不是最跟View。

图1-2.png

3.1 隐藏标题栏需要在setContentView之前设置。

4.一个View如何显示到屏幕上来

4.1view有多大 -- measure --- onMeasure

4.2view的位置在哪儿 -- layout --- onLayout

4.3view长什么样子 -- draw --- onDraw

5.不是所有的View都能添加子View

5.1 就是ViewGroup的子类,因为只有ViewGroup实现了ViewParent接口

5.2 我们通过getParent拿到的虽然是ViewParent,但是在它不为null的前提下,我们可以放心的强转为ViewGroup
其中measure和layout方法都是final的,无法重写,虽然draw不是final的,但是也不建议重写该方法。这三个方法都已经写好了View的逻辑,如果我们想实现自身的逻辑,而又不破坏View的工作流程,可以重写onMeasure、onLayout、onDraw方法。

6.measure只有View有这个方法,ViewGroup没有重载测量自身的大小

MeasureSpec:用来辅助计算View的大小
View.MeasureSpec.AT_MOST;// 最大
View.MeasureSpec.EXACTLY;// 精确
View.MeasureSpec.UNSPECIFIED;// 未知

目前未知没有使用,因为每一个View被添加到视图上时,都必须制定宽高,就算你不指定,也会有一个默认的(每个ViewGroup生成默认宽高都不一样)

通过 MeasureSpec.makeMeasureSpec() 来合成模式和宽高
通过 MeasureSpec.getMode和MeasureSpec.getSize 来获取模式和宽高

代码中设置View的宽和高必须要通过LayoutParams来设置,所有影响子View和父View之间的关系的属性全部需要通过LayoutParams来设置,也可以根据xml布局中属性名是否带有layout_开头来判断。
LayoutParams有很多种类,基本上是每一种布局都有一个自己的实现,因为每个布局都有各自的特征,没办法用一个统一类来描述

如果父亲的mode是 EXACTLY, 子View是WRAP_CONTENT  那么mode 是AT_MOST, size 是父亲的最大size。
如果父亲的mode是 EXACTLY, 子View是MATH_PARENT   那么mode 是EXACTLY, size 是父亲的最大size。
如果父亲的mode是 EXACTLY, 子View是DIP           那么mode 是EXACTLY, size 是DIP。

如果父亲的mode是 AT_MOST,  子View是WRAP_CONTENT  那么mode 是AT_MOST, size 是父亲的最大size。
如果父亲的mode是 AT_MOST,  子View是MATH_PARENT   那么mode 是EXACTLY, size 是父亲的最大size。 // ---(大多数View实现都是EXACTLY,但是有些View实现是AT_MOST,我们要尽量避免这种情况)
如果父亲的mode是 AT_MOST,  子View是DIP           那么mode 是EXACTLY, size 是DIP。

如果你设定宽高为MATH_PARENT 或者 DIP 则模式为 EXACTLY,如果是WRAP_CONTENT,则为AT_MOST
如果你设定的宽高为DIP,那么你的size就是dip,其他情况都是父View的最大值,或者父View剩余的最大值

7.通过调用requestLayout/requestFocus都将发起一个View树的测量。测量完毕后会进行布局,布局完毕后就会绘制。
如果View的大小没有发生改变,布局也没有变化,只是显示的内容发生了变化,则可以通过invalidate来请求绘制,此时将不会测量和布局,直接从绘制开始。

8.View内部的mPrivateFlags变量
View中有一个私有int变量mPrivateFlags,用于保存View的状态,int型32位,通过0/1可以保存32个状态的true或者false,采用这种方式可以有效的减少内存占用,提高运算效率。
当某一个View发起了测量请求时,将会把mPrivateFlags中的某一位从0变为1,同时请求父View,父View也会把自身的该值从0变为1,同时也将会把其他子View的值从0变为1。这样一层一层传递,最终传到到DecorView,DecorView的父View是ViewRoot,所以最终都将由ViewRoot来进行处理。
ViewRoot收到请求后,将会从上至下开始遍历,检查标记,只要有相对应的标记就执行测量/布局/绘制

9.Measure测量流程

图1-3.jpg

10.LayoutParams


参考博客:
安卓中的颜色相关内容,包括颜色的定义,创建颜色的几种方式,以及颜色的混合模式等
自定义View分类与流程
Canvas之画布操作
Canvas之图片文字

上一篇下一篇

猜你喜欢

热点阅读