一、Android 布局添加流程
二、View的绘制流程
1、view绘制流程
2、具体3大绘制过程
1.测量过程performMeasure()
2.布局过程performLayout()
调用ViewRootImpl里的
performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight)
在这个方法里又调用了view的layout()方法
view.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
//在该方法中,计算了view的相对父布局的位置,即left/right/top/bottom
//如果是viewgroup,则还需要实现onLayout(boolean changed, int left, int top, int right, int bottom),来计算其子view的位置,这样来算出每个view的位置
比如FrameLayout作为一个ViewGroup,就实现了自己的onLayout()方法来计算子view的位置。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
//在layoutChilden()里计算了所有子view的位置,然后子view调用自己的layout()方法将位置放入自身
总结:
ViewGroup
确定自己的位置:layout(),调用onLayout()进行子view的布局。
对于自定义的ViewGroup,需要重写onLayout()来计算子view的位置。
View
调用layout()确定自己的位置即可。
3.绘制过程performDraw()
里面调用了ViewRootImpl.draw()方法,然后调动drawSoftware(),里面调用了view的draw()方法
public void draw(Canvas canvas) {
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
如果View是ViewGroup,其重写了dispatchDraw()方法。
在dispatchDraw()里遍历其子view,调用子view的draw()方法绘制自身。
总结绘制过程:
1.绘制背景drawBackground(canvas)
2.绘制自己onDraw(canvas)
3.绘制子view dispatchDraw(canvas) -->如果是ViewGroup,子view该方法为空
4.绘制前景、滚动条等装饰onDrawForeground(canvas)
额外知识、Android系统UI卡顿原理及VSync信号机制
1、卡顿原因分析及常见解决方式
1.过度绘制
去除不必要的背景色;
布局视图扁平化:避免过度嵌套布局,每次测量都需要测量父布局、子布局的所有位置,尽量采取扁平布局,例如ConstrainLayout,减少ReletiveLayout、LinearLayout嵌套布局。
减少透明色的使用;
2.UI线程里复杂运算
3.频繁GC
尽可能减少在for循环里new对象;减少在onDraw里new对象
尽量不要在循环中大量使用局部变量。
2、VSync机制
1.概念
1.屏幕刷新率
2.帧率 FPS
3.VSync
屏幕产生的硬件VSync
由SurfaceFlinger将其转换成软件VSync信号
2.VSync的作用
Vertical Synchronization 垂直同步的缩写
主要是为了解决“Tearing”撕裂的现象
同步UI绘制和动画,是的可以达到60fps的固定帧率。
GPU计算结果放到Buffer里,屏幕从另一个Buffer里取数据。在VSync信号触发交换读写缓存。
三重缓存模型3、Choreographer机制
三、高级绘制:Paint/Canvas/Path详解
1、Paint详解
概念:画笔,保存了绘制几何图形、文本和位图的样式和颜色信息。
(1)常用API:
Paint常用API Paint.setStrokCap()效果 Paint.setStrokeJoin()效果 setFilterBitmap()设置双线性过滤 字体的度量
//字体的度量,paint.getFontMetrics()
public static class FontMetrics {
/**
* The maximum distance above the baseline for the tallest glyph in
* the font at a given text size.
*/
public float top;
/**
* The recommended distance above the baseline for singled spaced text.
*/
public float ascent;
/**
* The recommended distance below the baseline for singled spaced text.
*/
public float descent;
/**
* The maximum distance below the baseline for the lowest glyph in
* the font at a given text size.
*/
public float bottom;
/**
* The recommended additional space to add between lines of text.
*/
public float leading;
}
(2)颜色相关
private void setColor(){
mPaint = new Paint();
mPaint.setColor(Color.RED); //设置颜色,16进制数值,0xFFFF0000
mPaint.setARGB(0,0,0,0); //分别表示透明度,RGB三原色,0~255数值
Shader shader = new Shader();
//一般由以下几种着色器
shader = new LinearGradient();
shader = new RadialGradient();
shader = new SweepGradient();
shader = new BitmapShader();
shader = new ComposeShader();
mPaint.setShader(shader); //着色器
//
}
1.线性着色器:
//float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],@Nullable float positions[], @NonNull TileMode tile
//colors[]颜色数组
// positions[]数值范围[0,1],指定某个位置的颜色值,个数应该和colors[]个数一样,否则报错。默认为null,线性渐变。
mShader = new LinearGradient(0,0,500,500,new int[]{Color.RED,Color.BLUE},null, Shader.TileMode.CLAMP);
线性着色器效果
2.环形渲染
//RadialGradient()构造方法:
//float centerX, float centerY, float radius,
// @NonNull @ColorInt int colors[], @Nullable float stops[],
// @NonNull TileMode tileMode
//float centerX, float centerY, float radius,
// @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode
mShader = new RadialGradient(250,250,250,new int[]{Color.GREEN,Color.YELLOW,Color.RED},null, Shader.TileMode.CLAMP);
环形渲染
3.扫描渲染
//SweepGradient()构造方法:
//float cx, float cy, @NonNull @ColorInt int colors[], @Nullable float positions[]
//float cx, float cy, @ColorInt int color0, @ColorInt int color1
mShader = new SweepGradient(250,250,Color.RED,Color.GREEN);
扫描渲染
4.位图渲染
mBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.liuyifei);
mShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
...
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0,0,mBitmap.getWidth(),mBitmap.getHeight(),mPaint);
}
位图渲染
不同Shader.TileMode
// draw的区域超过shader的大小时,如何绘制。有以下三种绘制方法:
public enum TileMode {
/**
* replicate the edge color if the shader draws outside of its
* original bounds
*/
CLAMP (0),
/**
* repeat the shader's image horizontally and vertically
*/
REPEAT (1),
/**
* repeat the shader's image horizontally and vertically, alternating
* mirror images so that adjacent images always seam
*/
MIRROR (2);
TileMode(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0,0,mBitmap.getWidth()*1.5f,mBitmap.getHeight()*1.5f,mPaint);
}
TileMode.CLAMP
TileMode.MIRROR
TileMode.REPEAT
5.组合渲染
//ComposeShader()的构造方法:
//@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull PorterDuff.Mode mode
//@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode
mBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.liuyifei);
BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
LinearGradient linearGradient = new LinearGradient(0,0,500,500,new int[]{Color.RED,Color.BLUE,Color.GREEN},null, Shader.TileMode.CLAMP);
mShader = new ComposeShader(bitmapShader,linearGradient, PorterDuff.Mode.MULTIPLY);
组合渲染
其中PorterDuff.Mode是组合渲染中两个Shader进行混合的规则,PorterDuff.Mode.MULTIPLY是相乘。
2、利用Paint绘制滤镜Xfermode
PorterDuff.Mode图层混合模式
一共有18种模式。
//有三个地方用到图层混合
//1、ComposeShader
//2、mPaint.setXfermode
//3、PorterDuffColorFilter
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mShader = new ComposeShader(bitmapShader,linearGradient, new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
mPaint.setShader(mShader);
//由于某些图层混合在硬件加速下不能用
//所以需要禁止硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE,null);
网友评论