您的位置:首页 > 移动开发 > Android开发

【转】android中onMeasure初看,深入理解布局之一!

2013-11-19 17:59 369 查看
今天学习android自定义组件:docs/guide/topics/ui/custom-components.html

其中有两个对布局界面影响很的方法,onDraw(),和onMeasure().

onDraw()比较好理解.onMeasure()就比较难理解一些,也更复杂些,引用文档中的说法就是:

onMeasure()isalittlemoreinvolved.
其实还有另一个方面的原因就是我对这个单词measure不是很知道,然后果了下词典,就放了下心,确实是测量的意思.

实现onMeasure()方法基本需要完成下面三个方面的事情(最终结果是你自己写相应代码得出测量值并调用view的一个方法进行设置,告诉给你的view安排位置大小的父容器你要多大的空间.):

1.传递进来的参数,widthMeasureSpec,和heightMeasureSpec是你对你应该得出来的测量值的限制.

TheoveriddenonMeasure()methodiscalledwithwidthandheightmeasurespecifications(widthMeasureSpecandheightMeasureSpecparameters,bothareintegercodesrepresentingdimensions)whichshouldbetreatedasrequirementsfortherestrictionsonthewidth
andheightmeasurementsyoushouldproduce.

2.你在onMeasure计算出来设置的width和height将被用来渲染组件.应当尽量在传递进来的width和height声明之间.

虽然你也可以选择你设置的尺寸超过传递进来的声明.但是这样的话,父容器可以选择,如clipping,scrolling,或者抛出异常,或者(也许是用新的声明参数)再次调用onMeasure()

Yourcomponent'sonMeasure()methodshouldcalculateameasurementwidthandheightwhichwillberequiredtorenderthecomponent.itshouldtrytostaywithinthespecifiedpassedin.althoughitcanchoosetoexceedthem(inthiscase,theparentcanchoose
whattodo,includingclipping,scrolling,throwinganexcption,oraskingtheonMeasuretotryagain,perhapswithdifferentmeasurementspecifications).

3.一但width和height计算好了,就应该调用View.setMeasuredDimension(intwidth,intheight)方法,否则将导致抛出异常.

Oncethewidthandheightarecalculated,thesetMeasureDimension(intwidth,intheight)methodmustbecalledwiththecalculatedmeasurements.Failuretodothiswillresultinanexceptiionbeingthrown

在Android提提供的一个自定义View示例中(在APIdemos中的view/LabelView)可以看到一个重写onMeasure()方法的

实例,也比较好理解.

01
/**
02
*
@seeandroid.view.View#measure(int,int)
03
*/
04
@Override
05
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec)
{
06
setMeasuredDimension(measureWidth(widthMeasureSpec),
07
measureHeight(heightMeasureSpec));
08
}
09
10
/**
11
*
Determinesthewidthofthisview
12
*
@parammeasureSpecAmeasureSpecpackedintoanint
13
*
@returnThewidthoftheview,honoringconstraintsfrommeasureSpec
14
*/
15
private
int
measureWidth(
int
measureSpec)
{
16
int
result
=
0
;
17
int
specMode
=MeasureSpec.getMode(measureSpec);
18
int
specSize
=MeasureSpec.getSize(measureSpec);
19
20
if
(specMode
==MeasureSpec.EXACTLY){
21
//
Weweretoldhowbigtobe
22
result
=specSize;
23
}
else
{
24
//
Measurethetext
25
result
=(
int
)
mTextPaint.measureText(mText)+getPaddingLeft()
26
+
getPaddingRight();
27
if
(specMode
==MeasureSpec.AT_MOST){
28
//
RespectAT_MOSTvalueifthatwaswhatiscalledforbymeasureSpec
29
result
=Math.min(result,specSize);
30
}
31
}
32
33
return
result;
34
}
直接看measureWidth()

首先看到的是参数,分别代表宽度和高度的MeasureSpec

android2.2文档中对于MeasureSpec中的说明是:

一个MeasureSpec封装了从父容器传递给子容器的布局需求.

每一个MeasureSpec代表了一个宽度,或者高度的说明.

一个MeasureSpec是一个大小跟模式的组合值.一共有三种模式.

AMeasureSpecencapsulatesthelayoutrequirementspassedfromparenttochildEachMeasureSpecrepresentsarequirementforeitherthewidthortheheight.AMeasureSpeciscompsizedofasizeandamode.Therearethreepossiblemodes:

(1)UPSPECIFIED:父容器对于子容器没有任何限制,子容器想要多大就多大.

UNSPECIFIEDTheparenthasnotimposedanyconstraintonthechild.Itcanbewhateversizeitwants

(2)EXACTLY

父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间.

EXACTLYTheparenthasdeterminedandexactsizeforthechild.Thechildisgoingtobegiventhoseboundsregardlessofhowbigitwantstobe.

(3)AT_MOST

子容器可以是声明大小内的任意大小.

AT_MOSTThechildcanbeaslargeasitwantsuptothespecifiedsize

MeasureSpec是View类下的静态公开类,MeasureSpec中的值作为一个整型是为了减少对象的分配开支.此类用于

将size和mode打包或者解包为一个整型.

MeasureSpecsareimplementedasintstoreduceobjectallocation.Thisclassisprovidedtopackandunpackthesize,modetupleintotheint

我比较好奇的是怎么样将两个值打包到一个int中,又如何解包.

MeasureSpec类代码如下:(注释已经被我删除了,因为在上面说明了.)

01
public
static
class
MeasureSpec
{
02
private
static
final
int
MODE_SHIFT
=
30
;
03
private
static
final
int
MODE_MASK
=
0x3
<<
MODE_SHIFT;
04
05
public
static
final
int
UNSPECIFIED
=
0
<<
MODE_SHIFT;
06
public
static
final
int
EXACTLY
=
1
<<
MODE_SHIFT;
07
public
static
final
int
AT_MOST
=
2
<<
MODE_SHIFT;
08
09
public
static
int
makeMeasureSpec(
int
size,
int
mode)
{
10
return
size
+mode;
11
}
12
public
static
int
getMode(
int
measureSpec)
{
13
return
(measureSpec
&MODE_MASK);
14
}
15
public
static
int
getSize(
int
measureSpec)
{
16
return
(measureSpec
&~MODE_MASK);
17
}
}
我无聊的将他们的十进制值打印出来了:

mode_shift=30,mode_mask=-1073741824,UNSPECIFIED=0,EXACTLY=1073741824,AT_MOST=-2147483648

然后觉得也应该将他们的二进制值打印出来,如下:

mode_shift=11110,//30

mode_mask=11000000000000000000000000000000,

UNSPECIFIED=0,

EXACTLY=1000000000000000000000000000000,

AT_MOST=10000000000000000000000000000000

1
MODE_MASK
=
0x3
<<
MODE_SHIFT
//也就是说MODE_MASK是由11左移30位得到的.因为Java用补码表示数值.最后得到的值最高位是1所以就是负数了
1
对于上面的数值我们应该这样想,不要把0x3看成3而要看成二进制的11,

而把MODE_SHIFF就看成30.那为什么是二进制的11呢?

呢,因为只有三各模式,如果有四种模式就是111了因为111三个位才可以有四种组合对吧.

我们这样来看,

UNSPECIFIED=00000000000000000000000000000000,

EXACTLY=01000000000000000000000000000000,

AT_MOST=10000000000000000000000000000000

也就是说,0,1,2

对应00,01,10

当跟11想与时00&11还是得到00,11&01->01,10&

我觉得到了这个份上相信,看我博客的也都理解了.

return(measureSpec&~MODE_MASK);应该是return(measureSpec&(~MODE_MASK));

转自:/article/3475205.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: