布局绘制
本文目标:
(1)优化布局层级及复杂度
(2)避免过度绘制
视图绘制原理
(1)测量:确定大小。(自顶向下进行视图树的遍历,确认每个 ViewGroup 和 View 元素应该为多大)
(2)布局:确定位置。(也是自顶向下的遍历操作,每个 ViewGroup 根据测量阶段确认的大小,确认自己应该摆放的位置)
(3)绘制:绘制试图。(这个阶段,针对视图树中的每个对象,系统都会为它创建一个context对象,然后向GPU发生绘制命令)
性能瓶颈有哪些
(1)每个阶段耗时
(2)自顶向下的遍历
(3)特殊场景(?),会触发多少绘制
如何减少布局层级和复杂度?
准则:
(1)减少View树的层级,
(2)尽量宽而浅(从上往下,元素尽量在一个ViewGroup中),避免窄而深(从左往右,让布局深度更浅)
(3)不嵌套使用RelativeLayout
(4)不在嵌套LinearLayout中使用weight
(5)merge标签:减少一个层级,只能用于根View
ConstraintLayout
优点:
(1)实现几乎完全的扁平化布局
(2)构建复杂布局性能更高
(3)具有 RelativeLayout 和 LinearLayout 特性
什么是过度绘制?
(1)一个像素最好只被绘制一次
(2)调试GPU过度绘制(在手机的开发者选项中)
(3)蓝色可接受
避免过度绘制的方法
(1)去掉多余背景色,减少复杂shape使用
(2)避免层级叠加
(3)自定义View使用clipRect屏蔽被遮盖View绘制
// 自定义 View 绘制
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Don't draw anything until all the Asynctasks are done and all the DroidCards are ready.
if (mDroids.length > 0 && mDroidCards.size() == mDroids.length) {
// Loop over all the droids, except the last one.
int i;
for (i = 0; i < mDroidCards.size() - 1; i++) {
mCardLeft = i * mCardSpacing;
canvas.save();
// 指定绘制区域
//开始绘制
canvas.clipRect(mCardLeft,0,mCardLeft+mCardSpacing,mDroidCards.get(i).getHeight());
// Draw the card. Only the parts of the card that lie within the bounds defined by
// the clipRect() get drawn.
drawDroidCard(canvas, mDroidCards.get(i), mCardLeft, 0);
canvas.restore(); //记得释放
}
// Draw the final card. This one doesn't get clipped.
drawDroidCard(canvas, mDroidCards.get(mDroidCards.size() - 1),
mCardLeft + mCardSpacing, 0);
}
// Invalidate the whole view. Doing this calls onDraw() if the view is visible.
invalidate();
}
其他技巧
(1)Viewstub : 高效占位符,延时初始化
(2)onDraw中避免:创建大对象,耗时操作
(3)TextView优化,(见后续)