一個是斜體
兩個是加粗
三個是斜體加粗
以前都是繼承一個線性布局,相對布局,幀布局做點(diǎn)簡單修改,從來沒有繼承過viewGroup來寫一個,今天順道寫個測試下,結(jié)果發(fā)現(xiàn)各種坑啊,我以為就實(shí)現(xiàn)onLayout這個抽象方法就完事了,結(jié)果。。。
下邊代碼就是簡單實(shí)現(xiàn)了下線性布局類似的順序顯示,其他pading,margin都不考慮。就先看下功能
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
System.err.println(tag+"========onLayout======"+changed+"============="+l+"="+r+"=="+t+"==="+b);
int top=0;
for(int i=0;i<getChildCount();i++) {
View child=getChildAt(i);
if(child!=null) {
child.layout(l, top, r,top+child.getMeasuredHeight());
top+=child.getMeasuredHeight();
}
}
}
代碼如上,結(jié)果測試發(fā)現(xiàn)里邊的child都看不見,打印日志可以發(fā)現(xiàn)child.getMeasuredHeight()這個返回的一直是0,自然看不見了。完事我就把child.getMeasuredHeight()改成一個固定的比如200的值,發(fā)現(xiàn)是可以看到的,那原因就是child沒有進(jìn)行測量。
那就去看下FrameLayout咋弄的。代碼比較多,咱就抽出關(guān)鍵的代碼即可,也就是調(diào)用了measureChildWithMargins這個方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
System.err.println(tag+"===========onMeasure===========h="+getMeasuredHeight()+"===w="+getMeasuredWidth());
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for(int i=0;i<getChildCount();i++) {
View child=getChildAt(i);
if(child!=null) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
//android.view.ViewGroup$LayoutParams cannot be cast to android.view.ViewGroup$MarginLayoutParams
}
}
}
這下以為好了,運(yùn)行下直接掛了。異常
android.view.ViewGroup.LayoutParams cannot be cast to android.view.ViewGroup.MarginLayoutParams
原因就是如下的代碼,可以看到parama進(jìn)行了強(qiáng)轉(zhuǎn)
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
繼續(xù)看ViewGroup里有如下的幾個方法,里邊的LayoutParams就是ViewGroup里的一個靜態(tài)類public static class LayoutParams
/**
* Returns a new set of layout parameters based on the supplied attributes set.
*
* @param attrs the attributes to build the layout parameters from
*
* @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
* of its descendants
*/
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
/**
* Returns a safe set of layout parameters based on the supplied layout params.
* When a ViewGroup is passed a View whose layout params do not pass the test of
* {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
* is invoked. This method should return a new set of layout params suitable for
* this ViewGroup, possibly by copying the appropriate attributes from the
* specified set of layout params.
*
* @param p The layout parameters to convert into a suitable set of layout parameters
* for this ViewGroup.
*
* @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
* of its descendants
*/
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return p;
}
/**
* Returns a set of default layout parameters. These parameters are requested
* when the View passed to {@link #addView(View)} has no layout parameters
* already set. If null is returned, an exception is thrown from addView.
*
* @return a set of default layout parameters or null
*/
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
那咋辦了,修改下這幾個方法唄,讓默認(rèn)的child的params就是我們需要的MarginLayoutParams即可
@Override
protected android.view.ViewGroup.LayoutParams generateDefaultLayoutParams() {
// TODO Auto-generated method stub
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
@Override
public CharSequence getAccessibilityClassName() {
return TouchEventViewGroup.class.getName();
}
public static class LayoutParams extends MarginLayoutParams {
//省略掉構(gòu)造方法了
}
最后說明下如下的關(guān)系,marginLayoutParams就是多了寫margin參數(shù)而已
public static class MarginLayoutParams extends ViewGroup.LayoutParams
繼續(xù)研究,難道一定要用marginLayoutParams,當(dāng)然不一定了,如果你的child不需要設(shè)置margin那就不用了。
如下修改,剔除掉margin相關(guān)的,自然也就不要MarginLayoutParams
其實(shí),核心代碼就是child.measure(childWidthMeasureSpec, childHeightMeasureSpec);來測量child的大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
System.err.println(tag+"===========onMeasure===========h="+getMeasuredHeight()+"===w="+getMeasuredWidth());
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for(int i=0;i<getChildCount();i++) {
View child=getChildAt(i);
if(child!=null) {
// measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
//android.view.ViewGroup$LayoutParams cannot be cast to android.view.ViewGroup$MarginLayoutParams
ViewGroup.LayoutParams lp=child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeft() + getPaddingRight(), lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTop()+getPaddingBottom(), lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
如果沒有特殊的參數(shù),那么直接用MarginLayoutParams即可,如果有自己需要額外添加的參數(shù),那么可以繼承這個,自己寫一個
看下幾個常用的
LinearLayout,多了比重和重心
public static class LayoutParams extends ViewGroup.MarginLayoutParams
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a =
c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
a.recycle();
}
RelativeLayout
多了一對,相對位置的參數(shù),都放在下邊的數(shù)組里
private int[] mRules = new int[VERB_COUNT];
FrameLayout
多了個child的layout_gravity
public int gravity = UNSPECIFIED_GRAVITY;
總結(jié)下其實(shí)自定義ViewGroup的核心代碼就2行
child.measure(childWidthMeasureSpec, childHeightMeasureSpec); //對childView進(jìn)行大小測量
child.layout(int l, int t, int r, int b) 對child的位置進(jìn)行處理,上下左右4個點(diǎn)已經(jīng)決定了這個child的位置拉。
簡單不,嗯,簡單,再復(fù)雜不會了。
反正知道了基礎(chǔ)就這兩行,以后看別人寫的自定義ViewGroup就好理解了。