百分比布局

今天使用的是:Android Percent Support Library  百分比布局兼容库:

如何使用百分比布局

1.添加依赖

dependencies { 
implementation 'com.android.support:percent:27.0.2'
}

我的studio是6.0一下,所以将implemention改为compile。有的时候添加studio可能会报错,这个时候看一下自己的 compileSdkVersion,因为我的是26,所以需要修改一下

compile 'com.android.support:percent:26.0.0-alpha1'

接下来在函数库里面我们主要用到两个类:

  • PercentRelativeLayout
  • PercentFrameLayout
  • 通过名字就可以看出,这是继承自FrameLayoutRelativeLayout两个容器类;  

支持的属性有:

  • app:layout_heightPercent:用百分比表示高度
  • app:layout_widthPercent:用百分比表示宽度
  • app:layout_marginPercent:用百分比表示View之间的间隔
  • app:layout_marginLeftPercent:用百分比表示左边间隔
  • app:layout_marginRight:用百分比表示右边间隔
  • app:layout_marginTopPercent:用百分比表示顶部间隔
  • app:layout_marginBottomPercent:用百分比表示底部间隔
  • app:layout_marginStartPercent:用百分比表示距离第一个View之间的距离
  • app:layout_marginEndPercent:用百分比表示距离最后一个View之间的距离
  • app:layout_aspectRatio:用百分比表示View的宽高比

这几个是重要的属性:

  • app:layout_aspectRatio="50%"
    app:layout_heightPercent="70%"
    app:layout_widthPercent="100%"
    app:layout_marginStartPercent="10%"
    app:layout_marginEndPercent="10%"


接下来就是实际操作:

<?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">

    <TextView
        android:id="@+id/row_one_item_one"
        android:layout_alignParentTop="true"
        android:background="#7700ff00"
        android:gravity="center"
        android:text="w:70%,h:20%"
        app:layout_heightPercent="20%"
        app:layout_widthPercent="70%"
        />

    <TextView
        android:id="@+id/row_one_item_two"
        android:background="#396190"
        android:layout_alignParentRight="true"
        android:gravity="center"
        android:text="w:30%,h:20%"
        app:layout_heightPercent="20%"
        app:layout_widthPercent="30%"
        />

    <ImageView
        android:id="@+id/row_two_item_one"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_below="@+id/row_one_item_one"
        android:background="#d89695"
        android:scaleType="centerCrop"
        android:src="@drawable/image_car"
        app:layout_heightPercent="70%" />

    <TextView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_below="@id/row_two_item_one"
        android:background="#770000ff"
        android:gravity="center"
        android:text="width:100%,height:10%"
        app:layout_heightPercent="10%"
        app:layout_widthPercent="100%" />

</android.support.percent.PercentRelativeLayout>

效果图:

 

实现原理:

系统初始化布局都是通过LayoutInflater来实现的,比如在setContentView里面。LayoutParams在平时使用还是比较多的,其作用就是让父View决定如何摆放自己以及自己的宽高。当我们把child view写到布局里面,那么child view的LayoutParams由ViewGroup的generateDefaultLayoutParams来设置

下面就是LayoutInflater为子View赋值LayoutParams的关键代码:

void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
    boolean finishInflate, boolean inheritContext) {
    //...
    while (((type = parser.next()) != XmlPullParser.END_TAG ||
        parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

        //...
        final View view = createViewFromTag(parent, name, attrs, inheritContext);
        final ViewGroup viewGroup = (ViewGroup) parent;
        final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
        rInflate(parser, view, attrs, true, true);
        viewGroup.addView(view, params);
    //...
}

PercentRelativeLayout所以就实现了generateLayoutParams的方法,并且返回的是继承的RelativeLayout.LayoutParams,这样就保留了RelativeLayout原来属性。并且generateLayoutParams的方法参数是AttributeSet,里面就包含了我们声明的PercentLayout的属性值,例如layout_widthPercent等等。

PercentRelativeLayout.LayoutParams在构造方法就通过PercentLayoutHelper对AttributeSet进行解析,解析的结果保存在自定义的数据结构PercentLayoutHelper.PercentLayoutInfo,里面包括了在概述里面说的所有属性。

private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;

public LayoutParams(Context c, AttributeSet attrs) {
    super(c, attrs);
    mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
}

另外,我们知道所有的LayoutParams都是继承ViewGoup.LayoutParams,里面有个方法是用来初始化View两个layout_width,layout_height:

protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
    width = a.getLayoutDimension(widthAttr, "layout_width");
    height = a.getLayoutDimension(heightAttr, "layout_height");
}

如果我们没有在布局文件里面声明这两个属性,那么在LayoutInflater初始化的就会抛UnsupportedOperationException。由于使用了百分比的属性,所以这个属性就可以不需要,为了让其不抛异常,必须重写这个方法。

PercentLayoutHelper#fetchWidthAndHeight就是让其在没有值的情况下让LayoutParams的width和height的值都为0。

@Override
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
    PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
}

初始化布局的时候已经把所有需要的数据都保持在了PercentLayoutInfo里面,接下来就到了我们熟悉的三大流程了:onMeasure->onLayout->onDraw,由于是ViewGroup,所以只需要关注前面两个即可。先来看onMeasure:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if (mHelper.handleMeasuredStateTooSmall()) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

adjustChildren的主要工作就是遍历所有的child view,通过child view的PercentLayoutHelper.LayoutParams的宽高百分比转换为实际的占用的像素宽高。并保存在对应child view的LayoutParams里,然后再调用RelativeLayout原有的onMeasure,就可以实现宽高的百分比转换。

我们在前面读书笔记中View的工作原理measure的过程里面有提到,有时候我们在测量View的时候,如果父View最大能够给我们的空间小于我们需要的空间,就会给测量结果的高两位加上相应的状态表示MEASURED_STATE_TOO_SMALL。

如果出现了这种情况,并且为layout_width和layout_height设置了wrap_content,就需要调用handleMeasuredStateTooSmall来处理,将宽或者高重新按照wrap_content的属性来测量。

然后就到了onLayout的阶段,基本什么也没做。如果在child view里面设置了layout_width,layout_height等属性,那么在onMeasure阶段就会调用adjustChildren将他们都保存起来,等onLayout结束之后再把他们给还原回去。

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    mHelper.restoreOriginalParams();
}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@删库跑路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值