Android开发者们常谈的自定义View/ViewGroup

本文详细介绍了Android自定义View和ViewGroup时的关键步骤,重点解析了onMeasure的默认实现和在ViewGroup中的扩展。内容包括getDefaultSize、setMeasuredDimension、measureChildren和getChildMeasureSpec等方法的作用,以及如何根据测量模式和建议尺寸确定View的最终大小。此外,文章还强调了在自定义ViewGroup时如何遍历并测量子View。最后,作者推荐了B站的相关视频资源以帮助读者深入理解自定制View的实践。
摘要由CSDN通过智能技术生成

前言

自定义View和自定义ViewGroup前,如果了解了需要重写方法的一些工作流程,会让自定义工作更加得心应手。

自定义的时候至少要干的事情

  • 自定义View主要需要重写onMeaure方法和onDraw方法,一个是确定View的大小,另一个是绘制View的内容
  • 自定义ViewGroup主要需要重写onMesure方法和onLayout方法,一个用于确定ViewGroup的大小,另一个确定子View的位置。onDraw方法反而不是必须重写的,甚至不一定会被调用,只有当ViewGroup拥有background或调用setWillNotDraw(false)后才会回调onDraw方法。

onMeasure的默认实现

在View的源码中存在onMeasure的默认实现,ViewGroup继承View并且没有重写该方法,使用的还是默认的实现。

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

关于参数MeasureSpec详见 MeasureSpec到底是个什么东西

方法的实现具体涉及额外的四个方法:setMeasuredDimensiongetDefaultSizegetSuggestedMinimumWidthgetSuggestedMinimumHeight,其中getSuggestedMinimumWidthgetSuggestedMinimumHeight差不多,返回了mBackgroundsizeminiSize的其中较大的那一个,所以背景图片能够撑开一个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;
}

通过源码可以了解到这个方法是根据测量模式来返回指定值,当模式是UNSPECIFIED的时候返回getSuggestedMinimumSize方法传过来的值,当测量模式是AT_MOSTEXACTLY时返回由父View给定的适当尺寸。从这里可以看出在自定义View的时候如果不重写onMeasure方法处理AT_MOST测量模式的话,wrap_content是不会生效的。

setMeasuredDimension

它是最后设置View宽高的方法。

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
    boolean optical = isLayoutModeOptical(this);
    if (optical != isLayoutModeOptical(mParent)) {
        Insets insets = getOpticalInsets();
        int opticalWidth  = insets.left + insets.right;
        int opticalHeight = insets.top  + insets.bottom;
        measuredWidth  += optical ? opticalWidth  : -opticalWidth;
        measuredHeight += optical ? opticalHeight : -opticalHeight;
    }
    setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}

这里做了一个光学边界判断isLayoutModeOptical。简单查了一下资料,据说是Android 5.0才加的,具体作用还未详细了解不过出现在这里并不影响后续阅读,不过这个点需要今后留意。之后就调用了setMeasuredDimensionRaw

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值