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

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

前言


这篇博客主要介绍的是怎样自定义一个可以指定最大宽度,高度,以及宽高比的 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.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>


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


比如,我们要指定宽度是高度的某个比例的时候,如,宽度是高度的 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>


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


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


<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>


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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 和 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);
}


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


  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


相关文章
|
1月前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
86 0
|
20天前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
22天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
30 5
|
1月前
|
XML 前端开发 Java
安卓应用开发中的自定义View组件
【10月更文挑战第5天】自定义View是安卓应用开发的一块基石,它为开发者提供了无限的可能。通过掌握其原理和实现方法,可以创造出既美观又实用的用户界面。本文将引导你了解自定义View的创建过程,包括绘制技巧、事件处理以及性能优化等关键步骤。
|
6天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
11天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
13天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
15天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。
|
13天前
|
存储 API 开发工具
探索安卓开发:从基础到进阶
【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!
|
14天前
|
存储 XML JSON
探索安卓开发:从新手到专家的旅程
【10月更文挑战第36天】在这篇文章中,我们将一起踏上一段激动人心的旅程,从零基础开始,逐步深入安卓开发的奥秘。无论你是编程新手,还是希望扩展技能的老手,这里都有适合你的知识宝藏等待发掘。通过实际的代码示例和深入浅出的解释,我们将解锁安卓开发的关键技能,让你能够构建自己的应用程序,甚至贡献于开源社区。准备好了吗?让我们开始吧!
25 2