自定义控件---onMeasure和onLayout

Android的view组件显示主要经过mesure, layout和draw这三个过程。
1、mesure阶段: 告诉手机,你这个视图的大小是多少
调用mesure(int widthSpec, int heightSpec)方法,这个方法是final不能被重写。
在这个过程里会调用 onMesure(int widthSpec, int heightSpec)方法。

2、 layout阶段:告诉手机,你这个视图的位置在屏幕的什么位置
当组件设置好大小后,调用final layout(int l, int t, int r, int b)方法进行布局 ,这个方法是final不能被重写
在这个过程里会调用 onLayout(boolean changed, int l, int t, int r, int b)方法。

3、  draw阶段:在位置和大小确定的基础上,画出视图
当布局设置好后,会调用draw(Canvas canvas)方法还绘制控件。
        ㈠    先画Drawable背景
        ㈡    画完背景后,draw过程会调用onDraw(Canvas canvas)方法,绘制自身。
        ㈢    然后就是dispatchDraw(Canvas canvas)方法, 主要是分发给子组件进行绘制。
注意: ViewGroup容器组件的绘制,当它没有背景时直接调用的是dispatchDraw()方法, 而绕过了draw()方法,
当它有背景的时候就调用draw()方法,而draw()方法里包含了dispatchDraw()方法的调用。
因此要在ViewGroup上绘制东西的时候往往重写的是dispatchDraw()方法而不是onDraw()方法。



onMeasure(int, int)中,必须调用setMeasuredDimension(int width, int height)来存储测量得到的宽度和高度值,如果没有这么去做会触发异常IllegalStateException。

    /**
     *   onMeasure传入的两个参数是由上一层控件传入的大小.
     *   重写该方法时需要对计算控件的实际大小,
     *   然后调用setMeasuredDimension(int, int)设置实际大小
     *   onMeasure传入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值.
     *   我们需要通过:
     *       用int mode = MeasureSpec.getMode(widthMeasureSpec)得到模式,
     *       用int size = MeasureSpec.getSize(widthMeasureSpec)得到尺寸。
     *   mode共有三种情况,取值分别为
     *       MeasureSpec.UNSPECIFIED : 未指定尺寸。
     *                              这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式
     *       MeasureSpec.EXACTLY : 精确尺寸。
     *                              当我们将控件的layout_width或layout_height指定为具体数值时
     *                              如:andorid:layout_width="50dip",或者为FILL_PARENT是,都是控件大小已经确定的情况,
     *                              都是精确尺寸。
     *       MeasureSpec.AT_MOST : 最大尺寸。
     *                              当控件的layout_width或layout_height指定为WRAP_CONTENT时,
     *                              控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。
     *                              因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int measureWidth = measureWidth(widthMeasureSpec);
        int measureHeight = measureHeight(heightMeasureSpec);
        Log.i("TAG","父控件宽度:"+measureWidth+"父控件高度:"+measureHeight);
        measureChildren(widthMeasureSpec, heightMeasureSpec);  // 测量所有子控件的宽高
        // 调用setMeasuredDimension,指定视图在屏幕上的大小。屏幕上展示的大小以这个为准
        setMeasuredDimension(measureWidth, measureHeight);
    }

onLayout(boolean, int, int, int, int) 方法中,每个子视图都要调用各自的 layout(int, int, int, int) 方法。
    /**
     * @param changed 该参数指出当前ViewGroup的尺寸或者位置是否发生了改变
     * @param l、t、r、b 当前ViewGroup相对于其父控件的坐标位置
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int mTotalHeight = 0;
        int childCount = getChildCount();
        // 遍历所有子视图,将每个子控件的位置坐标设好
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            // 获取在onMeasure中计算的视图尺寸
            int measureHeight = childView.getMeasuredHeight();
            int measuredWidth = childView.getMeasuredWidth();
            // layout的四个参数分别是子控件 左、上、右、下。四个方位的坐标
            childView.layout(0, mTotalHeight, measuredWidth, mTotalHeight + measureHeight);
            mTotalHeight += measureHeight;
        }
    }










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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值