Android 自定义View、ViewGroup(二)之绘制流程

今天我们来从源码的角度讲解一下View的绘制流程,如果没有看过我上一篇文章的话,可以先去读一下Android 自定义View、ViewGroup(一)之工作原理

一. 总体把握View绘制流程

1.View的绘制是从ViewRoot.java类中的performTraversals方法开始的,我们找到ViewRoot.java的源码可以看到:

private void performTraversals() {
        final View host = mView;
	...
	host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
	...
	host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
	...
	draw(fullRedrawNeeded);
}

从代码中可以看出View的绘制流程大概分为三个过程,①measure测量(确定大小)、②layout布局(确定位置)、③draw(画出来)
2.我们去View.java中,看一下measure,layout,draw这三个方法

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
	...
    // measure ourselves, this should set the measured dimension flag back
    onMeasure(widthMeasureSpec, heightMeasureSpec);
	...
}
public final void layout(int l, int t, int r, int b) {
	...
	onLayout(changed, l, t, r, b);
	...
}
public void draw(Canvas canvas) {
   ...
   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)
   onDraw(canvas);
   ...
}
从上面三个方法中,我们知道measure和layout方法是不能够重写的,其中的业务逻辑处理已经封装到了onMeasure、onLayout以及onDraw方法中了,一般来说,我们是不需要改变整个绘制View的框架,如果我们要自定义View或者ViewGroup,通常来说我们只需要重写onMeasure、onLayout以及onDraw种的方法就可以了。

二.View的onMeasure方法实现测量

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//setMeasuredDimension方法不能被重写的,但是我们可以调用此方法指定其显示的大小,或者在布局文件中指定其大小	
          setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
			getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
 
public static int getDefaultSize(int size, int measureSpec) {
//根据传入的measureSpec可以获取到指定的mode和size
	int result = size;
	int specMode = MeasureSpec.getMode(measureSpec);
	int specSize =  MeasureSpec.getSize(measureSpec);
//视图本身会对最终大小进行排版
	switch (specMode) {
	case MeasureSpec.UNSPECIFIED:
		result = size;
		break;
	case MeasureSpec.AT_MOST:
	case MeasureSpec.EXACTLY:
		result = specSize;
		break;
	}
	return result;
}
综上所述:

①.父视图会给子视图参考的大小

②.我们可以通过setMeasuredDimension方法或者布局文件控制视图的大小

③视图本身会对最终大小进行排版

如果你对onMeasure过程不太清楚,请看下面两个连接

Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(上)

Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(下)

三.View的onLayout方法实现布局

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}

View的onLayout方法是一个空实现,原因是因为布局和显示子控件是ViewGroup的工作,也就是说,是父视图控制子视图的现实位置,我们看一下ViewGroup的onLayout

@Override
protected abstract void onLayout(boolean changed,int l, int t, int r, int b);
我们看到ViewGroup的onLayout是一个abstract的,也就是说需要一个具体的ViewGroup重写此方法去具体的实现相应的逻辑,而这个逻辑通常会有两步

①.遍历,得到每个子View

②.调用childView.layout方法对子View进行布局

给出一张图,让大家更好的了解onLayout方法后4个参数的含义


注意:onMeasure和onLayout结束之后,getMeasuredWidth方法和getWidth方法区别

①.获取的时机不一致

     getMeasuredWidth方法是在measure过程结束后就可以获取到

     getWidth方法是在layout过程结束后才可以获取到

②计算的方式不一致

    getMeasuredWidth方法获取的值是setMeasuredDimension方法已经设置好的

    getWidth方法获取的值是通过视图右边坐标减去左边坐标得到的

四.View的draw方法将视图绘制出来

   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)
按照上面6个步骤,将视图绘制出来,具体使用请看《Android 自定义View Canvas类的使用》


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值