发觉自己在进行Android开发的时候发觉自己开发的APP的UI几乎是由第三方View库(比如 android-pulltorefresh,ActionBar-PullToRefresh)等等堆砌出来的,发觉自己居然没有自定义过一个控件。心好痛:)
本文是一篇对于Android自定义View时继承View 并实现 onMeasure() 方法的理解。
为什么要写这篇文章为了避免以后自定义View的时候掉坑。
我在国内搜了相关文章(=_=!英语实在拙计),居然发觉理解起来非常生硬,相同的文章也特别多(没错我就是在吐槽天朝的技术贴,尼玛一搜前几篇帖子都是一模一样)
帮助自己理解以及游客们0.0
问题为什麼要实现onMeasure()。
什么情况下需要实现onMeasure()。
实现onMeasure()有什么作用。
调用父类onMeasure()
首先定义一个类继承View,重写onMeasure(),并调用父类onMeasure()方法。public class MeasureExampleView extends View {
public MeasureExampleView(Context context) {
super(context);
}
public MeasureExampleView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
再定义layout文件,使用MeasureExampleView并设置background,margin与父容器区分。<?xml version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="match_parent">
android:background="@android:color/holo_blue_dark"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
产生效果如下,不难发现MeasureExampleView充满了父控件
修改layout文件中的MeasureExampleView layout_width以及layout_height属性修改成wrap_content,结果仍然是一样的,就不贴图了。
修改layout文件中LinearLayout(父容器) layout_width以及layout_height属性修改成固定的值比如50dp,产生效果如下。发现 MeasureExampleView 跟随了LinearLayout(父容器)设置的固定大小(50dpx50dp)。
修改layout文件中MeasureExampleView layout_width以及layout_height属性修改成固定的值比如50dp,产生效果如下,发现 MeasureExampleView 的大小为设定的值 50dp x 50dp
结论
重写onMeasure(),并调用父类onMeasure()时MeasureExampleView的layout_width以及layout_height属性值 match_parent 或者 wrap_content显示大小由其父容器控件决定。
MeasureExampleView设置为固定的值,就显示该设定的值
怎样重写onMeasure()
示例//widthMeasureSpec 和 heightMeasureSpec的值 由父容器决定
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureDimension(DEFAULT_WIDTH, widthMeasureSpec);
int height = measureDimension(DEFAULT_HEIGHT, heightMeasureSpec);
setMeasuredDimension(width, height);
}
定义一个方法处理 widthMeasureSpec,heightMeasureSpec的值private int measureHanlder(int measureSpec){
int result = defaultSize;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(defaultSize, specSize);
} else {
result = defaultSize;
}
return result;
}
说明
MeasureSpec.getSize()会解析MeasureSpec值得到父容器width或者height。
MeasureSpec.getMode()会得到三个int类型的值分别为:MeasureSpec.EXACTLY MeasureSpec.AT_MOST,MeasureSpec.UNSPECIFIED。
MeasureSpec.UNSPECIFIED 未指定,所以可以设置任意大小。
MeasureSpec.AT_MOST MeasureExampleView可以为任意大小,但是有一个上限。比如这种情况<?xml version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="match_parent">
android:background="@android:color/holo_blue_dark"
android:layout_margin="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
MeasureSpec.EXACTLY 父容器为MeasureExampleView决定了一个大小,MeasureExampleView大小只能在这个父容器限制的范围之内。
比如这种情况:<?xml version="1.0" encoding="utf-8"?>
android:layout_width="50dp"
android:layout_height="50dp">
android:background="@android:color/holo_blue_dark"
android:layout_margin="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
又或者是这种情况:<?xml version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="match_parent">
android:background="@android:color/holo_blue_dark"
android:layout_margin="10dp"
android:layout_width="50dp"
android:layout_height="50dp" />
这样当你使用 android:layout_width|android:layout_height属性为:wrap_content时,MeasureView的大小为默认值 100dpx100dp,你也可以根据自己的规则去重写onMeasure()方法。
小结重写onMeasure()方法是为了自定义View尺寸的规则
如果你的自定义View的尺寸是根据父控件行为一致,就不需要重写onMeasure()方法