1. 程式人生 > >自定義ViewGroup-自定義LayoutParams支援顯示方位

自定義ViewGroup-自定義LayoutParams支援顯示方位

自定義屬性:

<resources>
    <declare-styleable name="CustomViewGroup5">
        <attr name="layout_position" format="enum">
            <enum name="left_top" value="0" />
            <enum name="right_top" value="1" />
            <enum name="left_bottom" value="2" />
            <enum name="right_bottom" value="3" />
        </attr>
    </declare-styleable>
</resources>

方位包含 4 個方向:左上角、右上角、左下角、右下角,在 attrs.xml 檔案中,定義一個名為layout_position 的屬性,型別為 enum,枚舉出這 4 個值。



activity_main:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp">

    <com.example.user.myapplication4.CustomViewGroup5
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#FFCCCCCC"
        android:padding="10dp">

        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:background="@android:color/holo_blue_bright"
            android:gravity="center"
            android:text="A"
            android:textColor="#FFFFFFFF"
            custom:layout_position="right_bottom" />

        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@android:color/holo_blue_dark"
            android:gravity="center"
            android:text="B"
            android:textColor="#FFFFFFFF"
            custom:layout_position="left_bottom" />

        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:background="@android:color/holo_red_dark"
            android:gravity="center"
            android:text="C"
            android:textColor="#FFFFFFFF"
            custom:layout_position="right_top" />

        <TextView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@android:color/holo_green_light"
            android:gravity="center"
            android:text="D"
            android:textColor="#FFFFFFFF"
            custom:layout_position="left_top" />
    </com.example.user.myapplication4.CustomViewGroup5>
</LinearLayout>


CustomViewGroup5:

public class CustomViewGroup5 extends ViewGroup {
    public CustomViewGroup5(Context context) {
        super(context);
    }

    public CustomViewGroup5(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomViewGroup5(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

    }

    /**
     * @param attrs
     * @return
     */
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        Log.i("log", "generateLayoutParams:AttributeSet");
        return new PositionLayoutParams(this.getContext(), attrs);
    }

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        Log.i("log", "generateLayoutParams:LayoutParams");
        return new PositionLayoutParams(p);
    }

    /**
     * @return
     */
    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        Log.i("log", "generateDefaultLayoutParams");
        return new PositionLayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT);
    }

    /**
     * 自定義LayoutParams
     */
    public static class PositionLayoutParams extends ViewGroup.MarginLayoutParams {
        public static final int LEFT_TOP = 0;
        public static final int RIGHT_TOP = 1;
        public static final int LEFT_BOTTOM = 2;
        public static final int RIGHT_BOTTOM = 3;
        public static final int NONE = -1;
        public int position;

        public PositionLayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            TypedArray typedArray = c.obtainStyledAttributes(attrs, R.styleable.CustomViewGroup5);
            position = typedArray.getInt(R.styleable.CustomViewGroup5_layout_position, NONE);

            typedArray.recycle();
        }

        public PositionLayoutParams(int width, int height) {
            super(width, height);
        }

        public PositionLayoutParams(MarginLayoutParams source) {
            super(source);
        }

        public PositionLayoutParams(LayoutParams source) {
            super(source);

        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //測量所有的子View
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        //測量自己的寬高
        int width = measureWidth(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);

        setMeasuredDimension(width, height);
    }

    private int measureHeight(int widthMeasureSpec) {
        //父控制元件建議自己測量的值
        int measureMode = MeasureSpec.getMode(widthMeasureSpec);
        int measureSize = MeasureSpec.getSize(widthMeasureSpec);

        int width = 0;

        if (measureMode == MeasureSpec.EXACTLY) {
            width = measureSize;
        } else if (measureMode == MeasureSpec.AT_MOST) {
            int aWidth = 0;
            int bWidth = 0;
            int cWidth = 0;
            int dWidth = 0;

            int haMargin = 0;
            int hbMargin = 0;
            int hcMargin = 0;
            int hdMargin = 0;


            for (int i = 0; i < getChildCount(); i++) {
                MarginLayoutParams layoutParams = (MarginLayoutParams) getChildAt(i).getLayoutParams();
                if (i == 0) {
                    aWidth = getChildAt(i).getMeasuredWidth();//左邊View的寬度
                    haMargin += layoutParams.topMargin + layoutParams.bottomMargin;
                } else if (i == 1) {
                    bWidth = getChildAt(i).getMeasuredWidth();
                    hbMargin += layoutParams.topMargin + layoutParams.bottomMargin;
                } else if (i == 2) {
                    cWidth = getChildAt(i).getMeasuredWidth();
                    hcMargin += layoutParams.topMargin + layoutParams.bottomMargin;
                } else if (i == 3) {
                    dWidth = getChildAt(i).getMeasuredWidth();
                    hdMargin = layoutParams.topMargin + layoutParams.bottomMargin;
                }
            }
            //getPaddingTop:top離top的內邊距   getPaddingBottom:bottom離底的內邊距
            width = Math.max(aWidth, bWidth) + Math.max(cWidth, dWidth)
                    + getPaddingTop() + getPaddingBottom()
                    + Math.max(haMargin, hbMargin) + Math.max(hcMargin, hdMargin);//取到寬度的最大值
        }
        return width;

    }

    private int measureWidth(int heightMeasureSpec) {
        int measureMode = MeasureSpec.getMode(heightMeasureSpec);
        int measureSize = MeasureSpec.getSize(heightMeasureSpec);

        int height = 0;

        if (measureMode == MeasureSpec.EXACTLY) {
            height = measureSize;
        } else if (measureMode == MeasureSpec.AT_MOST) {
            int aHeight = 0;
            int bHeight = 0;
            int cHeight = 0;
            int dHeight = 0;

            int waMargin = 0;
            int wbMargin = 0;
            int wcMargin = 0;
            int wdMargin = 0;

            for (int i = 0; i < getChildCount(); i++) {
                MarginLayoutParams layoutParams = (MarginLayoutParams) getChildAt(i).getLayoutParams();
                if (i == 0) {
                    aHeight = getChildAt(i).getMeasuredHeight();
                    waMargin = layoutParams.leftMargin + layoutParams.rightMargin;
                } else if (i == 1) {
                    bHeight = getChildAt(i).getMeasuredHeight();
                    wbMargin = layoutParams.leftMargin + layoutParams.rightMargin;
                } else if (i == 2) {
                    cHeight = getChildAt(i).getMeasuredHeight();
                    wcMargin = layoutParams.leftMargin + layoutParams.rightMargin;
                } else if (i == 3) {
                    dHeight = getChildAt(i).getMeasuredHeight();
                    wdMargin = layoutParams.leftMargin + layoutParams.rightMargin;
                }
            }
            height = Math.max(aHeight, bHeight) + Math.max(cHeight, dHeight)
                    + getPaddingLeft() + getPaddingRight()
                    + Math.max(waMargin, wbMargin) + Math.max(wcMargin, wdMargin);//取到寬度的最大值
        }
        return height;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int leftPadding = getPaddingLeft();
        int rightPadding = getPaddingRight();
        int topPadding = getPaddingTop();
        int bottomPadding = getPaddingBottom();

        for (int i = 0; i < getChildCount(); i++) {
            View childView = getChildAt(i);//某一個子View

            PositionLayoutParams layoutParams = (PositionLayoutParams) childView.getLayoutParams();
            int leftMargin = layoutParams.leftMargin;
            int topMargin = layoutParams.topMargin;
            int rightMargin = layoutParams.rightMargin;
            int bottomMargin = layoutParams.bottomMargin;
            int position = layoutParams.position;

            if (i == 0 && position == PositionLayoutParams.NONE || position == PositionLayoutParams.LEFT_TOP) {
                childView.layout(leftPadding + leftMargin,
                        topPadding + topMargin,
                        childView.getMeasuredWidth() + leftPadding + leftMargin,
                        childView.getMeasuredHeight() + topPadding + topMargin);
            } else if (i == 1 && position == PositionLayoutParams.NONE
                    || layoutParams.position ==
                    PositionLayoutParams.RIGHT_TOP) {
                childView.layout(getMeasuredWidth() - childView.getMeasuredWidth() - rightPadding - rightMargin,
                        topPadding + topMargin,
                        getMeasuredWidth() - rightPadding - rightMargin,
                        childView.getMeasuredHeight() + topPadding + topMargin);
            } else if (i == 2 && position == PositionLayoutParams.NONE
                    || layoutParams.position ==
                    PositionLayoutParams.LEFT_BOTTOM) {
                childView.layout(leftPadding + leftMargin,
                        getMeasuredHeight() - childView.getMeasuredHeight()
                                - bottomPadding - bottomMargin,
                        childView.getMeasuredWidth() + leftPadding + leftMargin,
                        getMeasuredHeight() - bottomPadding - bottomMargin);

            } else if (i == 3 && position == PositionLayoutParams.NONE
                    || layoutParams.position ==
                    PositionLayoutParams.RIGHT_BOTTOM) {
                childView.layout(getMeasuredWidth() - childView.getMeasuredWidth() - rightPadding - rightMargin,
                        getMeasuredHeight() - childView.getMeasuredHeight() - bottomPadding - bottomMargin,
                        getMeasuredWidth() - rightPadding - rightMargin,
                        getMeasuredHeight() - bottomPadding - bottomMargin);

            }
        }
    }
}

PositionLayoutParams(Context c, AttributeSet attrs)讀取了 layout_position 屬性值,儲存在 position成員變數中,如果未讀取到該屬性,則預設值為 NONE。其次定義了 4 個常量與 layout_position屬性的 4 個列舉值相對應。ViewGroup 類重寫的 generateLayoutParams()和 generateDefaultLayoutParams()方法返回的LayoutParams 為 PositionLayoutParams 物件。 其 中 public LayoutParams generateLayoutParams(AttributeSetattrs)方法將 attrs 傳入 public PositionLayoutParams(Context c, AttributeSet attrs)構造方法,所以,PositionLayoutParams 才能讀取到 layout_position 的屬性值。在 onLayout()方法中,我們需要根據當前子元件的 PositionLayoutParams 的 position 屬性來確定方位,這裡有兩種情況:一種是沒有為元件定義方位時,依舊按照從左往右、從上往下的方式進行放置;另一種是如果元件定義了特定方位,如 right_bottom,則將該元件顯示在容器的右下角。



結果顯示: