自定義ViewGroup-自定義LayoutParams支援顯示方位
阿新 • • 發佈:2018-12-23
自定義屬性:
<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>
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,則將該元件顯示在容器的右下角。
結果顯示: