39、自定义控件(四)-- View源码分析

一、View源码分析

ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程全是通过ViewRoot来完成的。

在ActivityThread中,当Activity对象被创建后,会将DectorView添加到Window中,同时会创建ViewRootImpl对象,并将

ViewRootImpl对象和decorView建立关联,这个过程我们可以参看源码:

root = new ViewRootImpl(view.getContext(),display);
root.setView(view,wparams,panelparentView);

View的绘制流程是从ViewRoot的performTeaversals方法开始的,它经过measure、layout、draw三个过程才能最终将一个View绘制出来。

其中measure用来测量View的宽和高,layout用来确定View在父容器中放置的位置,而draw负责将View绘制在屏幕上。

针对performTraversals的大致流程:

56c504d3-2716-42c0-bb2a-d003d907dba0_thumb

如图所示,performTraversals会依次调用performMeasure、performLayout和performDraw三个方法,分别完成顶级View的measure、layout和draw三大流程。

PperformMeasure会调用measure方法,measure方法又会调用onMeasure,其中onMeasure又会调用所有子元素的measure方法完成测量。

同理,performLayout和performDraw也是如此。

DecorView是顶级View,它其实是一个FrameLayout,View层的事件都先经过DecorView,然后才传递给我们的View。

1.1、Measure的原理

measure过程中,如果是View,则通过measure方法即可完成测量过程,如果是ViewGroup,则需要完成自己的测量过程外,

还会遍历去调用所有子元素的measure方法,各个子元素再递归去执行这个流程。

1、View的measure过程

  View的measure过程由其measure方法完成,measure方法是被final修饰的方法,不能重写。在View的measure方法中会去调用View的onMeasure方法,

因此只需要看onMeasure方法的实现即可:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

setMeasureDimension方法会设置View的宽和高来代表测量的结束,因此我们只需要看getDefaultSize方法即可:

public static int getDefaultSize(int size, int measureSpec) {
    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;
}

对于我们来说只需要看AT_MOST和EXACTLY两种情况,其实getDefaultSize返回的大小就是measureSpec中的specSize,它就是View测量后的大小。

而UNSPECIFIED这种情况一般用于系统内部的测量过程,在这种情况下,View的大小为getDefault的第一个参数size,即宽和高分别是

getSuggestedMinimumWidth和getSuggestedMinimumHeught这两个方法的返回值。

protected int getSuggestedMinimumHeight() {
    return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
}
protected int getSuggestedMinimumWidth() {
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}

可以看出,如果在没有指定背景的情况下,宽度和高度分别是mMinWidth和mMinHeight,如果不指定该属性,则默认为0.

如果指定背景的情况下,宽度和高度分别是max(mMinHeight, mBackground.getMinimumHeight())和max(mMinWidth, mBackground.getMinimumWidth())

那么我来看看mBackground.getMinimumWidth()方法:

public int getMinimumWidth() {
    final int intrinsicWidth = getIntrinsicWidth();
    return intrinsicWidth > 0 ? intrinsicWidth : 0;
}

可以看出,在Drawable有原始宽度的情况下,该方法返回的就是Drawable的原始宽度,当然高度也是如此。

转载于:https://www.cnblogs.com/pengjingya/p/5510714.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值