Android自定义可控最大宽高的Layout
2016-03-16 16:13
411 查看
完整项目示例Git仓库
https://git.oschina.net/jokerlee/CustomFrameLayout.git
新的官方文档建议使用第一个namespace声明(Android Studio仅支持该声明),旧版可使用第二种写法。
重载ViewGroup的layoutParams生成函数,这几个函数负责将view的属性集合或者现有的ViewGroup.LayoutParams转换为适用于当前view类型的布局参数。
核心的子view布局宽高控制为onMeasure函数,该函数计算并设置所有child view包括自身的大小,保证child view不会超过max值
https://git.oschina.net/jokerlee/CustomFrameLayout.git
Android View的宽高属性
View拥有的默认属性中含有minHeight以及minWidth可以控制view在其父View布局计算宽高时,能够有一个最小的宽高的限定;在进行一些布局的时候能够利用该属性来限定最小的宽高,但能否自己定义maxHeight和maxWidth来实现同样的限定view的宽高上限?<Button android:id="@+id/button" android:minHeight="155dp" android:minWidth="240dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Select"/>
Android自定义attr属性
View除了android赋予的默认属性可以使用外,还提供了自己定义属性的方法,即在在res/values文件下新建一个attrs.xml,加入自定义的属性:在布局xml使用自定义属性
在attrs.xml文件声明定义好自定义属性的名称和类型之后,在布局文件内加入:xmlns:custom_attr="http://schemas.android.com/apk/res-auto"
xmlns:custom_attr="http://schemas.android.com/apk/res/[your package name]
新的官方文档建议使用第一个namespace声明(Android Studio仅支持该声明),旧版可使用第二种写法。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:custom_attr="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.jokerlee.custommaxsizeframelayout.MainActivity"> <com.example.jokerlee.custommaxsizeframelayout.CustomFramlayout android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" custom_attr:layout_maxWidth="50dp" custom_attr:layout_maxHeight="30dp"> <ImageView android:layout_width="350dp" android:layout_height="330dp" android:layout_gravity="center" android:background="@color/material_blue_grey_800"/> </FrameLayout> </com.example.jokerlee.custommaxsizeframelayout.CustomFramlayout> </RelativeLayout>
最大宽高限制的实现
自定义LayoutParams类,用于自定义的view的布局,从属性集合内读取出maxWidth和maxHeight:public static class LayoutParams extends FrameLayout.LayoutParams { @ViewDebug.ExportedProperty(category = "layout") public int maxWidth; @ViewDebug.ExportedProperty(category = "layout") public int maxHeight; public LayoutParams(ViewGroup.LayoutParams other) { super(other); } public LayoutParams(LayoutParams other) { super(other); maxWidth = other.maxWidth; maxHeight = other.maxHeight; } public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.CustomFrameLayoutAttr, 0, 0); maxWidth = a.getDimensionPixelSize( R.styleable.CustomFrameLayoutAttr_layout_maxWidth, 0); maxHeight = a.getDimensionPixelSize( R.styleable.CustomFrameLayoutAttr_layout_maxHeight, 0); a.recycle(); } }
重载ViewGroup的layoutParams生成函数,这几个函数负责将view的属性集合或者现有的ViewGroup.LayoutParams转换为适用于当前view类型的布局参数。
@Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(),attrs); } @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LayoutParams ? new LayoutParams((LayoutParams)p):new LayoutParams(p); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LayoutParams; }
核心的子view布局宽高控制为onMeasure函数,该函数计算并设置所有child view包括自身的大小,保证child view不会超过max值
@Override protected void onMeasure(int widthSpec, int heightSpec) { final int widthMode = MeasureSpec.getMode(widthSpec); final int heightMode = MeasureSpec.getMode(heightSpec); if (DEBUG && widthMode != MeasureSpec.AT_MOST) { Log.w(TAG, "onMeasure: widthSpec " + MeasureSpec.toString(widthSpec) + " should be AT_MOST"); } if (DEBUG && heightMode != MeasureSpec.AT_MOST) { Log.w(TAG, "onMeasure: heightSpec " + MeasureSpec.toString(heightSpec) + " should be AT_MOST"); } final int widthSize = MeasureSpec.getSize(widthSpec); final int heightSize = MeasureSpec.getSize(heightSpec); int maxWidth = widthSize; int maxHeight = heightSize; final int count = getChildCount(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.maxWidth > 0 && lp.maxWidth < maxWidth) { maxWidth = lp.maxWidth; } if (lp.maxHeight > 0 && lp.maxHeight < maxHeight) { maxHeight = lp.maxHeight; } } final int wPadding = getPaddingLeft() + getPaddingRight(); final int hPadding = getPaddingTop() + getPaddingBottom(); maxWidth -= wPadding; maxHeight -= hPadding; int width = widthMode == MeasureSpec.EXACTLY ? widthSize : 0; int height = heightMode == MeasureSpec.EXACTLY ? heightSize : 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int childWidthSpec = makeChildMeasureSpec(maxWidth, lp.width); final int childHeightSpec = makeChildMeasureSpec(maxHeight, lp.height); child.measure(childWidthSpec, childHeightSpec); width = Math.max(width, Math.min(child.getMeasuredWidth(), widthSize - wPadding)); height = Math.max(height, Math.min(child.getMeasuredHeight(), heightSize - hPadding)); } setMeasuredDimension(width + wPadding, height + hPadding); } private int makeChildMeasureSpec(int maxSize, int childDimen) { final int mode; final int size; switch (childDimen) { case LayoutParams.WRAP_CONTENT: mode = MeasureSpec.AT_MOST; size = maxSize; break; case LayoutParams.MATCH_PARENT: mode = MeasureSpec.EXACTLY; size = maxSize; break; default: mode = MeasureSpec.EXACTLY; size = Math.min(maxSize, childDimen); break; } return MeasureSpec.makeMeasureSpec(size, mode); }
相关文章推荐
- android WebView设置最大高度
- ie8中图片设置max-width属性满足一定的条件会导致消失
- android layout maxHeight
- layer设置maxWidth及maxHeight解决方案
- 高德地图调用中出现的问题
- android view学习(一)
- Ubuntu下 搭建Android5.1.1编译环境
- Android IPC进程通信之Messager方式
- android开发步步为营之96:android两种常用截图技术
- Android进程优先级architecture : low memory killer (/system/core/lmkd/lmkd.c)
- Android 单位转换(dp、sp、px)
- Android模仿表单上传文件
- android上传图片(及普通参数)到服务器(j2ee后台服务器,ssh框架)
- Android之判断手机是否联网
- Android-BaseLine框架初识之MVP模式最佳方案(三)
- android camera(二):摄像头工作原理、s5PV310 摄像头接口(CAMIF)
- android 一个页面内 多个listview的实现滑动显示
- Android下使用正则表达式
- Android之将对象转换json字符串
- Android消息推送1----原始的socket长链接