自定义属性:
方位包含 4 个方向:左上角、右上角、左下角、右下角,在 attrs.xml 文件中,定义一个名为layout_position 的属性,类型为 enum,枚举出这 4 个值。
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,则将该组件显示在容器的右下角。