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

所以需要自定义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 = 0set(value) {field = valuerequestLayoutAndInvalidate()}/*** 最大宽度*/var maxWidth: Int = 0set(value) {field = valuerequestLayoutAndInvalidate()}/*** 最小高度*/var minHeight: Int = 0set(value) {field = valuerequestLayoutAndInvalidate()}/*** 最大高度*/var maxHeight: Int = 0set(value) {field = valuerequestLayoutAndInvalidate()}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.AdaptiveLinearLayoutandroid: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>

Android:自定义可设置最大最小长度和宽度的LinearLayout相关推荐

  1. Android自定义拍照上传界面,Android自定义dialog——设置头像(拍照,相册)

    Android自定义dialog--设置头像(拍照,相册) 需求场景:个人信息设置,点击头像,在界面上弹出一个弹框,用户选择"拍照"/"从图库选择",选择照片后 ...

  2. Android自定义快速设置

    Android自定义快速设置 Customizer Quick Setting 自定义快速设置 Android N/7.0 牛轧糖 Android自定义快速设置 前言 自定义快速设置图块 深入理解Ti ...

  3. plantuml最大宽度_设置TH最小和最大宽度非常缓慢

    给定一个固定头HTML表,我试图排队标题列与正文行列.我这样做是因为我使用CSS来使头部固定,而头部与身体其他部分不一致.设置TH最小和最大宽度非常缓慢 我正在使用的JavaScript工程,但速度非 ...

  4. 设计一个长方形类。成员变量包括:长度和宽度,成员函数除包括计算周长和计算面积外, 还包括用set方法来设置长方形的长度和宽度,以及用get的方法来获得长方形的长度和宽度 最后,编写一个测试程序来测试所

    本文为博主原创文章,未经博主允许不得转载. 版权为陈博超所有,第一次于2020年11月22日发表于BLOG上 本BLOG上原创文章未经本人许可,不得用于商业用途.转载请经允许后注明出处,否则保留追究法 ...

  5. Android 自定义ViewPager设置屏蔽左右滑动事件

    只要有欲望,就应该有奋斗的心.... 屏蔽左右滑动事件的viewPager public class CustomNoScrollViewPager extends ViewPager{private ...

  6. android自定义view设置高度,自定义View的宽高设定

    关于View的属性 自定义动态设置View的大小属性 使用LayoutParams来设置view的宽高. int textLen = AddShopActivity.mCategoryItemName ...

  7. Android 自定义dialog 设置宽度的问题

    自定义弹框效果图 首先在自定义布局文件中设置了宽度,但是不起作用 <RelativeLayout xmlns:android="http://schemas.android.com/a ...

  8. Android 自定义RatingBar设置步长没起作用

    在项目中使用到了RatingBar控件,在自定义RatingBar的样式后,设置stepSize 没起作用. <RatingBarandroid:id="@+id/ratingBar& ...

  9. android 自定义wifi设置在哪里,Android Wifi的设置、连接操作

    但是,参考了这段代码之后可没少忙活!怎么试都连不上,wifi的信息是创建了,可就是没法连接上.百思不得其解,后来我想,会不会是设置的地方出了问题. 原来是这样设置的: config.preShared ...

最新文章

  1. 如何让机器人持续地进行模仿学习(IROS 2021)
  2. 探索 Block 的本质
  3. MySQL中的UNIX_TIMESTAMP函数使用总结
  4. TCP通信过程大讨论
  5. 一天一个设计模式之JS实现——建造者模式
  6. for相关 java_用java编写一个程序,求2到100之间的偶数和(使用for循环)
  7. idea 修改样式要编译_在IDEA中DEBUG Javac源码
  8. 关于jquery跨域请求方法
  9. 为何time_before 起作用【转】
  10. python和tableau优缺点_matplotlib和Tableau之间哪一个最好?
  11. vue截取一个字符串_字符串截取方法
  12. untiy Input 获取鼠标以及触摸操作
  13. 人工智能如何入门学习?前景如何
  14. Unity中打开文件窗口(OpenFileDialog)的几种方法对比
  15. IOS停机卡免流线路下载更新
  16. html飘窗效果,js实现网页飘窗效果-Javascript-舒彬琪博客|前端技术博客|CMS教程|PbootCMS|JizhiCMS-www.cnsbq.com...
  17. 可移植bit-field与driverlib兼容的C2000控制器工程模板创建及SysConfig配置
  18. 宏定义 定义一年多少秒
  19. 用Python爬取新型冠状病毒肺炎实时数据,pyecharts v1.x绘制省市区疫情地图
  20. eclipse mdt java工程_Atitit.uml2 api 的编程代码实现设计uml开发 使用eclipse jar java 版本...

热门文章

  1. php精度两位小数点,php中小数精度的代码解析
  2. php 使用strrpos检查字串是否存在
  3. 《Chinese Open Relation Extraction and Knowledge Base Establishment》阅读记录
  4. android简单进度条对话框,android进度条对话框实例
  5. compareTo 比较
  6. vue进入页面执行的钩子函数_vue router的钩子函数总结
  7. ASP.NET DataBinder.Eval与 Eval
  8. 国金证券:2007年6月投资策略报告
  9. 微软中国和他的CEO们
  10. 理顺了Servlet struct spring jsp tomcat