View 绘制的流程框架
如图所示
View 的绘制是从上往下一层层迭代下来的. DecorView-->ViewGroup(--->ViewGroup)-->View , 按照这个流程从上往下, 依次 measure(测量),layout(布局),draw(绘制).
View 中重要方法
onMeasure(widthMeasureSpec, heightMeasureSpec)
onMeasure 过程决定了 View 的宽高, Measure 完成后可以通过 getMeasureWidth 和 getMeasureHeight 方法获取到 view 的测量后的宽高, 在几乎所有的情况下都会等于最终 view 的宽高
onMeasure() 方法接收两个参数, widthMeasureSpec 和 heightMeasureSpec, 这两个值分别用于确定视图的宽度和高度的规格和大小.
onLayout(boolean changed, int left, int top, int right, int bottom)
layout 过程决定了 View 的四个顶点的坐标和实际的 View 的宽高, 完成以后可以通过 getTop,getBottom,getLeft,getRight 来获取 View 的四个顶点位置, 并通过 getWidth,getHeight 获取 View 的最终宽高
onDraw(Canvas canvas)
draw 过程则决定了 View 的显示, 完成 draw 后 view 会显示在屏幕上
绘制背景 (background.draw(Canvas))
绘制自己 protected void onDraw(Canvas canvas) onDraw 绘制自己, 新建一个 paint 在 canvas 上绘制自己的图形
绘制 children (dispatchDraw)dispatchDraw 会遍历调用所有子元素的 draw 方法 绘制装饰 (onDrawScrollBars)
isEnabled() 当前视图是否可用.
可以调用 setEnable() 方法来改变视图的可用状态, 传入 true 表示可用, 传入 false 表示不可用.
它们之间最大的区别在于, 不可用的视图是无法响应 onTouch 事件的.
isFocused() 当前视图是否获得焦点
通常情况下有两种方法可以让视图获得焦点, 即通过键盘的上下左右键切换视图, 以及调用 requestFocus() 方法.
而现在的 Android 手机几乎都没有键盘了, 因此基本上只可以使用 requestFocus() 这个办法来让视图获得焦点了.
而 requestFocus() 方法也不能保证一定可以让视图获得焦点, 它会有一个布尔值的返回值, 如果返回 true 说明获得焦点成功, 返回 false 说明获得焦点失败. 一般只有视图在 focusable 和 focusable in touch mode 同时成立的情况下才能成功获取焦点, 比如说 EditText.
offsetTopAndBottom(int offset) 及 offsetLeftAndRight(int offset)
offsetTopAndBottom 直接改变的是 top, bottom, 相当于在 parent 中上下平移 View 的位置;
offsetLeftAndRight 直接改变的是 left, right, 相当于在 parent 中左右平移 View 的位置;
View 的边界直接发生了变化, 又因为 View 和他的子 View 的相对位置没变, 所以他的子 View 的边界也跟着变化了.
从 View 的测量, 布局和绘制原理来看, 要实现自定义 View, 根据自定义 View 的种类不同, 可能分别要自定义实现不同的方法. 但是这些方法不外乎: onMeasure() 方法, onLayout() 方法, onDraw() 方法.
onMeasure() 方法: 单一 View, 一般重写此方法, 针对 wrapcontent 情况, 规定 View 默认的大小值, 避免于 matchparent 情况一致. ViewGroup, 若不重写, 就会执行和单子 View 中相同逻辑, 不会测量子 View. 一般会重写 onMeasure() 方法, 循环测量子 View.
onLayout() 方法: 单一 View, 不需要实现该方法. ViewGroup 必须实现, 该方法是个抽象方法, 实现该方法, 来对子 View 进行布局.
onDraw() 方法: 无论单一 View, 或者 ViewGroup 都需要实现该方法, 因其是个空方法
自定义 View 优化策略
为了加速你的 view, 对于频繁调用的方法, 需要尽量减少不必要的代码. 先从 onDraw 开始, 需要特别注意不应该在这里做内存分配的事情, 因为它会导致 GC, 从而导致卡顿. 在初始化或者动画间隙期间做分配内存的动作. 不要在动画正在执行的时候做内存分配的事情.
还需要尽可能的减少 onDraw 被调用的次数, 大多数时候导致 onDraw 都是因为调用了 invalidate(). 因此请尽量减少调用 invaildate() 的次数. 如果可能的话, 尽量调用含有 4 个参数的 invalidate() 方法而不是没有参数的 invalidate(). 没有参数的 invalidate 会强制重绘整个 view.
另外一个非常耗时的操作是请求 layout. 任何时候执行 requestLayout(), 会使得 Android UI 系统去遍历整个 View 的层级来计算出每一个 view 的大小. 如果找到有冲突的值, 它会需要重新计算好几次. 另外需要尽量保持 View 的层级是扁平化的, 这样对提高效率很有帮助.
如果你有一个复杂的 UI, 你应该考虑写一个自定义的 ViewGroup 来执行他的 layout 操作. 与内置的 view 不同, 自定义的 view 可以使得程序仅仅测量这一部分, 这避免了遍历整个 view 的层级结构来计算大小.
最后文末放上一个技术交流群: Android 架构设计 (185873940)
群内有许多技术大牛, 有任何问题, 欢迎广大网友一起来交流, 群内还不定期免费分享高阶 Android 学习视频资料和面试资料包~
再推荐一篇文章, 具体的架构视频, 面试专题, 学习笔记都在这篇文章中:"寒冬未过", 阿里 P9 架构分享 Android 必备技术点, 让你 offer 拿到手软!
偷偷说一句: 群里高手如云, 欢迎大家加群和大佬们一起交流讨论啊!
来源: http://www.jianshu.com/p/fe076a105755