关于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的运行效果不一样;