一个是斜体
两个是加粗
三个是斜体加粗
以前都是继承一个线性布局,相对布局,帧布局做点简单修改,从来没有继承过viewGroup来写一个,今天顺道写个测试下,结果发现各种坑啊,我以为就实现onLayout这个抽象方法就完事了,结果。。。
下边代码就是简单实现了下线性布局类似的顺序显示,其他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();
}
}
}
代码如上,结果测试发现里边的child都看不见,打印日志可以发现child.getMeasuredHeight()这个返回的一直是0,自然看不见了。完事我就把child.getMeasuredHeight()改成一个固定的比如200的值,发现是可以看到的,那原因就是child没有进行测量。
那就去看下FrameLayout咋弄的。代码比较多,咱就抽出关键的代码即可,也就是调用了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
}
}
}
这下以为好了,运行下直接挂了。异常
android.view.ViewGroup.LayoutParams cannot be cast to android.view.ViewGroup.MarginLayoutParams
原因就是如下的代码,可以看到parama进行了强转
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);
}
继续看ViewGroup里有如下的几个方法,里边的LayoutParams就是ViewGroup里的一个静态类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);
}
那咋办了,修改下这几个方法呗,让默认的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 {
//省略掉构造方法了
}
最后说明下如下的关系,marginLayoutParams就是多了写margin参数而已
public static class MarginLayoutParams extends ViewGroup.LayoutParams
继续研究,难道一定要用marginLayoutParams,当然不一定了,如果你的child不需要设置margin那就不用了。
如下修改,剔除掉margin相关的,自然也就不要MarginLayoutParams
其实,核心代码就是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);
}
}
}
如果没有特殊的参数,那么直接用MarginLayoutParams即可,如果有自己需要额外添加的参数,那么可以继承这个,自己写一个
看下几个常用的
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
多了一对,相对位置的参数,都放在下边的数组里
private int[] mRules = new int[VERB_COUNT];
FrameLayout
多了个child的layout_gravity
public int gravity = UNSPECIFIED_GRAVITY;
总结下其实自定义ViewGroup的核心代码就2行
child.measure(childWidthMeasureSpec, childHeightMeasureSpec); //对childView进行大小测量
child.layout(int l, int t, int r, int b) 对child的位置进行处理,上下左右4个点已经决定了这个child的位置拉。
简单不,嗯,简单,再复杂不会了。
反正知道了基础就这两行,以后看别人写的自定义ViewGroup就好理解了。
网友评论