IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android:自定义可设置最大最小长度和宽度的LinearLayout -> 正文阅读

[移动开发]Android:自定义可设置最大最小长度和宽度的LinearLayout

需求原因需要一个本身自适应长宽,但有最大值或者最小值限制的布局.

所以需要自定义View,重新onMeasure进行布局绘制。
我们继承LinearLayout,准备重写onMeasure方法

class AdaptiveLinearLayout
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        //todo
    }
}

看的出,onMeasure需要传入MeasureSpec,那么MeasureSpec是个什么东西呢?

MeasureSpec是什么?

? MeasureSpec 封装了父布局传递给子布局的布局要求,每个 MeasureSpec modesize 组成,包含了父布局对子布局相应的宽高要求。

? MeasureSpec 有三种模式,UNSPECIFIEDEXACTLYAT_MOST

? UNSPECIFIED:父布局不对子布局做任何限制,它想多大就多大;一般自定义 View 中用不到;(常见于系统内部控件,例如 ListView、ScrollView)

? EXACTLY:父布局对子布局的宽高大小有明确的要求,不管子布局想要多大,它都不能超过父布局对它的限制;(一般指具体的大小如 100dp,或者 match_parent,都是确切的尺寸)

? AT_MOST:子布局想要多大就可以多大,但是一般来说不会超过父布局的尺寸;(一般对应的父布局尺寸为 wrap_content,父布局无法确定子布局的尺寸)

怎么获取到MeasureSpec?

? 可以通过调用View.MeasureSpec.makeMeasureSpec()传入size和mode 来获取一个MeasureSpec。具体的大家可以看源码噢,这里就不做详述了。

val size = 100dp
val mode = MeasureSpec.AT_MOST
val measureSpec = View.MeasureSpec.makeMeasureSpec(size,mode )

大致思路

  • 调用super.onMeasure()之前获取到,倘若设置了最大尺寸,那么此时判断父布局给的mode如果是AT_MOST,那么就以最大尺寸和AT_MOST重新获取对应的measureSpec 否则就以最大尺寸和EXACTLY重新获取measureSpec 。然后测量。(这样就可以做到,所以xml文件设置的是wrapcontent,就可以自适应且最大尺寸为设置的最大尺寸。如果父布局设置的是match_parent或者固定值,就直接已最大尺寸作为宽高)。
  • 调用super.onMeasure()之后判断测量后的宽高有没有小于设置的宽高,如果小于,则直接按照上述方式重新获取measureSpec ,然后测量将宽高限定到最小值。注意:此时也需要判断原来的模式是否为EXACTLY,如果是EXACTLY,则不做操作(按照不觉设置的最小值来),否则将模式设置为EXACTLY,将最小值传入,直接设置宽高为最小值

测量代码如下

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        //测量最大值
        val widthMS = getMaxMeasureSpec(widthMeasureSpec, maxWidth)
        val heightMS = getMaxMeasureSpec(heightMeasureSpec, maxHeight)
        super.onMeasure(widthMS, heightMS)
        //测量最小值
        val minW = getMinWidthMeasureSpec(widthMS)
        val minH = getMinHeightMeasureSpec(heightMS)
        if (widthMS != minW || heightMS != minH) {
            super.onMeasure(minW, minH)
        }
    }

    /**
     * 得到最大值的MeasureSpec
     * AT_MOST 子布局想要多大就可以多大,但是一般来说不会超过父布局的尺寸;(一般对应的父布局尺寸为 wrap_content,父布局无法确定子布局的尺寸)所以用来设置最大值
     */
    private fun getMaxMeasureSpec(measureSpec: Int, value: Int): Int {
        if (value > 0 && MeasureSpec.getSize(measureSpec) > value) {
            return if (MeasureSpec.getMode(measureSpec) == MeasureSpec.AT_MOST) {
                MeasureSpec.makeMeasureSpec(value, MeasureSpec.AT_MOST)
            } else {
                MeasureSpec.makeMeasureSpec(value, MeasureSpec.EXACTLY)
            }
        }
        return measureSpec
    }

    /**
     * 得到最小宽度MeasureSpec
     */
    private fun getMinWidthMeasureSpec(measureSpec: Int): Int {
        return if (MeasureSpec.getMode(measureSpec) != MeasureSpec.EXACTLY
                && measuredWidth < minWidth) {
            MeasureSpec.makeMeasureSpec(minWidth, MeasureSpec.EXACTLY)
        } else measureSpec
    }


    /**
     * 得到最小高度MeasureSpec
     */
    private fun getMinHeightMeasureSpec(measureSpec: Int): Int {
        //父布局没有设置EXACTLY,那么此时判断测量后的高度小于最小高度,直接明确设置.如果父布局明确设置为EXACTLY 则直接设置为按照父布局的设置来
        return if (MeasureSpec.getMode(measureSpec) != MeasureSpec.EXACTLY
                && measuredHeight < minHeight) {
            MeasureSpec.makeMeasureSpec(minHeight, MeasureSpec.EXACTLY)
        } else measureSpec
    }

上面的测量是核心的代码思路了,下面放上完整的代码。

设置xml 属性 在attrs.xml文件中新增 所需的属性

<declare-styleable name="AdaptiveLinearLayout">
        <!--最小宽度-->
        <attr name="min_width" format="dimension" />
        <!--对打宽度-->
        <attr name="max_width" format="dimension" />
        <!--最小高度-->
        <attr name="min_height" format="dimension" />
        <!--最大高度-->
        <attr name="max_height" format="dimension" />
    </declare-styleable>

AdaptiveLinearLayout的整体代码

class AdaptiveLinearLayout
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {

    /**
     * 最小宽度
     */
    var minWidth: Int = 0
        set(value) {
            field = value
            requestLayoutAndInvalidate()
        }

    /**
     * 最大宽度
     */
    var maxWidth: Int = 0
        set(value) {
            field = value
            requestLayoutAndInvalidate()
        }

    /**
     * 最小高度
     */
    var minHeight: Int = 0
        set(value) {
            field = value
            requestLayoutAndInvalidate()
        }

    /**
     * 最大高度
     */
    var maxHeight: Int = 0
        set(value) {
            field = value
            requestLayoutAndInvalidate()
        }

    init {
        //解析xml属性
        val styleAttr = context.obtainStyledAttributes(attrs, R.styleable.AdaptiveLinearLayout)
        minWidth = styleAttr.getDimension(R.styleable.AdaptiveLinearLayout_min_width, 0F).toInt()
        maxWidth = styleAttr.getDimension(R.styleable.AdaptiveLinearLayout_max_width, 0F).toInt()
        minHeight = styleAttr.getDimension(R.styleable.AdaptiveLinearLayout_min_height, 0F).toInt()
        maxHeight = styleAttr.getDimension(R.styleable.AdaptiveLinearLayout_max_height, 0F).toInt()
        styleAttr.recycle()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        //测量最大值
        val widthMS = getMaxMeasureSpec(widthMeasureSpec, maxWidth)
        val heightMS = getMaxMeasureSpec(heightMeasureSpec, maxHeight)
        super.onMeasure(widthMS, heightMS)
        //测量最小值
        val minW = getMinWidthMeasureSpec(widthMS)
        val minH = getMinHeightMeasureSpec(heightMS)
        if (widthMS != minW || heightMS != minH) {
            super.onMeasure(minW, minH)
        }
    }

    /**
     * 得到最大值的MeasureSpec
     * AT_MOST 子布局想要多大就可以多大,但是一般来说不会超过父布局的尺寸;(一般对应的父布局尺寸为 wrap_content,父布局无法确定子布局的尺寸)所以用来设置最大值
     */
    private fun getMaxMeasureSpec(measureSpec: Int, value: Int): Int {
        if (value > 0 && MeasureSpec.getSize(measureSpec) > value) {
            return if (MeasureSpec.getMode(measureSpec) == MeasureSpec.AT_MOST) {
                MeasureSpec.makeMeasureSpec(value, MeasureSpec.AT_MOST)
            } else {
                MeasureSpec.makeMeasureSpec(value, MeasureSpec.EXACTLY)
            }
        }
        return measureSpec
    }

    /**
     * 得到最小宽度MeasureSpec
     */
    private fun getMinWidthMeasureSpec(measureSpec: Int): Int {
        return if (MeasureSpec.getMode(measureSpec) != MeasureSpec.EXACTLY
                && measuredWidth < minWidth) {
            MeasureSpec.makeMeasureSpec(minWidth, MeasureSpec.EXACTLY)
        } else measureSpec
    }


    /**
     * 得到最小高度MeasureSpec
     */
    private fun getMinHeightMeasureSpec(measureSpec: Int): Int {
        //父布局没有设置EXACTLY,那么此时判断测量后的高度小于最小高度,直接明确设置.如果父布局明确设置为EXACTLY 则直接设置为按照父布局的设置来
        return if (MeasureSpec.getMode(measureSpec) != MeasureSpec.EXACTLY
                && measuredHeight < minHeight) {
            MeasureSpec.makeMeasureSpec(minHeight, MeasureSpec.EXACTLY)
        } else measureSpec
    }

    /**
     * 布局重新请求绘制
     */
    private fun requestLayoutAndInvalidate() {
        requestLayout()
        invalidate()
    }
}

在布局文件中直接使用例子

<com.widget.AdaptiveLinearLayout
                android:id="@+id/ll_phone"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center_vertical"
                android:orientation="horizontal"
                app:min_height="50dp"
                app:min_width ="50dp"
				app:max_height="500dp"
                app:max_width ="500dp">

            
            </com.widget.AdaptiveLinearLayout>
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-22 14:18:44  更:2021-07-22 14:20:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/2 9:07:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码