自定义ViewGroup

目录

View的绘制流程

View的绘制流程都是无非就是onMeasure()、onDraw()、onLayout()三个方法,现在具体记录下这三个方法的作用。
面试【View的绘制流程】
第一步 performMeasure():用于指定和测量layout中所有控件的宽高,对于ViewGroup,先去测量里面的子孩子,根据子孩子的宽高再来计算和指定自己的宽高
对于View,它的宽高是由自己和父布局决定的。这句话的意思是其MeasureSpec由父容器的MeasureSpec和自身的layoutParams来共同决定的,当View是固定宽/高,父容器的MeasureSpec是什么,它的SpecMode的值都是EXACTLY。当view宽/高是Match_parent,父容器specMode的值是什么它就是啥,且大小不会超过父容器的剩余空间。当View宽/高是wrap_content,view的specMode总是AT_MOST,且大小不会超过父容器的剩余空间。
第二步 performLayout():用于摆放子布局,for循环所有子view,用child.layout()摆放ChildView,前提是控件不是GONE。View->layout()->onLayout()
第三步 PerformDraw():用于绘制自己还有子View,对于ViewGroup首先绘制自己的背景,for循环绘制子view调用子view的draw()方法,对于view绘制自己的背景,绘制自己现实的内容(TextView设置 background)
View->draw()->drawBackground();//画背景 onDraw(canvas);// 画自己 ViewGroup 默认情况下不会调用
dispatchDraw(canvas);// 画子View 不断的循环调用子View的 draw()

Tips

1.如果要获取Vie的高度,前提肯定需要调用测量方法,测量完毕之后才能获取宽高。
2.View的绘制流程是在onResume()之后才开始。
3.addView()、setVisibility()等等会调用requestLayout();重新走一遍View的绘制流程。
4.在onDraw()不要布局嵌套。

自定义ViewGroup - 流式布局 TagLayout

2.1 onMeasure()根据子View指定ViewGroup的宽高

    private List<List<View>> mChildViews=new ArrayList<>();
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //清空集合
        mChildViews.clear();

        //1.1.1 for循环测量子view
        int childCount=getChildCount();
        //获取手机屏幕宽度
        int width=MeasureSpec.getSize(widthMeasureSpec);
        //ViewGroup的高度不需要占满屏幕 根据子View来就行
        int height=getPaddingTop()+getPaddingBottom();
        //paddingLeft内边距 marginleft外边距
        //lineWidth表示在一行中所占的宽度   该变量主要为了控制换行
        int lineWidth=getPaddingLeft();
        //子view高度不一致的情况下
        int maxheight=0;

        //有几个ArrayList说明有几行 换行拿到paddingLeft
        //在拿到不同的Arraylist存的childView进行摆放
        //这个ArrayList放不需要换行的控件
        ArrayList<View> childViews=new ArrayList<>();
        mChildViews.add(childViews);


        for(int i=0;i<childCount;i++){
            View childView=getChildAt(i);
            //这段话执行之后就可以获取子view的宽高,因为会调用子view的onMeasure
            measureChild(childView,widthMeasureSpec,heightMeasureSpec);
            //只有通过系统的MarginLayoutParams才能得到LeftMargin和RightMargin
            //ViewGroup.LayoutParams得不到LeftMargin和RightMargin
            //LinearLayout.LayoutParams有所有需要的margin值
            ViewGroup.MarginLayoutParams params=(MarginLayoutParams) childView.getLayoutParams();
            //得到宽高
            if(lineWidth+(childView.getMeasuredWidth()+params.leftMargin+params.rightMargin)>width){
                //换行,累加高度,加上上一行条目中最大的高度
                height+=childView.getMeasuredHeight()+params.bottomMargin+params.topMargin;
                lineWidth=childView.getMeasuredWidth()+params.rightMargin+params.leftMargin;
                //这个ArrayList放需要换行的控件
                childViews=new ArrayList<>();
                mChildViews.add(childViews);
            }else{
                //如果控件宽度没有超过屏幕宽度,说明可以放多个控件
                //高度应该取多个控件中最高的
                 lineWidth+=childView.getMeasuredWidth()+params.rightMargin+params.leftMargin;
                 maxheight=Math.max((childView.getMeasuredHeight()+params.topMargin+params.bottomMargin),maxheight);
            }
            childViews.add(childView);
        }
        //高度等于不断换行增加的高度以及没有换行时的一排View控件中的最大高度
        height+=maxheight;
        //1.1.2 根据子view计算和指定自己的宽高
        setMeasuredDimension(width,height);
    }

2.2 onLayout()

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
             //在onMeasure()中我们主要做的是得到要画的ViewGroup的宽高
             //在onLayout()中我们要做的是设置子View在什么位置摆放
             //不断调用此方法摆放子view
        int left,top=getPaddingTop(),right,bottom;
        for (List<View> childViews : mChildViews) {
            left = getPaddingLeft();

            for (View childView : childViews) {
                ViewGroup.MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
                left += params.leftMargin;
                right = left + childView.getMeasuredWidth();
                int childTop = top + childView.getMeasuredHeight();
                bottom = childTop + childView.getMeasuredHeight();
                childView.layout(left, childTop, right, bottom);
                //left在摆放的过程中在不断叠加
                left += childView.getMeasuredWidth() + params.rightMargin;
            }

            ViewGroup.MarginLayoutParams params = (MarginLayoutParams) childViews.get(0).getLayoutParams();
            top += childViews.get(0).getMeasuredHeight() + params.topMargin + params.bottomMargin;
        }

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值