Android常用Layout源码总结—FrameLayout

前言

通过学习Android官方Layout的源码,可以帮助自己更好的理解Android的UI框架系统,了解内部便捷的封装好的API调用,有助于进行布局优化和自定义view实现等工作。这里把学习结果通过写博客进行总结,便于记忆,不至于将来遗忘。

本篇博客中源码基于Android 8.1

FrameLayout特点

FrameLayout是Android开发中最常用的Layout之一,它的特点就是子view们是层叠覆盖,后添加的子view会覆盖在其他子view之上。

源码探究

构造函数

FrameLayout的构造函数很简单,处理一个FrameLayout的属性measureAllChildren:

public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
        @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
   
    super(context, attrs, defStyleAttr, defStyleRes);

    final TypedArray a = context.obtainStyledAttributes(
            attrs, R.styleable.FrameLayout, defStyleAttr, defStyleRes);

    if (a.getBoolean(R.styleable.FrameLayout_measureAllChildren, false)) {
   
        setMeasureAllChildren(true);
    }

    a.recycle();
}

measureAllChildren属性作用是设置是否在测量宽高时计算所有的子view。默认为false,即在measure阶段不会考虑状态为GONE的子view

LayoutParams

FrameLayout中定义了静态内部类LayoutParams继承自MarginLayoutParams,含有一个成员gravity:

public int gravity = UNSPECIFIED_GRAVITY;

因此支持子view设置父布局对齐方式。

测量onMeasure

由于FrameLayout帧布局的特点,它不像LinearLayout和RelativeLayout需要权重或相对关系等,只需要遍历子view,依次调用child测量,然后设置自身尺寸即可。但是也有细分不同情况,当FrameLayout的MeasureSpec模式为EXACTLY时,只需按常规流程进行即可。当模式为AT_MOST时,意味着FrameLayout自身尺寸不明确,需要反向依赖最大的那个child的尺寸,因此在遍历的同时需要记录最大尺寸。若同时存在child的LayoutParams设置了MATCH_PARENT,则意味着child又依赖父布局尺寸,因此在FrameLayout设置完自身尺寸后,需要再对它们进行一次测量。

FrameLayout中的宽高测量分为两部分。上部分为计算子view中的最大宽高,从而设置自身宽高。下部分为二次计算在上部分中未能精确计算宽高的子view的宽高,此时传给child的测量规格是根据FrameLayout测量后的宽高生成。

一、上部分:计算最大宽高

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   
    int count = getChildCount();

	// 该变量用于判断是否记录需要二次测量子view(若FrameLayout的父布局给定的测量规格中未指明精确的大小,则为true)。
    final boolean measureMatchParentChildren =
            MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
            MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
    // mMatchParentChildren为一个ArrayList集合,用于缓存需要二次测量宽高的子view。
    mMatchParentChildren.clear();

    int maxHeight = 0;
    int maxWidth = 0;
    int childState = 0;

	// 遍历子view
    for (int i = 0; i < count; i++) {
   
        final View child = getChildAt(i);
        // 判断child是否为GONE,或设置了measureAllChildren属性。
        if (mMeasureAllChildren || child.getVisibility() != GONE) {
   
        	// 调用child测量。
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
            final LayoutParams lp = (LayoutParams) child.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值