Android官方貌似没有按照比例计算自己大小的View,有个按照父控件计算自己大小的百分比View。而往往我们用到自身需要比例的时候还是比较多的,比如显示一张特定照片,宽度match_parent,高度是多少呢,交给ImageView自己测量,要么有白边,要么被部分切割。
其实计算View比例很简单,在View的生命周期中用来计算View大小的的方法是onMeasure,它有两个int参数widthMeasureSpec和heightMeasureSpec,每个分别表示长的大小和模式、宽的大小和模式。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
通过MeasureSpec.getMode
和MeasureSpec.getSize
获取模式和值。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
super.onMeasure(MeasureSpec.makeMeasureSpec(widthSize, widthMode), MeasureSpec.makeMeasureSpec(heightSize, heightMode));
}
Mode分为MeasureSpec.EXACTLY
、MeasureSpec.AT_MOST
、MeasureSpec.UNSPECIFIED
三种,具体意思请百度。我们在布局中如果需要用比例必定长宽有一个是固定值(某dp或者match_parent),所以可以通过上面三种Mode中的MeasureSpec.EXACTLY
、MeasureSpec.AT_MOST
来判别哪一边是固定值。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode != MeasureSpec.UNSPECIFIED) {
// 宽是固定的
heightSize = (int) (widthSize / widthScale * heightScale);
heightMode = MeasureSpec.EXACTLY;
} else if (heightMode != MeasureSpec.UNSPECIFIED) {
// 高是固定的
widthSize = (int) (heightSize / heightScale * widthScale);
widthMode = MeasureSpec.EXACTLY;
}
super.onMeasure(MeasureSpec.makeMeasureSpec(widthSize, widthMode), MeasureSpec.makeMeasureSpec(heightSize, heightMode));
}
但是在Android Studio中预览并不是成比例的,真机正常,可能由于我不是很了解MeasureSpec.UNSPECIFIED
,或者是AS预览的bug。然后我换了另一种方式,通过LayoutParams是否不是WRAP_CONTENT来判断是不是固定。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthParam = getLayoutParams().width;
int heightParam = getLayoutParams().height;
if (widthScale > 0 && heightScale > 0) {
if (widthParam != ViewGroup.LayoutParams.WRAP_CONTENT) {
heightSize = (int) (widthSize / widthScale * heightScale);
heightMode = MeasureSpec.EXACTLY;
} else if (heightParam != ViewGroup.LayoutParams.WRAP_CONTENT) {
widthSize = (int) (heightSize / heightScale * widthScale);
widthMode = MeasureSpec.EXACTLY;
}
}
super.onMeasure(MeasureSpec.makeMeasureSpec(widthSize, widthMode), MeasureSpec.makeMeasureSpec(heightSize, heightMode));
}
这样预览也正常了。
所以最终在布局中引用
<cn.xuzhijun.proportional.ProportionalFrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#123456"
app:scaleHeight="9"
app:scaleWidth="16">
<!--You View-->
</cn.xuzhijun.proportional.ProportionalFrameLayout>
而且高度和宽度只要有一边是固定值或者match_parent就可以了。
很简单。
有需要的可以直接引用。
compile 'cn.xuzhijun:xproportional-layout:1.0.0'