自定义ViewGroup系列---基础理论篇

从本篇开始,我们来讲述自定义ViewGroup的相关知识。本系列还是来自 李赞红 老师的《Android自定义组件开发详解》,书看完了,如果仅仅是看完了,没有任何输出的话,那我们对书中知识的吸收只怕是少得可怜。只有从输入–》输出–》应用 这样形成一个闭环,我们才能说自己理解这个这个知识点,也才能将这个知识点列入自己的知识框架中。

废话不多说了,我们现在就进入自定义ViewGroup的学习吧!

一般情况下,自定义View,更关注的是组件的外观和功能。比如我们前面自定义view系列中的圆形头像、验证码等等;而自定义ViewGroup,关注的则是容器内的组件怎么排列和摆放,比如LinearLayout的组件只能水平或者垂直排列,帧布局FrameLayout中的组件可以重叠。。。

ViewGroup作为容器类的父类,有它自己鲜明的特征,我们必须先要了解下。

在ViewGroup中定义了一个View[]类型的数组 mChildren,该数组保存了容器中所有的子组件,负责维护组件的add、remove、组件顺序等功能。
相关API如下:

  • public int getChildCount()
  • public View getChildAt(int index)
  • public void addView(View child, int index, LayoutParams params)
  • public void addView(View child, int index)
  • public void addView(View child)
  • public void removeViewAt(int index)
  • public void removeView(View view)
  • protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)

ViewGroup运行的基本流程大致为:
1, 测量容器尺寸
重写onMeasure()方法测量容器大小,和自定义View的区别是,在测量容器大小之前,必须先调用measureChildren()方法测量所有子组件的大小。

// measureChildren ==》measureChild
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
   final int size = mChildrenCount;
   final View[] children = mChildren;
   for (int i = 0; i < size; ++i) {
       final View child = children[i];
       if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
           measureChild(child, widthMeasureSpec, heightMeasureSpec);//*******
       }
   }
}
// measureChild 中的关键方法getChildMeasureSpec
protected void measureChild(View child, int parentWidthMeasureSpec,
            int parentHeightMeasureSpec) {
   final LayoutParams lp = child.getLayoutParams();

   final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
           mPaddingLeft + mPaddingRight, lp.width);
   final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
           mPaddingTop + mPaddingBottom, lp.height);

   child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

根据父布局的模式和大小, 辅助子布局的模式和大小,一起来确认子布局的模式和大小(resultSize和resultMode)
在这里插入图片描述

2,确定每个子组件的位置(自定义ViewGroup的难点部分)
重写onLayout()方法确定每个子组件的位置,在onLayout中,调用View的layout()确定自组件的位置。

3,绘制容器
重写onDraw()方法,其实ViewGroup类并没有重写onDraw(),除非特殊要求,比如Linearlayout重写了该方法用于绘制水平或垂直分割条。

参照上面的步骤,我们给出一个简单的自定义viewGroup,叫做SizeViewGroup。
注意:SizeViewGroup只是为了说明自定义VierGroup的思路,其本身不具有实际意义。

我们假定这个SizeViewGroup是一个带红色边框的,且是圆角的矩行,其有一个TextView类型的子view。

public class SizeViewGroup extends ViewGroup {
    public SizeViewGroup(Context context) {
        this(context,null,0);
    }

    public SizeViewGroup(Context context, AttributeSet attrs) {
        this(context,attrs,0);
    }

    public SizeViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs,defStyleAttr);
		//new 子view
        TextView textView = new TextView(context);
		//设置子view的大小
        ViewGroup.LayoutParams layoutParams = new LayoutParams(200,200);
        textView.setText("Android");
        textView.setBackgroundColor(Color.YELLOW);
		//将子view添加进来
        addView(textView,layoutParams);

        this.setBackgroundColor(Color.alpha(255));


    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
    	//定位子view
        View textView = getChildAt(0);
        textView.layout(50,50,textView.getMeasuredWidth() + 50, textView.getMeasuredHeight() + 50);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	    //测量子View
        this.measureChildren(widthMeasureSpec, heightMeasureSpec);
    	//设置SizeViewGroup自身大小(即使在layout.xml文件中指定了layout_width/layout_height,也没有效果了。)
        setMeasuredDimension(500,500);
    }

    @Override
    protected void onDraw(Canvas canvas) {
		//绘制SizeViewGroup自身的边框效果
        RectF rect = new RectF(0,0,getMeasuredWidth(),getMeasuredHeight());
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(2);
        paint.setColor(Color.RED);

        Path path = new Path();
        path.addRoundRect(rect,20,20,Path.Direction.CCW);
        canvas.drawPath(path,paint);

        super.onDraw(canvas);
    }
}

运行后SizeVieGroup的效果如下:

在这里插入图片描述

我们在xml中是这样定义SizeViewGroup的

<com.example.cyy.customerview.view.SizeViewGroup
        android:layout_width="10dp"
        android:layout_height="10dp"/>

看,即使我们给出的具体宽高值,但是由于在java的onMeasure()方法中,直接设置setMeasuredDimension(500,500);xml中的值就会被忽略。因此,我们也得知,真正决定控件大小的不是xml中设置的值,而是measure方法。

DONE
此系列后续持续更新。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值