对 AbsoluteLayout 的一点理解

菜鸟记录一下,把之前的笔记抄到这里

/**
 * A layout that lets you specify exact locations (x/y coordinates) of its
 * children. Absolute layouts are less flexible and harder to maintain than
 * other types of layouts without absolute positioning.
 *
 * <p><strong>XML attributes</strong></p> <p> See {@link
 * android.R.styleable#ViewGroup ViewGroup Attributes}, {@link
 * android.R.styleable#View View Attributes}</p>
 * 
 * @deprecated Use {@link android.widget.FrameLayout}, {@link android.widget.RelativeLayout}
 *             or a custom layout instead.
 */
@Deprecated
@RemoteView
public class AbsoluteLayout extends ViewGroup {

1、哈哈,首先,AbsoluteLayout 继承自 ViewGroup;
2、同样,AbsoluteLayout 需要通过onMeasure 和 onLayout:
a、首先,需要通过 attrs 获取 AbsoluteLayout 的自定义属性,不难理解;

/**
         * Creates a new set of layout parameters. The values are extracted from
         * the supplied attributes set and context. The XML attributes mapped
         * to this set of layout parameters are:
         *
         * <ul>
         *   <li><code>layout_x</code>: the X location of the child</li>
         *   <li><code>layout_y</code>: the Y location of the child</li>
         *   <li>All the XML attributes from
         *   {@link android.view.ViewGroup.LayoutParams}</li>
         * </ul>
         *
         * @param c the application environment
         * @param attrs the set of attributes from which to extract the layout
         *              parameters values
         */
        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            TypedArray a = c.obtainStyledAttributes(attrs,
                    com.android.internal.R.styleable.AbsoluteLayout_Layout);
            x = a.getDimensionPixelOffset(
                    com.android.internal.R.styleable.AbsoluteLayout_Layout_layout_x, 0);
            y = a.getDimensionPixelOffset(
                    com.android.internal.R.styleable.AbsoluteLayout_Layout_layout_y, 0);
            a.recycle();
        }

b、onMeasure

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

        int maxHeight = 0;
        int maxWidth = 0;

        // Find out how big everyone wants to be
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        // Find rightmost and bottom-most child
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                int childRight;
                int childBottom;

                AbsoluteLayout.LayoutParams lp
                        = (AbsoluteLayout.LayoutParams) child.getLayoutParams();

                childRight = lp.x + child.getMeasuredWidth();
                childBottom = lp.y + child.getMeasuredHeight();

                maxWidth = Math.max(maxWidth, childRight);
                maxHeight = Math.max(maxHeight, childBottom);
            }
        }

        // Account for padding too
        maxWidth += mPaddingLeft + mPaddingRight;
        maxHeight += mPaddingTop + mPaddingBottom;

        // Check against minimum height and width
        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
                resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
    }

1)首先,通过 getChildCount() 获取 子控件的个数;
2)由注释 Find out how big everyone wants to be 可知,第二步通过measureChildren(widthMeasureSpec, heightMeasureSpec); 对子控件进行测量;
3)接下来进入了一个 for 循环,通过注释 // Find rightmost and bottom-most child 可知,需要找出最右边和最下边的子控件,如下
这里写图片描述

从左上角开始,使用 wrap_content,通过上图可以知道,需要找出 最右边 和 最下边的 子view的 right 或者 bottom,所以接下来 for 循环中 便是 通过 每次获取 以及 比较来找到想要的结果:
1)首先,获取每个 子view,同样,当 child.getVisibility() 为 GONE 的时候,我们可以直接忽略,所以判断条件为 不是 GONE;
2)通过 AbsoluteLayout.LayoutParams 获取每个子view的 layoutParams,layoutParams 封装了view 的 x 以及 y

public static class LayoutParams extends ViewGroup.LayoutParams {
        /**
         * The horizontal, or X, location of the child within the view group.
         */
        public int x;
        /**
         * The vertical, or Y, location of the child within the view group.
         */
        public int y;

上面是 AbsoluteLayout 的 LayoutParams ,对比一下 RelativeLayout 的 LayoutParams,

 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
        @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
            @ViewDebug.IntToString(from = ABOVE,               to = "above"),
            @ViewDebug.IntToString(from = ALIGN_BASELINE,      to = "alignBaseline"),

通过对比可知,AbsoluteLayout 的 LayoutParams 直接继承了 ViewGroup 的 LayoutParams ,而 RelativeLayout 的 继承的 是 MarginLayoutParams,可以得知 AbsoluteLayout 不支持 margin 属性,所以 在 onmeasure 的 for 循环中对每个子View 进行测量无需考虑 margin;
3)接下来,通过 每个 子View 的 x 以及 y 坐标,以及子View 宽高,获得最大的 height 和 width

                childRight = lp.x + child.getMeasuredWidth();
                childBottom = lp.y + child.getMeasuredHeight();

                maxWidth = Math.max(maxWidth, childRight);
                maxHeight = Math.max(maxHeight, childBottom);

4)for 循环之后,执行以下代码:

 // Account for padding too
        maxWidth += mPaddingLeft + mPaddingRight;
        maxHeight += mPaddingTop + mPaddingBottom;

        // Check against minimum height and width
        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
                resolveSizeAndState(maxHeight, heightMeasureSpec, 0));

主要是通过 for 循环计算得出的最大的 height 以及 width,并且加上 最外层 AbsoluteLayout 的 padding,如下:
这里写图片描述

AbsoluteLayout 添加了 mPaddingLeft 和 mPaddingBottom 之后,子View的坐标轴会 向右 移动 mPaddingLeft 以及向下移动 mPaddingBottom,所以 for 循环之后需要 加上 这两个参数;

c、onLayout
onLayout 代码如下:

@Override
    protected void onLayout(boolean changed, int l, int t,
            int r, int b) {
        int count = getChildCount();

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

                AbsoluteLayout.LayoutParams lp =
                        (AbsoluteLayout.LayoutParams) child.getLayoutParams();

                int childLeft = mPaddingLeft + lp.x;
                int childTop = mPaddingTop + lp.y;
                child.layout(childLeft, childTop,
                        childLeft + child.getMeasuredWidth(),
                        childTop + child.getMeasuredHeight());

            }
        }
    }

同样,通过 getChildCount() 获取数目之后,在for循环中摆放 子View的位置,AbsoluteLayout 通过 x和 y 来确定 位置,所以 通过 x、y 以及 子view 的height 以及 width 即可,调用 child.layout();四个参数分别为 子view 的 x、子view 的y、子view 的 width、子view 的height;

3、接下来,有一个方法

 @Override
    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) 
    {
        return new AbsoluteLayout.LayoutParams(getContext(), attrs);
    }`
 我们首先在 类 LayoutInflater 中可以看到如下代码:
             final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;

                    // Create layout params that match root, if supplied
                    params = root.generateLayoutParams(attrs);

可以知道,generateLayoutParams 方法将我们自定义 的属性转换成 父布局的 LayoutParams,所以我们在 xml 中使用属性也是通过这个方法转换。
以上便是个人对 AbsoluteLayout 的一点理解,不对之处欢迎吐槽。

猜你喜欢

转载自blog.csdn.net/handsonn/article/details/53364373