自定義控件系列:
秒懂OnMeasure
秒懂OnLayout
讓自定義ViewGroup裏的子控件支持Margin
讓自定義ViewGroup支持Padding
自定義ViewGroup的一個綜合實踐 FlowLayout
知識點:
- 讓自定義ViewGroup裏的子控件支持Margin,需要自定義ViewGroup在自身的onMeasure和onLayout裏去處理Margin,
因爲子控件寫margin之後,必然會讓自定義ViewGroup的寬高受到影響(所以onMeasure需要處理)
子控件寫margin之後,自定義ViewGroup在擺放這些子控件時候,必須考慮到margin,才能擺放到正確的位置所以onLayout需要處理)
- 但是如何得到子控件的Margin呢,不是你想的那麼容易直接就可拿到的,必須重寫下面的三個方法,但是好在寫法固定,照抄即可,想知道爲什麼,網上搜索吧,但是我覺得這是一個結論直接記住就行了,哪有那麼多爲什麼(點到爲止)
// 支持Margin的固定寫法,下面照抄就行了,至於爲什麼,可以去看源碼,但是我覺得直接記住就ok了
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
應用
1. 繼續寫上文的MyLinearLayout,讓他的子控件支持Margin
先看療效:
第一步:支持Margin的固定寫法
// 支持Margin的固定寫法,下面照抄就行了,至於爲什麼,可以去看源碼,但是我覺得直接記住就ok了
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
第二步:onMeasure裏考慮margin
private Point caculateAtMostSize(int widthMeasureSpec, int heightMeasureSpec) {
int width = 0;
int height = 0;
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
// 測量一下子控件的寬高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 得到MarginLayoutParams,margin就在這裏保存着
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 獲得子控件的寬高(需要加上對應的margin,讓控件的寬高包含margin,
// 這樣才能讓自定義的viewgroup在計算自身在AtMost模式的尺寸時候考慮到這些margin)
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
// 因爲我們的自定義View模擬的是豎向的LinearLayout,所以:
// 控件的寬度爲所有子控件裏,寬度最大的那個view的寬度,
// 控件高度是所有子空間的高度之和
width = Math.max(childWidth, width);
height += childHeight;
}
return new Point(width, height);
}
第三步:onLayout裏考慮Margin
代碼如下:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int top = 0;
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
// 得到MarginLayoutParams,margin就在這裏保存着
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 獲得子控件的寬高(需要加上對應的margin,讓控件的寬高包含margin)
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
// 佈局,就是設置好這個子控件的左上右下
// 在佈局的時候,考慮控件的margin,把子view擺放好
// (這是一個豎向的LinearLayout,想想如何佈置子控件)
child.layout(lp.leftMargin,
top + lp.topMargin,
childWidth - lp.rightMargin,
top + childHeight - lp.bottomMargin);
top += childHeight;
}
}