博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android (单帧布局)FrameLayout
阅读量:6884 次
发布时间:2019-06-27

本文共 5940 字,大约阅读时间需要 19 分钟。

hot3.png

感觉FrameLayout很神秘,是因为用到它的地方少,一直觉得它鸡肋,原来是自己无知。最近需要实现一些layer的效果,就用到了它。它的用法很简单,这里就不多说了,这里就说说它的原理吧。

FrameLayout类里面没有什么东西,主要说的还是它的自身的布局参数FrameLayout.LayoutParams,布局参数类继承MarginLayoutParams。看名词就知道,就是控制view的外边距的,FrameLayout.LayoutParams本身自己定义的参数只有一个gravity。

好了,既然有了布局参数,那就会有空间的一些属性吧。首先FrameLayout也是一个View,所以他必然有Padding(view的内边距)相关属性。这里我们重点关注它的onMeasure与onLayout:

onMeasure意思就是计算出该ViewGroup的布局大小及孩子View的布局大小

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int count = getChildCount();

        final boolean measureMatchParentChildren =

                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
        mMatchParentChildren.clear();

        int maxHeight = 0;

        int maxWidth = 0;
        int childState = 0;

        for (int i = 0; i < count; i++) {

            final View child = getChildAt(i);
            if (mMeasureAllChildren || child.getVisibility() != GONE) {
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                maxWidth = Math.max(maxWidth,
                        child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                maxHeight = Math.max(maxHeight,
                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                childState = combineMeasuredStates(childState, child.getMeasuredState());
                if (measureMatchParentChildren) {
                    if (lp.width == LayoutParams.MATCH_PARENT ||
                            lp.height == LayoutParams.MATCH_PARENT) {
                        mMatchParentChildren.add(child);
                    }
                }
            }
        }

        // Account for padding too

        maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
        maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

        // Check against our minimum height and width

        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

        // Check against our foreground's minimum height and width

        final Drawable drawable = getForeground();
        if (drawable != null) {
            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
        }

        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),

                resolveSizeAndState(maxHeight, heightMeasureSpec,
                        childState << MEASURED_HEIGHT_STATE_SHIFT));

        count = mMatchParentChildren.size();

        if (count > 1) {
            for (int i = 0; i < count; i++) {
                final View child = mMatchParentChildren.get(i);

                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

                int childWidthMeasureSpec;
                int childHeightMeasureSpec;
                if (lp.width == LayoutParams.MATCH_PARENT) {
                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
                            getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
                            lp.leftMargin - lp.rightMargin,
                            MeasureSpec.EXACTLY);
                } else {
                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                            getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                            lp.leftMargin + lp.rightMargin,
                            lp.width);
                }
                if (lp.height == LayoutParams.MATCH_PARENT) {
                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
                            getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
                            lp.topMargin - lp.bottomMargin,
                            MeasureSpec.EXACTLY);
                } else {
                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                            lp.topMargin + lp.bottomMargin,
                            lp.height);
                }

                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

            }
        }
    }

简单的说父容器的width就是所有孩子View的width取最大值加View外边距和它的内边距:child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin+getPaddingLeftWithForeground() + getPaddingRightWithForeground();如果ViewGroup有图片,还有跟图片比较取较大值,容器的height也是同样的道理。FrameLayout孩子View的布局大小跟普通View大小确定是一样的。

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

        final int count = getChildCount();

        final int parentLeft = getPaddingLeftWithForeground();

        final int parentRight = right - left - getPaddingRightWithForeground();

        final int parentTop = getPaddingTopWithForeground();

        final int parentBottom = bottom - top - getPaddingBottomWithForeground();

        mForegroundBoundsChanged = true;

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                final int width = child.getMeasuredWidth();

                final int height = child.getMeasuredHeight();

                int childLeft;

                int childTop;

                int gravity = lp.gravity;

                if (gravity == -1) {
                    gravity = DEFAULT_CHILD_GRAVITY;
                }

                final int layoutDirection = getResolvedLayoutDirection();

                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;

                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {

                    case Gravity.LEFT:
                        childLeft = parentLeft + lp.leftMargin;
                        break;
                    case Gravity.CENTER_HORIZONTAL:
                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                        lp.leftMargin - lp.rightMargin;
                        break;
                    case Gravity.RIGHT:
                        childLeft = parentRight - width - lp.rightMargin;
                        break;
                    default:
                        childLeft = parentLeft + lp.leftMargin;
                }

                switch (verticalGravity) {

                    case Gravity.TOP:
                        childTop = parentTop + lp.topMargin;
                        break;
                    case Gravity.CENTER_VERTICAL:
                        childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                        lp.topMargin - lp.bottomMargin;
                        break;
                    case Gravity.BOTTOM:
                        childTop = parentBottom - height - lp.bottomMargin;
                        break;
                    default:
                        childTop = parentTop + lp.topMargin;
                }

                child.layout(childLeft, childTop, childLeft + width, childTop + height);

            }
        }
    }

从代码中可以看出,孩子View左上角坐标的X值就是ViewGroup的左内边距加上view的外边距,Y值同理。

哎,怎么感觉自己写着写着,还把问题说复杂了点,感觉还是看代码能明白。

还有,我在2.2版本的代码上,gravity与margin属性必须同时设置,margin才能发挥作用,如果只有margin属性而没有设置gravity,就没有效果。如果有谁知道,欢迎指教哈。

在上一篇中说了下android里drag and drop 一个View,当时那个imageView放在一个LinearLayout里面,事实上放在FrameLayout同样适用,因为他们的布局参数都继承MarginLayoutParams类,所以通过margin控制他们的布局位置同样在FrameLayout使用。事实上GridLayout,LinearLayout,RelativeLayout,FrameLayout的布局参数都是继承MarginLayoutParams的。

转载于:https://my.oschina.net/gavinjin/blog/42213

你可能感兴趣的文章
ORACLE RAC 之I/O分离--hangcheck-timer模块配置
查看>>
有用的runas命令,尤其对域用户环境
查看>>
Oracle 备份与恢复学习笔记(11)
查看>>
Windows Home Server 是什么?
查看>>
VC++动态链接库(DLL)编程深入浅出(一)
查看>>
内存技术术语不完全手册
查看>>
20个Linux命令及Linux终端的趣事
查看>>
Howto:在 Windows Server 2008 64-Bit 上安装 WSUS v3.0 with SP1
查看>>
Java 注释
查看>>
从无到有的升级-缓存
查看>>
Android开发技巧——使用RecyclerView实现分组列表
查看>>
磁盘加密技术保障数据安全之七种武器
查看>>
java并发编程(2)--volatile(转)
查看>>
Spring中BeanUtils.copyProperties方法测试
查看>>
Java外挂开发入门示例
查看>>
centos 5.6安装nginx+mysql+php(php-fpm)+phpmyadmin总结
查看>>
SQL记录一下
查看>>
Linux时间修改与同步
查看>>
水晶报表技术(9)——.NET环境下水晶报表使用总结(上)
查看>>
Oracle数据库中NULL异常使用的教训
查看>>