自定义ViewGroup中的几个方法记载

以下记载为在自定义ViewGroup,并向其中放入控件时的方法的理解,后期在能力提升上来后,会重新修改记录;

一、onMeasure

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
1.1

翻译View中父类对该方法的注释是:

  • 测量视图及其内容,以确定测量的宽度和测量的高度,这个方法应该由子类覆盖,以提供对其内容的精确和高效的度量;
  • 重写此方法时,必须使用setMeasuredDimension(int,int)来存储此视图的测量宽度和高度,如果不这样做,将触发lllegalStateException异常。也可以直接调用父类的onMeasure方法;
  • 测量的基类默认为背景大小,出入度量允许较大的大小,子类应该覆盖以提供更好的内容度量;
  • 如果这个方法被重写,那么子类的责任就是确保被测量的高度和宽度至少是视图的最小高度和宽度;

上面说这么多,我理解就是:自定义时,子类可复写此方法,并测量好控件的宽高,最终调用setMeasuredDimension(int width,int height)方法给定控件的宽高
这个方法最终的目的就是测量控件的宽高;

1.2

在解释这两个参数前,需要先说一个类MeasureSpec:封装了从父级传递到子级的布局需求,每一项测量都表示对宽度和高度的要求,MeasureSpec由一个尺寸和一个模式组成;
它有三种模式:

MeasureSpec的三种模式 解释
MeasureSpec.UNSPECIFLED 他的父控件没有给他任何约束
他可以是他想要的任意大小尺寸空间
MeasureSpec.EXACTLY 他的父控件已经确定了约束尺寸
不管该控件想要多大,都会赋予该确定的尺寸
布局属性(MATCH_PARENT/固定值
MeasureSpec.AT_MOST 子元素的大小可以达到指定的大小
布局常用(wrap_content)

获取模式:getMode(int)
获取尺寸:getSize(int)

1.3

经过1.2的解释,现在我们再来看传递进来的两个参数:

  • 参数解释

int widthMeasureSpec : 父控件给当前控件的水平宽度和约束条件
int heightMeasureSpec: 父控件给当前控件的垂直高度和约束条件
根据参数我们可以分别得到宽高的模式和尺寸:

//宽度模式
 int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
//宽度尺寸
 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
//高度模式
 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//高度尺寸
 int heightSize = MeasureSpec.getSize(heightMeasureSpec);

为什么传入的参数是int类型?其实是MeasureSpec是View的一个内部类,其中有一个makeMeasureSpec(int size,int mode )方法将size和mode打包成一个32位的int值,这样可以减少内存的分配。所以我们可以使用getMode()和getSize()获取到父控件给与的约束和尺寸;

1.4

现在我们已经知道父控件给与本控件的模式和尺寸,现在看看,如何测量该ViewGroup中子控件的宽高;
在ViewGroup中,提供了三个关于测量子控件的方法:

/**
* 访问所有的子控件并测量他们,包含他们的约束条件和padding,并且跳过了状态
*为Gone的子控件
* @param widthMeasureSpec 父控件给与本ViewGroup的宽度约束
* @param heightMeasureSpec 父控件给与本ViewGroup的高度约束
*/
protected void measureChildren(int widhtMeasureSpec,int heightMeasureSpec);
/**
*访问这个ViewGroup中的单个子控件,并测量他,包含了这个视图的约束条件和padding
* @param child  要测量的子控件
* @param parentWidthMeasureSpec 父控件的宽度约束要求
* @param parentHeightMeasureSpec 父控件的高度约束要求
*/
protected void measureChild(View child,
                        int parentWidthMeasureSpec,int parentHeightMeasureSpec);
/**
* 测量这个子控件,包含这个视图的约束要求以及他的填充和边缘,
* 这个子控件必须有 MarginLayoutParams获得是在getChildMeasureSpec中完成滴
*/
protect void measureChildWithMargins(View child,
                        int parentWidthMeasureSpec,int widthUsed,
                        int parentHeightMeasureSpec ,int heightused);

我现在只使用了第一种方法 measureChildren(),其他两种没研究也不会用,就不说了;
当使用measureChildren() 方法后,我们就可以获取子控件的测量宽高了

view.getMeasuredWidth();
view.getMeasuredHeight();

下面是我在学习时用到的onMeasure中的代码:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        Log.i(TAG, "onMeasure: widhtSize = " + widthSize + ",  heigthSize = " + heightSize);
        measureChildren(widthMeasureSpec,heightMeasureSpec);

        int width = getMaxWidth(widthMode,widthSize);
        int height = getMaxHeight(heightMode,heightSize);
        
        setMeasuredDimension(width,height );        //确定控件的宽高大小并设置
    }
 public int getMaxWidth(int widthMode,int widthSize) {
        int childNum = getChildCount();
        int maxWidth = 0;
        Log.i(TAG, "getMaxWidth: 宽度模式");
        switch (widthMode) {
            case MeasureSpec.AT_MOST:
                Log.i(TAG, "getMaxWidth: MeasureSpec.AT_MOST 设置了最大范围(一般是WRAP_CONTENT)");
                for (int i = 0; i < childNum; i++) {
                    View childView = getChildAt(i);
                    int childMeasuredWidth = childView.getMeasuredWidth();
                    if (i == 0){
                        maxWidth += childMeasuredWidth ;
                        continue;
                    }
                    if (maxWidth + childMeasuredWidth + mHorizontalSpace > widthSize){
                        maxWidth = widthSize;
                        break;
                    }else{
                        maxWidth += (childMeasuredWidth + mHorizontalSpace);
                    }
                }
                break;
            case MeasureSpec.EXACTLY:
                Log.i(TAG, "getMaxWidth: MeasureSpec.EXACTLY  精确(一般是MATCH_PARENT和固定值)");
                maxWidth = widthSize ;
                break;
            case MeasureSpec.UNSPECIFIED:        //没用过,所以没有写值
                Log.i(TAG, "getMaxWidth: MeasureSpec.UNSPECIFIED ");
                break;
            default:
                break;
        }
        return maxWidth;
    }
    public int getMaxHeight(int heightMode,int heightSize){
        int height = 0 ;
        Log.i(TAG, "getMaxHeight: 高度模式");
        switch (heightMode){
            case MeasureSpec.EXACTLY:
                Log.i(TAG, "getMaxHeight: MeasureSpec.EXACTLY");
                height = heightSize;
                break;
                case MeasureSpec.AT_MOST:
                    Log.i(TAG, "getMaxHeight: MeasureSpec.AT_MOST");
                    //自己计算想要的高度值
                 
                    break;
                case MeasureSpec.UNSPECIFIED:        //没用过
                    Log.i(TAG, "getMaxHeight: MeasureSpec.UNSPECIFIED");
                    break;
        }
        return height;
    }

二、 onLayout()

为View和其子View分配一个大小和位置
自定义ViewGroup则需要实现从ViewGroup中的抽象方法:

ViewGroup.class:

  @Override
    protected abstract void onLayout(boolean changed,
            int l, int t, int r, int b);

而这个方法使用@Override标注,是其是重写View类中的onLayout

View.class:

   /**
     * 当这个View和其子View被分配一个大小和位置时访问layout。
     * 带有子类的派生类应该重写此方法,并在每个子类上调用布局
     * @param changed 当前View的大小和位置被改变了
     * @param left 左边位置(相对于父视图)
     * @param top 顶部位置(相对于父视图)
     * @param right 右边位置(相对于父视图)
     * @param bottom 底部位置(相对于父视图)
     */
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    }

由上面可见,继承ViewGroup时,实现的onLayout方法的参数是该控件相对于父控件的位置,如图理解该位置:

上面是我自己画的一个草图,应该看到还是很清楚的
我这里既然说的是ViewGroup,那么肯定还需要确定其子控件的位置,这就需要我们自己给出子控件的相对于父控件的left,top,right,bottom的值,然后调用:child.layout(left,top,right,bottom);
例:

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.i(TAG, "onLayout: ################################");
        Log.i(TAG, "onLayout: l="+l+",t="+t+",r="+r+",b="+b);
        int height = b - t;        //父控件本身的高
        Log.i(TAG, "onLayout: 控件的高 = " + height );
        int left = getPaddingLeft();
        int top = getPaddingTop();
        int childNum = getChildCount();
        for (int i = 0; i < childNum; i++) {
            View child = getChildAt(i);
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();
            Log.i(TAG, "onLayout: left=" + left + ",top="+ (height/2 - childHeight/2) + ",right="+(left+childWidth) + ",bottom="+ ( height/2 + childHeight/2) );
//            child.layout(left, top, left + childWidth, b);
            child.layout(left, height/2 - childHeight/2, left + childWidth, height/2 + childHeight/2);
            left += (childWidth + mHorizontalSpace);
        }
    }

本文中的代码,仅做简单的思路学习参考,还是需要以实际需求为主;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容