自定义控件三部曲视图篇(一)——测量与布局

本文详细介绍了自定义布局控件的测量与布局过程,包括ViewGroup的绘制流程、onMeasure()与MeasureSpec的使用,以及onLayout()的实现。通过实例分析了如何在自定义控件中处理测量、布局以及处理子控件的Margin。文章旨在帮助开发者深入理解Android自定义布局的工作原理。
摘要由CSDN通过智能技术生成

前言:生命总是要有信仰,有梦想才能一直前行,哪怕走的再慢,也是在前行。

 

 

相关文章:

Android自定义控件三部曲文章索引》http://blog.csdn.net/harvic880925/article/details/50995268

 

今天给大家讲讲有关自定义布局控件的问题,大家来看这样一个需求,你需要设计一个container,实现内部控件自动换行。即里面的控件能够根据长度来判断当前行是否容得下它,进而决定是否转到下一行显示。效果图如下

在上图中,所有的紫色部分是FlowLayout控件,明显可以看出,内部的每个TextView控件,可以根据大小自动排列。
效果图就是这样子了,第一篇先讲下预备知识。

一、ViewGroup绘制流程

注意,View及ViewGroup基本相同,只是在ViewGroup中不仅要绘制自己还是绘制其中的子控件,而View则只需要绘制自己就可以了,所以我们这里就以ViewGroup为例来讲述整个绘制流程。
绘制流程分为三步:测量、布局、绘制
分别对应:onMeasure()、onLayout()、onDraw()

其中,他们三个的作用分别如下:
onMeasure():测量自己的大小,为正式布局提供建议。(注意,只是建议,至于用不用,要看onLayout);
onLayout():使用layout()函数对所有子控件布局;
onDraw():根据布局的位置绘图;

有关绘图的部分,大家可以参考我的系列博客《android Graphics(一):概述及基本几何图形绘制》共有四篇,讲述了有关android 绘图的90%内容,大家可以参考。
这篇文章着重将内容放在分析onMeasure()和onLayout()上。

 

 

二、onMeasure与MeasureSpec

布局绘画涉及两个过程:测量过程和布局过程。测量过程通过measure方法实现,是View树自顶向下的遍历,每个View在循环过程中将尺寸细节往下传递,当测量过程完成之后,所有的View都存储了自己的尺寸。第二个过程则是通过方法layout来实现的,也是自顶向下的。在这个过程中,每个父View负责通过计算好的尺寸放置它的子View。
前面讲过,onMeasure()是用来测量当前控件大小的,给onLayout()提供数值参考,需要特别注意的是:测量完成以后通过setMeasuredDimension(int,int)设置给系统。

1、onMeasure

首先,看一下onMeasure()的声明:

 

 

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

这里我们主要关注传进来的两个参数:int widthMeasureSpec, int heightMeasureSpec
与这两个参数有关的是两个问题:意义和组成。即他们是怎么来的,表示什么意思;还有,他们是组成方式是怎样的。
我们先说他们的意义:
他们是父类传递过来给当前view的一个建议值,即想把当前view的尺寸设置为宽widthMeasureSpec,高heightMeasureSpec
有关他们的组成,我们就直接转到MeasureSpec部分。

2、MeasureSpec

虽然表面上看起来他们是int类型的数字,其实他们是由mode+size两部分组成的。
widthMeasureSpec和heightMeasureSpec转化成二进制数字表示,他们都是32位的。前两位代表mode(测量模式),后面30位才是他们的实际数值(size)。
(1)模式分类
它有三种模式:
①、UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
②、EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
③、AT_MOST(至多),子元素至多达到指定大小的值。

他们对应的二进制值分别是:
UNSPECIFIED=00000000000000000000000000000000
EXACTLY =01000000000000000000000000000000
AT_MOST =10000000000000000000000000000000
由于最前面两位代表模式,所以他们分别对应十进制的0,1,2;
(2)模式提取
现在我们知道了widthMeasureSpec和heightMeasureSpec是由模式和数值组成的,而且二进制的前两位代表模式,后28位代表数字。
我们先想想,如果我们自己来提取widthMeasureSpec和heightMeasureSpec中的模式和数值是怎么提取呢?
首先想到的肯定是通过MASK和与运算去掉不需要的部分而得到对应的模式或数值。
说到这大家可能会迷茫,我们写段代码来提取模式部分吧:

 

 

//对应11000000000000000000000000000000;总共32位,前两位是1
int MODE_MASK  = 0xc0000000;

//提取模式
public static int getMode(int measureSpec) {
    return (measureSpec & MODE_MASK);
}
//提取数值
public static int getSize(int measureSpec) {
    return (measureSpec & ~MODE_MASK);
}

相信大家看了代码就应该清楚模式和数值提取的方法了吧,主要用到了MASK的与、非运算,难度不大,如果有问题,自行谷歌一下与、非运算方法吧。
(3)、MeasureSpec
上面我们自已实现了模式和数值的提取。但在强大的andorid面前,肯定有提供提取模式和数值的类。这个类就是MeasureSpec
下面两个函数就可以实现这个功能:

MeasureSpec.getMode(int spec) //获取MODE
MeasureSpec.getSize(int spec) //获取数值

另外MODE的取值为:

MeasureSpec.AT_MOST
MeasureSpec.EXACTLY
MeasureSpec.UNSPECIFIED

通过下面的代码就可以分别获取widthMeasureSpec和heightMeasureSpec的MODE和数值

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

其实大家通过查看代码可以知道,我们的实现就是MeasureSpec.getSize()和MeasureSpec.getMode()的实现代码。
(4)、模式有什么用呢
我们知道这里有三个模式:EXACTLY、AT_MOST、UNSPECIFIED
需要注意的是widthMeasureSpec和heightMeasureSpec各自都有它对应的模式,模式的由来分别来自于XML定义:
简单来说,XML布局和模式有如下对应关系:

  • wrap_content-> MeasureSpec.AT_MOST
  • match_parent -> MeasureSpec.EXACTLY
  • 具体值 -> MeasureSpec.EXACTLY

 

例如,下面这个XML

<com.example.harvic.myapplication.FlowLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
     
</com.example.harvic.myapplication.FlowLayout>

那FlowLayout在onMeasure()中传值时widthMeasureSpec的模式就是 MeasureSpec.EXACTLY,即父窗口宽度值。heightMeasureSpec的模式就是 MeasureSpec.AT_MOST,即不确定的。

一定要注意是,当模式是MeasureSpec.EXACTLY时,我们就不必要设定我们计算的大小了,因为这个大小是用户指定的,我们不应更改。但当模式是MeasureSpec.AT_MOST时,也就是说用户将布局设置成了wrap_content,我们就需要将大小设定为我们计算的数值,因为用户根本没有设置具体值是多少࿰

评论 48
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值