关于android.view.View中的onMeasure方法

转载自:http://hi.baidu.com/huareal/item/0cc8e5611e3e8d9ec5d24980

最近在完善一个连连看的游戏,想在原始的单一个View的基础上增加top和bottom区域,基本设计思路为:

Top:Logo,showprogressbar高度40

Center:showlinkicons高度400

bottom:showhelpandotherbutton高度40

对应的layout格式如下:

<?xmlversion="1.0"encoding="utf-8"?>

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"android:layout_height="fill_parent"

android:layout_width="fill_parent">

<LinearLayout

android:layout_width="fill_parent"

android:id="@+id/topLayout"android:orientation="horizontal"android:layout_height="wrap_content">

<ImageViewandroid:id="@+id/topLogo"

android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/toplogog"></ImageView>

</LinearLayout>

<LinearLayout

android:id="@+id/bodyLayout"android:orientation="horizontal"android:layout_width="wrap_content"android:layout_height="wrap_content">

<org.huareal.game.linkchess.view.GameView

android:layout_width="fill_parent"android:id="@+id/gameView"android:layout_height="fill_parent"/>

</LinearLayout>

<LinearLayout

android:id="@+id/bottomLayout"android:orientation="horizontal"android:layout_height="fill_parent"android:layout_width="fill_parent">

<ImageButtonandroid:src="@drawable/help"android:id="@+id/helpButton"

android:layout_width="32px"android:layout_height="32px">

</ImageButton>

<TextViewandroid:layout_width="fill_parent"

android:layout_height="wrap_content"android:text="连连看"/>

<TextViewandroid:id="@+id/text"android:text="开始游戏"

android:visibility="invisible"android:layout_width="wrap_content"

android:layout_height="wrap_content"android:layout_centerInParent="true"

android:gravity="center_horizontal"android:textColor="#ff8888ff"

android:textSize="24sp"/>

</LinearLayout>

</LinearLayout>

本来的这样设计,但是运行时,bottom区域的button显示不全,尝试了很多中做法,调整中间的自定义

org.huareal.game.linkchess.view.GameView的显示图标各数也不解决问题

在设计器发现,center区域的LinearLayout布局明显的高度超过了400,也许需要调整对应的GameView的实际高度

首先调整中间的LinearLayout的属性android:layout_width="wrap_content"android:layout_height="wrap_content"

然后在GameView类中增加对方法onMeasure的设置

@Override

protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){

}

该方法的设置很关键

第一次尝试

@Override

protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){

super.onMeasure(widthMeasureSpec,heightMeasureSpec);

intparentWidth=MeasureSpec.getSize(widthMeasureSpec);

intparentHeight=MeasureSpec.getSize(heightMeasureSpec);

this.setMeasuredDimension(parentWidth,parentHeight-40);

}

发现运行时仍然无法正常显示中间的GameView中的Icon,如果将this.setMeasuredDimension(parentWidth,parentHeight-40);改为

this.setMeasuredDimension(parentWidth,parentHeight-20);

GameView显示正常了,但是下面的按钮还是显示不全;

从网上搜集了一大把,终于发现,这样写代码:

@Override

protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){

//super.onMeasure(widthMeasureSpec,heightMeasureSpec);

intparentWidth=MeasureSpec.getSize(widthMeasureSpec);

intparentHeight=MeasureSpec.getSize(heightMeasureSpec);

this.setMeasuredDimension(parentWidth,parentHeight-40);

this.setLayoutParams(newLinearLayout.LayoutParams(parentWidth,parentHeight-40));

super.onMeasure(widthMeasureSpec,heightMeasureSpec);

}

结果运行正常了

总结一下onMeasure的特殊:

callingsuper.onMeasurepriortothesetMeasuredDimensionwillmeasureallthechildrenintheviewbasedontheoriginalsize,thenjustcuttheviewinhalfwhenthesetMeasuredDimensionresizesit

如果先调用onMeasure在setMeasuredDimension之前的话,将会基于最初的大小来measure所有的子节点,然后会在setMeasuredDimension进行resize时减去一半

Instead,youneedtocallsetMeasuredDimension(asrequiredforanonMeasureoverride)andprovideanewLayoutParamsforyourview,thencallsuper.onMeasure.Remember,yourLayoutParamsarederivedfromyourview'sparenttype,notyourview'stype

对于的意思就是,先调用setMeasuredDimension,然后

设置一个LayoutParams给你的VIew,再调用super.onMeasure

实践看来真的如此啊。

补充上述的onMeasure方法在模拟器和真机上的差异:

上述的测试画面是在模拟器上的效果,但是到了真机上后,bottom所在的控制栏仍然不能够显示;

思索了好久,在eclipse的设计器中,一直看不到最下面的LinearLayout,默认在真机上的效果跟此有关系;

尝试修改onMeasure如下:

@Override

protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){

//super.onMeasure(widthMeasureSpec,heightMeasureSpec);

intparentWidth=MeasureSpec.getSize(widthMeasureSpec);

intparentHeight=MeasureSpec.getSize(heightMeasureSpec);

this.setMeasuredDimension(parentWidth,parentHeight-40);

this.setLayoutParams(newLinearLayout.LayoutParams(parentWidth,parentHeight-40));

super.onMeasure(widthMeasureSpec,heightMeasureSpec-40);

}

将后面的super.onMeasure的高度修改一下,竟然在Eclipse的设计器中能够看到完整的几个Layout的布局

在模拟器运行,发现中间的LinearLayout部分被挡住了,但是真机上运行正常了;

有些问题看来在模拟器和真机上还是有差异的,补充一下,同样的代码模拟器针对android2.1和2.3的运行效果不一样;

相关推荐