这篇博客主要介绍的是怎样自定义一个可以指定最大宽度,高度,以及宽高比的 Layout。原理其实很简单,就是通过重写 onMeasure 方法,重新制定 MeasureSpec。
常用的自定义属性
<attr name="ml_maxWidth" format="dimension" />
<attr name="ml_maxheight" format="dimension" />
<attr name="ml_ratio" format="float" />
<attr name="ml_ratio_standard">
<enum name="w_h" value="1" />
<enum name="h_w" value="2" />
</attr>
</declare-styleable>
key | 含义 | 补充说明 |
---|---|---|
ml_maxWidth | 最大宽度 | |
ml_maxheight | 最大高度 | |
ml_ratio_standard | 指定比例的模式,即是宽高比还是高宽比 | w_h,宽高比, h_w 高宽比 |
ml_ratio | 比例值 | 只有比例模式是 w_h 或者 h_w,该值才会生效 |
指定最大宽度,最大高度,我们值需要使用 ml_maxWidth,ml_maxheight 属性即可,当然我们也可以同时指定最大宽度和最大高度。如下
<com.xj.maxlayout.MaxLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginTop="15dp"
android:background="@android:color/holo_blue_light"
app:ml_maxWidth="200dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="10dp"
android:text="指定最大宽度,指定最大宽度,指定最大宽度" />
</com.xj.maxlayout.MaxLayout>
<com.xj.maxlayout.MaxLayout
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:background="@android:color/holo_blue_light"
app:ml_maxheight="200dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:padding="10dp"
android:text="指定最大高度" />
</com.xj.maxlayout.MaxLayout>
<com.xj.maxlayout.MaxLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:background="@android:color/holo_blue_light"
app:ml_maxWidth="200dp"
app:ml_maxheight="150dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="10dp"
android:text="同时指定最大宽度和最大高度" />
</com.xj.maxlayout.MaxLayout>
指定宽高比,我们需要设置两个属性,ml_ratio_standard 和 ml_ratio。ml_ratio_standard 有两个值,w_h 代表已宽度为基准,h_w 代表已高度为基准。
比如,我们要指定高度是宽度的某个比例的时候,如,高度是宽度的两倍,可以这样写
<com.xj.maxlayout.MaxLayout
android:id="@+id/ml_1"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
app:ml_ratio="2.0"
app:ml_ratio_standard="w_h">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/jsy03" />
</com.xj.maxlayout.MaxLayout>
比如,我们要指定宽度是高度的某个比例的时候,如,宽度是高度的 0.8,可以这样写
<com.xj.maxlayout.MaxLayout
android:id="@+id/ml_2"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginLeft="20dp"
android:layout_toRightOf="@id/ml_1"
android:background="@android:color/holo_blue_light"
app:ml_ratio="0.8"
app:ml_ratio_standard="h_w">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/jsy04" />
</com.xj.maxlayout.MaxLayout>
当然,也可以同时指定比例和最大宽度,高度。
<com.xj.maxlayout.MaxLayout
android:id="@+id/ml_03"
android:layout_width="match_parent"
android:layout_height="220dp"
android:layout_below="@id/ml_1"
android:layout_marginTop="15dp"
android:background="@android:color/holo_blue_light"
app:ml_maxWidth="150dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/jsy05" />
</com.xj.maxlayout.MaxLayout>
原理其实很简单,对自定义 View 有基本了解的人都知道,View 的宽度和高度,是在 onMeasure 方法中进行测量的,他们的大小受 MeasureSpec 的影响。既然如此,那么我们在继承 FrameLayout,重写它的 onMeasure 方法。在 onMeasure 方法中根据我们指定的最大宽度,高度和比例对 MeasureSpec 进行调整即可。
思路大概如下
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 是否设置了比例
boolean setRatio = isSetRatio();
// 没有设置最大宽度,高度,宽高比例,不需要调整,直接返回
if (mMaxWidth <= DEF_VALUE && mMaxHeight <= DEF_VALUE && !setRatio) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
// 拿到原来宽度,高度的 mode 和 size
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
Log.d(TAG, "origin onMeasure: widthSize =" + widthSize + "heightSize = " + heightSize);
if (mRatioStandrad == W_H) { // 当模式已宽度为基准
widthSize = getWidth(widthSize);
if (mRatio >= 0) {
heightSize = (int) (widthSize * mRatio);
}
heightSize = getHeight(heightSize);
int maxHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
int maxWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
super.onMeasure(maxWidthMeasureSpec, maxHeightMeasureSpec);
} else if (mRatioStandrad == H_W) { // 当模式已高度为基准
heightSize = getHeight(heightSize);
if (mRatio >= 0) {
widthSize = (int) (heightSize * mRatio);
}
widthSize = getWidth(widthSize);
int maxHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
int maxWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
super.onMeasure(maxWidthMeasureSpec, maxHeightMeasureSpec);
} else { // 当没有设定比例的时候
widthSize = getWidth(widthSize);
heightSize = getHeight(heightSize);
int maxHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
int maxWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
super.onMeasure(maxWidthMeasureSpec, maxHeightMeasureSpec);
}
Log.d(TAG, "adjust onMeasure: widthSize =" + widthSize + "heightSize = " + heightSize);
}
我们来看一下,有三种模式:
源码到此分析为止
宽高比例的,其实在 2015 的时候,google 已经推出了 PercentFrameLayout,PercentRelativeLayout,可以很好得进行宽高比例的调整。在 API level 26.1.0 的时候,上述的 PercentFrameLayout,PercentRelativeLayout 背标记为过时,并推荐使用 ConstraintLayout。
写这一篇博客,主要是有时候一些旧项目里面,有时候需要设置最大宽度,高度,或者比例,并没有使用最新的一些控件 ConstraintLayout,如果不进行封装,经常需要在代码里面动态设置,这样比较麻烦。再者,这一篇可以帮助大家更好得理解 onMeasue 方法,尤其是 MeasureSpec。即 MeasureSpec 决定了 width, height。想调整 width, height 的话,可以通过调整 MeasureSpec 实现。
同时,这里还有一个坑,如果在代码里面直接设置 width 的话,当 TextView 超过设置的 width 的时候,textView 显示的文字会被截断。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有