Android 自定义最大宽度,高度, 宽高比例 Layout

news/2024/10/17 22:17:29/

前言

这篇博客主要介绍的是怎样自定义一个可以指定最大宽度,高度,以及宽高比的 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 属性即可,当然我们也可以同时指定最大宽度和最大高度。如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vTianQOh-1678031745314)(https://wx3.sinaimg.cn/mw690/9fe4afa0gy1g0lyz2uctnj20ah0hidg7.jpg)]

<com.xj.maxlayout.MaxLayoutandroid:layout_width="match_parent"android:layout_height="100dp"android:layout_marginTop="15dp"android:background="@android:color/holo_blue_light"app:ml_maxWidth="200dp"><TextViewandroid: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.MaxLayoutandroid:layout_width="200dp"android:layout_height="match_parent"android:layout_marginTop="15dp"android:background="@android:color/holo_blue_light"app:ml_maxheight="200dp"><TextViewandroid: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.MaxLayoutandroid: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"><TextViewandroid: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.MaxLayoutandroid: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"><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"android:src="@mipmap/jsy03" /></com.xj.maxlayout.MaxLayout>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tAokbs8N-1678031745315)(https://wx4.sinaimg.cn/mw690/9fe4afa0ly1g0lz6mgyl7j205208ygmt.jpg)]

比如,我们要指定宽度是高度的某个比例的时候,如,宽度是高度的 0.8,可以这样写

<com.xj.maxlayout.MaxLayoutandroid: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"><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"android:src="@mipmap/jsy04" /></com.xj.maxlayout.MaxLayout>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LexIZrhc-1678031747769)(null)]

当然,也可以同时指定比例和最大宽度,高度。

<com.xj.maxlayout.MaxLayoutandroid: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"><ImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"android:src="@mipmap/jsy05" /></com.xj.maxlayout.MaxLayout>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8r5zAa0j-1678031747674)(null)]


原理介绍

原理其实很简单,对自定义 View 有基本了解的人都知道,View 的宽度和高度,是在 onMeasure 方法中进行测量的,他们的大小受 MeasureSpec 的影响。既然如此,那么我们在继承 FrameLayout,重写它的 onMeasure 方法。在 onMeasure 方法中根据我们指定的最大宽度,高度和比例对 MeasureSpec 进行调整即可。

思路大概如下

  • 没有设置最大宽度,高度,宽高比例,不需要调整,直接返回
  • 先拿到原来的 mode 和 size,暂存起来
  • 根据宽高的比例进行相应的调整
@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 和 sizeint 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);}

我们来看一下,有三种模式:

  1. 当模式已宽度为基准的时候,我们首先对宽度进行调整,是否超出最大宽度,超出取最大宽度,没超出,取原来的值。接着,高度按照 mRatio 进行调整,接着判断高度是否超出最大高度,超出取最大高度,没超出,取原来的值。最后,根据相应的 size,mode 生成相应的 MeasureSpec
  2. 当模式已高度为基准的时候,我们首先对高度进行调整,是否超出最大高度,超出取最大高度,没超出,取原来的值。接着,宽度按照 mRatio 进行调整,接着判断宽度是否超出最大宽度,超出取最大宽度,没超出,取原来的值。最后,根据相应的 size,mode 生成相应的 MeasureSpec
  3. 当模式是默认,没有指定宽度或者高度作为基准的时候,直接判断宽高度是否超出最大的宽高度,制定相应的 MeasureSpec 即可。

源码到此分析为止


题外话

宽高比例的,其实在 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 显示的文字会被截断。


Github MaxLayout Sample


http://www.ppmy.cn/news/285025.html

相关文章

【每天学习一点新知识】今天谈谈SSRF

目录 可能出现SSRF的地方&#xff1a; SSRF相关函数 SSRF 的危险协议 常用绕过方式 SSRF (Server-Side Request Forgery) 即服务端请求伪造&#xff0c;从字面意思上理解就是伪造一个服务端请求&#xff0c;也即是说攻击者伪造服务端的请求发起攻击&#xff0c;攻击者不能直…

12v电瓶20安时是什么意思

问题&#xff1a;是这样的,我去买电瓶车,老板说是20安时的电瓶,但是我是装了4个12V的电瓶的,也就是48V的,我想问一下,是一个电瓶是20安时呢?还是4个电瓶加起来20安时. 答&#xff1a;如果4个12V20A的电瓶并联的话(正极接正极,负极接负极)这时的电压就还是12V,但是电流(安时)会…

汽车蓄电池

在汽车上&#xff0c;发电机是汽车的主要电源&#xff0c;蓄电池是辅助电源。 调节器的功能是在发电机转速升高到一定程度时&#xff0c;自动调节发电机输出电压使其保持稳定。 一、蓄电池的构造和型号 1、蓄电池的分类 &#xff08;1&#xff09;干荷电蓄电池 极板在干燥状…

组装锂电48V20A/H

1.力神18650电芯到货104颗&#xff0c;组成8并13串电池组&#xff1a; 2.装上支架&#xff0c;开始点焊&#xff1a; 3.点焊完成&#xff0c;开始装保护板&#xff1a; 4.装好保护板后&#xff0c;接好输出线&#xff0c;装入电池盒中。 5.上车试骑&#xff1a; 6.两年的二手车…

汇编指令对端口的读写

对端口的读写不能用mov、push、pop等这些对内存的操作指令&#xff0c;对端口的读写只有两条in和out in:读取出端口的内容到程序中 out:写入内容到端口中 ;以下为对8为的端口进行的读写&#xff0c;如果是对16位的端口进行读写需要使用ax存放数据在写入端口 ;对端口号在0~25…

Monica: 您的又一个免费ChatGPT 4.0

最近 ChatGPT 又开始封号了&#xff0c;主要原因如下&#xff1a; 违反使用条款&#xff1a;如果用户违反了平台或应用的使用条款&#xff0c;例如发布违法、恶意或滥用行为的内容&#xff0c;侵犯他人的权利&#xff0c;或者从事垃圾信息传播等&#xff0c;管理员可能会采取封…

4-20mA一进二出隔离分配器

SunYuan DIN 1X2 ISOD系列4-20mA信号一进二出隔离分配器&#xff0c;是一种将工业现场仪器仪表与传感器、PLC/DCS等输出的模拟4-20mA电流环路信号&#xff0c;经隔离分配成精度、线性度、阻抗相匹配的两路标准4-20mA信号分配模块。该模块内部集成了两组高隔离的模拟信号隔离放…

2020-10-04 Chapter5 译码电路相关

第五章 输入输出接口概念——译码电路相关部分&#xff09; 写在前面第五章笔记节选&#xff08;和课堂训练有关的部分&#xff09;2. 端口选择&#xff08;译码&#xff09;技术门电路译码例题1&#xff1a;设计I/O端口地址为2F8H的只读译码电路例题2&#xff1a;某接口数据输…