百分比布局适配

Hanna ·
更新时间:2024-09-20
· 651 次阅读

文章目录一、简述二、google推荐的百分比布局的使用方式三、实现3.1 创建属性文件3.2 解析3.3 计算并设置百分比布局四、使用五、完整代码六、注意 一、简述

百分比布局适配,就是以父容器的尺寸作为基准,在view的加载过程中,根据当前父容器实际尺寸换算出目标尺寸,再作用在view上。

百分比布局,实际是对容器的一种扩展,即对宽高百分比的设置。

二、google推荐的百分比布局的使用方式

首先要引入:implementation ‘com.android.support:percent:28.0.0’
然后将布局引用为:android.support.percent.PercentRelativeLayout
给控件设置:app:layout_widthPercent=“50%”
百分比布局,实际是对容器的一种扩展,即对宽高百分比的设置。

三、实现 3.1 创建属性文件

attrs.xml中来定义必备的属性

3.2 解析

解析在哪做?以RelativeLayout为例,重要属性都封装在静态类LayoutParams中。这个静态类中就定义了一些自定义属性,也是RelativeLayout特有的属性(相对布局中的独有属性,不能作用于线性布局中):

public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.RelativeLayout_Layout); final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion; mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 || !c.getApplicationInfo().hasRtlSupport()); final int[] rules = mRules; //noinspection MismatchedReadAndWriteOfArray final int[] initialRules = mInitialRules; final int N = a.getIndexCount(); for (int i = 0; i < N; i++) { int attr = a.getIndex(i); switch (attr) { case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing: alignWithParent = a.getBoolean(attr, false); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf: rules[LEFT_OF] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf: rules[RIGHT_OF] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above: rules[ABOVE] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below: rules[BELOW] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline: rules[ALIGN_BASELINE] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft: rules[ALIGN_LEFT] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop: rules[ALIGN_TOP] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight: rules[ALIGN_RIGHT] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom: rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft: rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop: rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight: rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom: rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent: rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal: rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical: rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf: rules[START_OF] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf: rules[END_OF] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart: rules[ALIGN_START] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd: rules[ALIGN_END] = a.getResourceId(attr, 0); break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart: rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0; break; case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd: rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0; break; } } mRulesChanged = true; System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT); a.recycle(); }

通过TypedArray来进行属性的解析,然后switch case中一堆特有属性的获取、解析。解析完成后,就可以在onMeasure方法中用到这些自定义属性了。
这里涉及到了View的加载过程-setContentView方法,简单说就是:我们一个控件的布局属性的创建,是通过父容器来完成的。

创建静态内部类LayoutParams并继承RelativeLayout的LayoutParams来保证相对布局的属性都能用

public static class LayoutParams extends RelativeLayout.LayoutParams{ //在arrts.xml中定义的属性 private float widthPercent; private float heightPercent; private float marginLeftPercent; private float marginRightPercent; private float marginTopPercent; private float marginBottomPercent; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); //解析自定义属性 TypedArray a = c.obtainStyledAttributes(attrs,R.styleable.PercentLayout); widthPercent = a.getFloat(R.styleable.PercentLayout_widthPercent, 0); heightPercent = a.getFloat(R.styleable.PercentLayout_heightPercent, 0); marginLeftPercent = a.getFloat(R.styleable.PercentLayout_marginLeftPercent, 0); marginRightPercent = a.getFloat(R.styleable.PercentLayout_marginRightPercent, 0); marginTopPercent = a.getFloat(R.styleable.PercentLayout_marginTopPercent, 0); marginBottomPercent = a.getFloat(R.styleable.PercentLayout_marginBottomPercent, 0); a.recycle(); } }

总结一下就是先在attrs.xml中定义的属性,然后再解析自定义属性(宽、高、四周间距)

3.3 计算并设置百分比布局

先通过MeasureSpec.getSize()方法获取父容器尺寸:

//获取父容器的尺寸 int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec);

根据view加载的源码分析,还得重写父容器generateLayoutParams方法,return内部的LayoutParams:

public LayoutParams generateLayoutParams(AttributeSet attrs){ return new LayoutParams(getContext(), attrs); }

在onMeasure中判断,这个params是否属于当前类的LayoutParams

@Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LayoutParams; }

然后就可以进行百分比布局的设置了,从params中取出百分比的值后赋值。如果各个属性已经设置好了百分比的值,就能进行换算了

float widthPercent = lp.widthPercent; params.width = (int) (widthSize * widthPercent); 四、使用

在xml的控件中,直接使用这些属性,并给定浮点型的值

五、完整代码

attrs.xml

PercentLayout.xml

public class PercentLayout extends RelativeLayout { public PercentLayout(Context context) { super(context); } public PercentLayout(Context context, AttributeSet attrs) { super(context, attrs); } public PercentLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //获取父容器的尺寸 int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int count = getChildCount(); //遍历子控件 for (int i = 0; i 0){ params.width = (int) (widthSize * widthPercent); } if (heightPercent > 0){ params.height = (int) (heightSize * heightPercent); } if (marginLeftPercent > 0){ ((LayoutParams) params).leftMargin = (int) (widthSize * marginLeftPercent); } if (marginRightPercent > 0){ ((LayoutParams) params).rightMargin = (int) (widthSize * marginRightPercent); } if (marginTopPercent > 0){ ((LayoutParams) params).topMargin = (int) (heightSize * marginTopPercent); } if (marginBottomPercent > 0){ ((LayoutParams) params).bottomMargin = (int) (heightSize * marginBottomPercent); } } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LayoutParams; } public LayoutParams generateLayoutParams(AttributeSet attrs){ return new LayoutParams(getContext(), attrs); } public static class LayoutParams extends RelativeLayout.LayoutParams{ //在arrts.xml中定义的属性 private float widthPercent; private float heightPercent; private float marginLeftPercent; private float marginRightPercent; private float marginTopPercent; private float marginBottomPercent; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); //解析自定义属性 TypedArray a = c.obtainStyledAttributes(attrs,R.styleable.PercentLayout); widthPercent = a.getFloat(R.styleable.PercentLayout_widthPercent, 0); heightPercent = a.getFloat(R.styleable.PercentLayout_heightPercent, 0); marginLeftPercent = a.getFloat(R.styleable.PercentLayout_marginLeftPercent, 0); marginRightPercent = a.getFloat(R.styleable.PercentLayout_marginRightPercent, 0); marginTopPercent = a.getFloat(R.styleable.PercentLayout_marginTopPercent, 0); marginBottomPercent = a.getFloat(R.styleable.PercentLayout_marginBottomPercent, 0); a.recycle(); } } } 六、注意

这些属性的设定,对原有属性没有任何影响,我们只是扩展了它的使用。


作者:墨玉浮白



布局 百分比

需要 登录 后方可回复, 如果你还没有账号请 注册新账号