support-percent
来源:互联网 发布:页游 知乎 编辑:程序博客网 时间:2024/05/17 07:46
一、Android百分比支持库介绍
Android-percent-support这个库可以通过百分比控制控件的大小
在应用module中的build.gradle中添加
compile 'com.android.support:percent:25.1.0'目前支持FrameLayout和RelativeLayout,封装的类叫PercentFrameLayout和PercentRelativeLayout.
支持的属性有
layout_widthPercent
layout_heightPercent
layout_marginPercent
layout_marginLeftPercent
layout_marginTopPercent
layout_marginRightPercent
layout_marginBottomPercent
layout_marginStartPercent
layout_marginEndPercent
layout_aspectRatio
可以看到支持宽高,以及margin。另外需要特别说明一下layout_aspectRatio
layout_aspectRatio表示设置计算比例,只需要定义宽或高,然后就可以根据aspectRatio自动计算出另一个尺寸,比如
<?xml version="1.0" encoding="utf-8"?><android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"><TextViewandroid:layout_width="300dp"android:layout_height="0dp"app:layout_aspectRatio="178%"/></android.support.percent.PercentRelativeLayout>
面View指定了view的宽度为300dp,根据layout_aspectRatio="178%"自动计算出height。
"178%"表示width/height=1.78,如果是"78%"则表示width/height=0.78
由此可计算出height=300dp/1.78;
则根据比例1.78自动计算出height=300/1.78=169
注意:
1、如果设置了layout_widthPercent就不用具体设置layout_width的值,设置了layout_heightPercent就不用具体设置layout_height,
就算设置了值也会被忽略
<?xml version="1.0" encoding="utf-8"?><android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 因为设置了layout_widthPercent和layout_heightPercent 所以不用指定layout_width和layout_height的值--> <TextView android:id="@+id/top_left"android:layout_width="0dp"android:layout_height="0dp" android:layout_alignParentTop="true" android:background="#ff44aacc" app:layout_heightPercent="20%" app:layout_widthPercent="70%" /> <!-- layout_width=0、layout_height=300dp 设置宽、高的值也会被忽略 --> <View android:id="@+id/top_right" android:layout_width="0dp" android:layout_height="300dp" android:layout_alignParentTop="true" android:layout_toRightOf="@+id/top_left" android:background="#ffe40000" app:layout_heightPercent="20%" app:layout_widthPercent="30%" /></android.support.percent.PercentRelativeLayout>2、更好的显示尺寸
虽然可以不指定layout_width或layout_height,但是如果view的尺寸不太确定,又不想让百分比尺寸限制内容显示完全,可以设置
layout_width/height="wrap_content"
这样当percentage size is too small for the View's content,it will be resized using wrap_content rule当百分比尺寸小于内容所需的宽高,可以重新计算尺寸以便显示完全.
二、源码解析
下面我们以PercentRelativelayout来分析一下源码
PercentRelativeLayout.java
public class PercentRelativeLayout extends RelativeLayout { private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this); public PercentRelativeLayout(Context context) { super(context); } public PercentRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } public PercentRelativeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //根据子view中设置的百分比,计算宽高和margin值 mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec); //如果计算的百分比宽高值小于view的size,则设置layoutParam.width/height=wrap_content if (mHelper.handleMeasuredStateTooSmall()) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //恢复原始的layoutparam mHelper.restoreOriginalParams(); } /** * 继承RelativeLayout.LayoutParams 并实现PercentLayoutParams接口 */ public static class LayoutParams extends RelativeLayout.LayoutParams implements PercentLayoutHelper.PercentLayoutParams { private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); /** * 获取layoutparams中的百分比参数,然后封装到PercentLayoutInfo中,在onMeasure中遍历子View并判断计算宽高、margin值。 */ mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs); } public LayoutParams(int width, int height) { super(width, height); } public LayoutParams(ViewGroup.LayoutParams source) { super(source); } public LayoutParams(MarginLayoutParams source) { super(source); } @Override public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() { if (mPercentLayoutInfo == null) { mPercentLayoutInfo = new PercentLayoutHelper.PercentLayoutInfo(); } return mPercentLayoutInfo; } @Override protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { /** * 重写setBaseAttributes,避免不设置layout_width或layout_height导致的异常发生。下面是父类中的写法: width = a.getLayoutDimension(widthAttr, "layout_width"); height = a.getLayoutDimension(heightAttr, "layout_height"); */ PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr); } }}可以看到核心功能都在PercentLayoutHelper中。
PercentLayoutHelper.java
public class PercentLayoutHelper { private static final String TAG = "PercentLayout"; public static boolean DEBUG = true; private static final boolean VERBOSE = true; private final ViewGroup mHost; //百分比View容器,即PercentRelativeLayout或PercentFrameLayout public PercentLayoutHelper(@NonNull ViewGroup host) { if (host == null) { throw new IllegalArgumentException("host must be non-null"); } mHost = host; } /** * 重写setBaseAttributes,避免不设置layout_width或layout_height导致的异常发生。下面是父类中的写法: width = a.getLayoutDimension(widthAttr, "layout_width"); height = a.getLayoutDimension(heightAttr, "layout_height"); */ public static void fetchWidthAndHeight(ViewGroup.LayoutParams params, TypedArray array, int widthAttr, int heightAttr) { params.width = array.getLayoutDimension(widthAttr, 0); params.height = array.getLayoutDimension(heightAttr, 0); Log.d(TAG,"params.width="+params.width+",params.height="+params.height); } /** * 迭代所有子View,根据LayoutParam中设置的百分比计算宽高、margin值 */ public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) { // 获取容器的宽高 int widthHint = View.MeasureSpec.getSize(widthMeasureSpec) - mHost.getPaddingLeft() - mHost.getPaddingRight(); int heightHint = View.MeasureSpec.getSize(heightMeasureSpec) - mHost.getPaddingTop() - mHost.getPaddingBottom(); /** * 遍历children,根据 */ for (int i = 0, N = mHost.getChildCount(); i < N; i++) { View view = mHost.getChildAt(i); ViewGroup.LayoutParams params = view.getLayoutParams(); if (params instanceof PercentLayoutParams) { //子view的layoutParams是父类容器的LayoutParams PercentLayoutInfo info = ((PercentLayoutParams) params).getPercentLayoutInfo(); if (info != null) { if (params instanceof ViewGroup.MarginLayoutParams) { info.fillMarginLayoutParams(view, (ViewGroup.MarginLayoutParams) params, widthHint, heightHint); } else { info.fillLayoutParams(params, widthHint, heightHint); } } } } } /** * 获取LayoutParams中属性值 */ public static PercentLayoutInfo getPercentLayoutInfo(Context context, AttributeSet attrs) { PercentLayoutInfo info = null; TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout); //获取宽度百分比、margin百分比 ......... return info; } /** * 重新恢复原本的尺寸值,也就是说onMeasure里面的对值进行了改变,测量完成后。在这个地方,将值又恢复成如果布局文件中的值 */ public void restoreOriginalParams() { Log.d(TAG,"---------------------------restoreOriginalParams---------------------"); for (int i = 0, N = mHost.getChildCount(); i < N; i++) { View view = mHost.getChildAt(i); ViewGroup.LayoutParams params = view.getLayoutParams(); if (params instanceof PercentLayoutParams) { PercentLayoutInfo info = ((PercentLayoutParams) params).getPercentLayoutInfo(); if (info != null) { if (params instanceof ViewGroup.MarginLayoutParams) { info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params); } else { info.restoreLayoutParams(params); } } } } } /** * 避免设置的百分比计算出来的尺寸过小,导致内容显示不全,可设置layout_width/height=WRAP_CONTENT,这样可重新测量 */ public boolean handleMeasuredStateTooSmall() { boolean needsSecondMeasure = false; for (int i = 0, N = mHost.getChildCount(); i < N; i++) { View view = mHost.getChildAt(i); ViewGroup.LayoutParams params = view.getLayoutParams(); if (params instanceof PercentLayoutParams) { PercentLayoutInfo info = ((PercentLayoutParams) params).getPercentLayoutInfo(); if (info != null) { if (shouldHandleMeasuredWidthTooSmall(view, info)) { needsSecondMeasure = true; params.width = ViewGroup.LayoutParams.WRAP_CONTENT; } if (shouldHandleMeasuredHeightTooSmall(view, info)) { needsSecondMeasure = true; params.height = ViewGroup.LayoutParams.WRAP_CONTENT; } } } } return needsSecondMeasure; } private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) { int state = ViewCompat.getMeasuredWidthAndState(view) & ViewCompat.MEASURED_STATE_MASK; //设置了widthPercent并且layout_width=WRAP_CONTENT; return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0 && info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT; } private static boolean shouldHandleMeasuredHeightTooSmall(View view, PercentLayoutInfo info) { int state = ViewCompat.getMeasuredHeightAndState(view) & ViewCompat.MEASURED_STATE_MASK; //设置了heightPercent并且layout_height=WRAP_CONTENT; return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.heightPercent >= 0 && info.mPreservedParams.height == ViewGroup.LayoutParams.WRAP_CONTENT; } static class PercentMarginLayoutParams extends ViewGroup.MarginLayoutParams { private boolean mIsHeightComputedFromAspectRatio; private boolean mIsWidthComputedFromAspectRatio; public PercentMarginLayoutParams(int width, int height) { super(width, height); } } /** * Container for information about percentage dimensions and margins. It acts as an extension * for {@code LayoutParams}. */ public static class PercentLayoutInfo { public float widthPercent; public float heightPercent; public float leftMarginPercent; public float topMarginPercent; public float rightMarginPercent; public float bottomMarginPercent; public float startMarginPercent; public float endMarginPercent; public float aspectRatio; final PercentMarginLayoutParams mPreservedParams; //保存原始的尺寸信息,当onMeasure改变后尺寸后,在onLayout中恢复原始的参数信息。 public PercentLayoutInfo() { widthPercent = -1f; heightPercent = -1f; leftMarginPercent = -1f; topMarginPercent = -1f; rightMarginPercent = -1f; bottomMarginPercent = -1f; startMarginPercent = -1f; endMarginPercent = -1f; mPreservedParams = new PercentMarginLayoutParams(0, 0); } /** * 设置layoutparams的宽、高 */ public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint, int heightHint) { // 保留原始的layout params, 以便onMeasure后在onLayout中恢复. mPreservedParams.width = params.width; mPreservedParams.height = params.height; final boolean widthNotSet = (mPreservedParams.mIsWidthComputedFromAspectRatio || mPreservedParams.width == 0) && (widthPercent < 0); final boolean heightNotSet = (mPreservedParams.mIsHeightComputedFromAspectRatio || mPreservedParams.height == 0) && (heightPercent < 0); //根据宽度百分比计算宽度 if (widthPercent >= 0) { params.width = Math.round(widthHint * widthPercent); } //根据高度百分比计算高度 if (heightPercent >= 0) { params.height = Math.round(heightHint * heightPercent); } //如果只指定了宽或高,根据aspectRatio计算另一个值 if (aspectRatio >= 0) { if (widthNotSet) { params.width = Math.round(params.height * aspectRatio); // Keep track that we've filled the width based on the height and aspect ratio. mPreservedParams.mIsWidthComputedFromAspectRatio = true; } if (heightNotSet) { params.height = Math.round(params.width / aspectRatio); // Keep track that we've filled the height based on the width and aspect ratio. mPreservedParams.mIsHeightComputedFromAspectRatio = true; } } } /** * 根据百分比设置margin值 */ public void fillMarginLayoutParams(View view, ViewGroup.MarginLayoutParams params, int widthHint, int heightHint) { //设置宽高 fillLayoutParams(params, widthHint, heightHint); // Preserve the original margins, so we can restore them after the measure step. mPreservedParams.leftMargin = params.leftMargin; mPreservedParams.topMargin = params.topMargin; mPreservedParams.rightMargin = params.rightMargin; mPreservedParams.bottomMargin = params.bottomMargin; MarginLayoutParamsCompat.setMarginStart(mPreservedParams, MarginLayoutParamsCompat.getMarginStart(params)); MarginLayoutParamsCompat.setMarginEnd(mPreservedParams, MarginLayoutParamsCompat.getMarginEnd(params)); //设置margin if (leftMarginPercent >= 0) { params.leftMargin = Math.round(widthHint * leftMarginPercent); } if (topMarginPercent >= 0) { params.topMargin = Math.round(heightHint * topMarginPercent); } if (rightMarginPercent >= 0) { params.rightMargin = Math.round(widthHint * rightMarginPercent); } if (bottomMarginPercent >= 0) { params.bottomMargin = Math.round(heightHint * bottomMarginPercent); } boolean shouldResolveLayoutDirection = false; if (startMarginPercent >= 0) { MarginLayoutParamsCompat.setMarginStart(params, Math.round(widthHint * startMarginPercent)); shouldResolveLayoutDirection = true; } if (endMarginPercent >= 0) { MarginLayoutParamsCompat.setMarginEnd(params, Math.round(widthHint * endMarginPercent)); shouldResolveLayoutDirection = true; } if (shouldResolveLayoutDirection && (view != null)) { MarginLayoutParamsCompat.resolveLayoutDirection(params, ViewCompat.getLayoutDirection(view)); } if (DEBUG) { Log.d(TAG, "after fillMarginLayoutParams: (" + params.width + ", " + params.height + ")"); } } @Override public String toString() { return String.format("PercentLayoutInformation width: %f height %f, margins (%f, %f, " + " %f, %f, %f, %f)", widthPercent, heightPercent, leftMarginPercent, topMarginPercent, rightMarginPercent, bottomMarginPercent, startMarginPercent, endMarginPercent); } /** * 设置layouparams中的margin值 * Restores the original dimensions and margins after they were changed for percentage based * values. You should call this method only if you previously called * {@link PercentLayoutHelper.PercentLayoutInfo#fillMarginLayoutParams(View, ViewGroup.MarginLayoutParams, int, int)}. */ public void restoreMarginLayoutParams(ViewGroup.MarginLayoutParams params) { restoreLayoutParams(params); params.leftMargin = mPreservedParams.leftMargin; params.topMargin = mPreservedParams.topMargin; params.rightMargin = mPreservedParams.rightMargin; params.bottomMargin = mPreservedParams.bottomMargin; MarginLayoutParamsCompat.setMarginStart(params, MarginLayoutParamsCompat.getMarginStart(mPreservedParams)); MarginLayoutParamsCompat.setMarginEnd(params, MarginLayoutParamsCompat.getMarginEnd(mPreservedParams)); } /** * 设置layouparams的原始宽高 * Restores original dimensions after they were changed for percentage based values. * You should call this method only if you previously called * {@link PercentLayoutHelper.PercentLayoutInfo#fillLayoutParams(ViewGroup.LayoutParams, int, int)}. */ public void restoreLayoutParams(ViewGroup.LayoutParams params) { if (!mPreservedParams.mIsWidthComputedFromAspectRatio) { // Only restore the width if we didn't compute it based on the height and // aspect ratio in the fill pass. params.width = mPreservedParams.width; } if (!mPreservedParams.mIsHeightComputedFromAspectRatio) { // Only restore the height if we didn't compute it based on the width and // aspect ratio in the fill pass. params.height = mPreservedParams.height; } // Reset the tracking flags. mPreservedParams.mIsWidthComputedFromAspectRatio = false; mPreservedParams.mIsHeightComputedFromAspectRatio = false; } } public interface PercentLayoutParams { PercentLayoutInfo getPercentLayoutInfo(); }}
注释已经写的很清楚了,就不做详解了。
- support-percent
- Android percent-support-lib
- android-percent-support-extend
- android-percent-support-lib-sample
- android-percent-support-lib-sample
- Percent Support Library使用详解
- 【Android】Percent Support Library实践
- android support Library—-Percent Support Library
- Android--百分比布局库(percent-support-lib)
- Android 百分比支持库 android-percent-support
- android-support-percent的学习使用笔记
- Android 百分比布局库(percent-support-lib)
- Android 百分比布局(Percent Support Library)
- Android百分比布局(percent-support-lib)
- Android 百分比布局库(percent-support-lib)
- android studio percent support librarys 百分比布局
- Android percent-support-lib百分比布局
- Android 百分比布局库(percent-support-lib) 解析与扩展
- CentOS6.7安装TeamViewer
- Java总结篇系列:Java多线程(一)
- 数据分析与数据挖掘在常规工作中的应用——数据 离散化
- u-boot之添加一块新板子的支持
- 深入了解JAVA可变长度的参数(Varargs)
- support-percent
- 微信小程序checkbox排列方向
- 4.面向对象
- 【9-1-7】链表的冒泡 bubblesort
- 419. Battleships in a Board 难度:medium
- ES6的变量声明
- 66. Plus One
- 2017年了,别再混日子了
- openjdk8的安装