自定义控件详解(五):onMeasure()、onLayout()

前言:

  自定义控件的三大方法:

测量: onMeasure():  测量自己的大小,为正式布局提供建议 
布局: onLayout():   使用layout()函数对所有子控件布局
绘制: onDraw():     根据布局的位置绘图 

      onDraw() 里面是绘制的操作,可以看下其他的文章,下面来了解 onMeasure()和onLayout()方法。

一、onMeasure()、测量

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  

参数即父类传过来的两个宽高的"建议值",即把当前view的高设置为:heightMeasureSpec ;宽设置为:widthMeasureSpec

这个参数不是简单的整数类型,而是2位整数(模式类型)和30位整数(实际数值) 的组合

其中模式分为三种:

①、UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;UNSPECIFIED=00000000000000000000000000000000

②、EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;EXACTLY =01000000000000000000000000000000

获取模式int值 和 获取数值int值的方法:

int measureWidth = MeasureSpec.getSize(widthMeasureSpec);  
int measureHeight = MeasureSpec.getSize(heightMeasureSpec);  
int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);  
int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);

模式的值有:

MeasureSpec.AT_MOST       = 2
MeasureSpec.EXACTLY       = 1
MeasureSpec.UNSPECIFIED   = 0

上面我们知道了 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法参数的意义

下面了解参数对应的三个模式分别对应的意义:

每一个模式都对应的xml布局中的一个值

wrap_content   --- MeasureSpec.AT_MOST
match_parent   --- MeasureSpec.EXACTLY
具体值          --- MeasureSpec.UNSPECIFIED

注意:当模式是MeasureSpec.AT_MOST时,即wrap_content时,需要将大小设置一个数值。

二、onLayout() 、 布局

首先先了解几个需要用到的方法:

  (1)、

      这个方法和onMeasure()方法类似。其实这个方法的作用就是 设置当前View的宽高。

  (2)、

      这个方法就和

方法类似了,不过少了第一个参数boolean changed

      这个方法的目的是用于当前ViewGroup中的子控件的布局

  再看

方法,只要是继承ViewGroup的类都必须要重写该方法,来实现该控件内部子控件的布局情况。

  我们写一个自定义类继承ViewGroup实现Linearlayout垂直排列的效果看下:

  public class XViewGroup extends ViewGroup{
    public XViewGroup(Context context) {
        super(context);
    }
    public XViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public XViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
        int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
     
      
     // 计算所有子控件需要用到的宽高
        int height = 0;              //记录根容器的高度
        int width = 0;               //记录根容器的宽度
        int count = getChildCount(); //记录容器内的子控件个数
        for (int i=0;i<count;i++) {
            //测量子控件
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            //获得子控件的高度和宽度
            int childHeight = child.getMeasuredHeight();
            int childWidth = child.getMeasuredWidth();
            //得到最大宽度,并且累加高度
            height += childHeight;
            width = Math.max(childWidth, width);
        }
     // 设置当前View的宽高
        setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY) ? measureWidth: width, (measureHeightMode == MeasureSpec.EXACTLY) ? measureHeight: height);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int top = 0;
        int count = getChildCount();
        for (int i=0;i<count;i++) {
            View child = getChildAt(i);
            int childHeight = child.getMeasuredHeight();
            int childWidth = child.getMeasuredWidth();
            //该子控件在父容器的位置  , 高度是之前所有子控件的高度和开始 ,从上往下排列,就实现了类似Linearlayout布局垂直排列的布局
            child.layout(0, top, childWidth, top + childHeight); //以父容器左上角为原点进行布局
            top += childHeight;
        }
    }
    
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏懒人开发

鸿洋AutoLayout代码分析(三):入口类分析

我们可以发现,只是做了name的判断, 如果是属性中的3中Layout, 会自动替换成 对应的AutoXXXLayout 如果不是,直接调用父类的 View...

762
来自专栏Android开发指南

9.代码抽取(adapter)

2569
来自专栏技术小黑屋

Android中Java和JavaScript交互

Android提供了一个很强大的WebView控件用来处理Web网页,而在网页中,JavaScript又是一个很举足轻重的脚本。本文将介绍如何实现Java代码和...

792
来自专栏mukekeheart的iOS之旅

onclicklistener到底怎么用?

相信很多像我一样的新手学习Android开发会遇到这个问题,通过这几天的归类和总结,将我的理解写在下面,欢迎大家一起前来讨论: 以按钮BUTTON的监听事件为例...

2466
来自专栏吴小龙同學

Drawable /Bitmap、String/InputStream、Bitmap/byte[]互转

Drawable互转Bitmap Drawable转Bitmap 1 2 3 4Resources res = getResources(); Drawable...

3156
来自专栏程序员叨叨叨

听说你想玩RecyclerView嵌套GridView

有很多小伙伴们可能会遇到这样的问题: 为什么不论我传入多大size的List,我的GridView只能显示一行?

1912
来自专栏双十二技术哥

Android LayoutInflater Factory 源码解析

在上一篇文章《Android LayoutInflater 源码解析》中我们说到 View 的 inflate 中有一个方法 createViewFromTag...

822
来自专栏我就是马云飞

View的绘制流程之MeasureSpec

目的 我在一个多月之前就说我准备开始梳理基础的事,好吧,我承认这一个月没我怎么梳理。或者梳理的不多,当我梳理到view的时候,发现需要分成绘制流程以及事件分发进...

1769
来自专栏androidBlog

自定义Drawable实现圆角图片和圆形图片

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

562
来自专栏成长道路

XStream将XML映射到对对象

<?xml version="1.0"  encoding="UTF-8"?> <address-book> <contacts>     <contact...

2050

扫码关注云+社区