关于android:继承ViewGroup学习onMeasure和onLayout

32次阅读

共计 4805 个字符,预计需要花费 13 分钟才能阅读完成。

在继承 ViewGroup 类时,须要重写两个办法,别离是 onMeasure 和 onLayout。

1,在办法 onMeasure 中调用 setMeasuredDimension 办法 void android.view.View.setMeasuredDimension(int measuredWidth, int measuredHeight)
在 onMeasure(int, int)中,必须调用 setMeasuredDimension(int width, int height)来存储测量失去的宽度和高度值,如果没有这么去做会触发异样 IllegalStateException。
2,在办法 onMeasure 中调用孩子的 measure 办法

void android.view.View.measure(int widthMeasureSpec, int heightMeasureSpec)

这个办法用来测量出 view 的大小。父 view 应用 width 参数和 height 参数来提供 constraint 信息。实际上,view 的测量工作在 onMeasure(int, int)办法中实现。因而,只有 onMeasure(int, int)办法能够且必须被重写。参数 widthMeasureSpec 提供 view 的程度空间的规格阐明,参数 heightMeasureSpec 提供 view 的垂直空间的规格阐明。

3,解析 onMeasure(int, int)办法

void android.view.View.onMeasure(int widthMeasureSpec, int heightMeasureSpec)

测量 view 及其内容来确定 view 的宽度和高度。这个办法在 measure(int, int)中被调用,必须被重写来准确和无效的测量 view 的内容。

在重写这个办法时,必须调用 setMeasuredDimension(int, int)来存储测量失去的宽度和高度值。执行失败会触发一个 IllegalStateException 异样。调用父 view 的 onMeasure(int, int)是非法无效的用法。

view 的根本测量数据默认取其背景尺寸,除非容许更大的尺寸。子 view 必须重写 onMeasure(int, int)来提供其内容更加精确的测量数值。如果被重写,子类确保测量的 height 和 width 至多是 view 的最小高度和宽度 (通过 getSuggestedMinimumHeight() 和 getSuggestedMinimumWidth()获取)。

4,解析 onLayout(boolean, int, int, int, int)办法

void android.view.ViewGroup.onLayout(boolean changed, int l, int t, int r, int b)

调用场景:在 view 给其孩子设置尺寸和地位时被调用。子 view,包含孩子在内,必须重写 onLayout(boolean, int, int, int, int)办法,并且调用各自的 layout(int, int, int, int)办法。

参数阐明:参数 changed 示意 view 有新的尺寸或地位;参数 l 示意绝对于父 view 的 Left 地位;参数 t 示意绝对于父 view 的 Top 地位;参数 r 示意绝对于父 view 的 Right 地位;参数 b 示意绝对于父 view 的 Bottom 地位。.

5,解析 View.MeasureSpec 类
android.view.View.MeasureSpec
MeasureSpec 对象,封装了 layout 规格阐明,并且从父 view 传递给子 view。每个 MeasureSpec 对象代表了 width 或 height 的规格。
MeasureSpec 对象蕴含一个 size 和一个 mode,其中 mode 能够取以下三个数值之一:
UNSPECIFIED,1073741824 [0x40000000],未加规定的,示意没有给子 view 增加任何规定。
EXACTLY,0 [0x0],准确的,示意父 view 为子 view 确定准确的尺寸。
AT_MOST,-2147483648 [0x80000000],子 view 能够在指定的尺寸内尽量大。

在这里给大家举一个例子 demo:
第一步:自定义一个 View 实现 ViewGroup 接口,即自定义 ViewGroup:

import android.content.Context;  
import android.util.AttributeSet;  
import android.view.View;  
import android.view.ViewGroup;  
  
public class MyViewGroup extends ViewGroup {public MyViewGroup(Context context) {super(context);  
    }  
  
    public MyViewGroup(Context context, AttributeSet attrs) {super(context, attrs);  
    }  
  
    public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);  
    }  
  
    /** 
     * 计算控件的大小 
     */  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
        int measureWidth = measureWidth(widthMeasureSpec);  
        int measureHeight = measureHeight(heightMeasureSpec);  
        // 计算自定义的 ViewGroup 中所有子控件的大小  
        measureChildren(widthMeasureSpec, heightMeasureSpec);  
        // 设置自定义的控件 MyViewGroup 的大小  
        setMeasuredDimension(measureWidth, measureHeight);  
    }  
  
    private int measureWidth(int pWidthMeasureSpec) {  
        int result = 0;  
        int widthMode = MeasureSpec.getMode(pWidthMeasureSpec);// 失去模式  
        int widthSize = MeasureSpec.getSize(pWidthMeasureSpec);// 失去尺寸  
  
        switch (widthMode) {  
        /** 
         * mode 共有三种状况,取值别离为 MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, 
         * MeasureSpec.AT_MOST。*  
         *  
         * MeasureSpec.EXACTLY 是准确尺寸,* 当咱们将控件的 layout_width 或 layout_height 指定为具体数值时如 andorid 
         * :layout_width="50dip",或者为 FILL_PARENT 是,都是控件大小曾经确定的状况,都是准确尺寸。*  
         *  
         * MeasureSpec.AT_MOST 是最大尺寸,* 当控件的 layout_width 或 layout_height 指定为 WRAP_CONTENT 时 
         *,控件大小个别随着控件的子空间或内容进行变动,此时控件尺寸只有不超过父控件容许的最大尺寸即可 
         *。因而,此时的 mode 是 AT_MOST,size 给出了父控件容许的最大尺寸。*  
         *  
         * MeasureSpec.UNSPECIFIED 是未指定尺寸,这种状况不多,个别都是父控件是 AdapterView,* 通过 measure 办法传入的模式。*/  
        case MeasureSpec.AT_MOST:  
        case MeasureSpec.EXACTLY:  
            result = widthSize;  
            break;  
        }  
        return result;  
    }  
  
    private int measureHeight(int pHeightMeasureSpec) {  
        int result = 0;  
  
        int heightMode = MeasureSpec.getMode(pHeightMeasureSpec);  
        int heightSize = MeasureSpec.getSize(pHeightMeasureSpec);  
  
        switch (heightMode) {  
        case MeasureSpec.AT_MOST:  
        case MeasureSpec.EXACTLY:  
            result = heightSize;  
            break;  
        }  
        return result;  
    }  
  
    /** 
     * 覆写 onLayout,其目标是为了指定视图的显示地位,办法执行的前后程序是在 onMeasure 之后,因为视图必定是只有晓得大小的状况下,* 能力确定怎么摆放 
     */  
    @Override  
    protected void onLayout(boolean changed, int l, int t, int r, int b) {  
        // 记录总高度  
        int mTotalHeight = 0;  
        // 遍历所有子视图  
        int childCount = getChildCount();  
        for (int i = 0; i < childCount; i++) {View childView = getChildAt(i);  
  
            // 获取在 onMeasure 中计算的视图尺寸  
            int measureHeight = childView.getMeasuredHeight();  
            int measuredWidth = childView.getMeasuredWidth();  
  
            childView.layout(l, mTotalHeight, measuredWidth, mTotalHeight  
                    + measureHeight);  
  
            mTotalHeight += measureHeight;  
  
        }  
    }  
  
}

第二步,布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:background="#00f0f0"  
    tools:context=".MainActivity" >  
  
    <net.loonggg.viewgroup.MyViewGroup  
        android:id="@+id/myViewGroup"  
        android:layout_width="480dp"  
        android:layout_height="300dp"  
        android:background="#0f0f0f" >  
  
        <TextView  
            android:layout_width="200dp"  
            android:layout_height="100dp"  
            android:background="#000000"  
            android:gravity="center"  
            android:text="第一个 TextView" />  
  
        <TextView  
            android:layout_width="100dp"  
            android:layout_height="200dp"  
            android:background="#ffffff"  
            android:gravity="center"  
            android:text="第二个 TextView" />  
    </net.loonggg.viewgroup.MyViewGroup>  
  
</RelativeLayout>

相干视频
ViewGroup 的 layout 布局过程

正文完
 0