自定义控件(一)

构造方法

public CustomView(Context context) {
    super(context);
}

public CustomView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
}

public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
}

onMeasure() 测量View大小

测量模式一共有三种

模式 描述
MeasureSpec.UNSPECIFIED 未指定尺寸,父控件没有给子view任何限制,子View可以设置为任意大小。
MeasureSpec.EXACTLY 精确尺寸,表示父控件已经确切的指定了子View的大小。
MeasureSpec.AT_MOST 最大尺寸,表示子View具体大小没有尺寸限制,但是存在上限,上限一般为父View大小。

因此,在重写onMeasure方法时要根据模式不同进行尺寸计算,用的比较多的方法:

@Override    
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // setMeasuredDimension这个方法,这个方法决定了当前View的大小    
    setMeasuredDimension(getMeasuredLength(widthMeasureSpec, true), getMeasuredLength(heightMeasureSpec, false));    
}    

private int getMeasuredLength(int length, boolean isWidth) {    
    int specMode = MeasureSpec.getMode(length);    
    int specSize = MeasureSpec.getSize(length);    
    int size;    
    int padding = isWidth ? getPaddingLeft() + getPaddingRight()    
        : getPaddingTop() + getPaddingBottom();    
    if (specMode == MeasureSpec.EXACTLY) {    
        size = specSize;    
    } else {    
        size = isWidth ? padding + mWave.length / 4 : DEFAULT_HEIGHT    
            + padding;    
        if (specMode == MeasureSpec.AT_MOST) {    
            size = Math.min(size, specSize);    
        }    
    }    
    return size;    
} 

还有一些常用的,如

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);

    int widthsize = MeasureSpec.getSize(widthMeasureSpec);      //取出宽度的确切数值
    int widthmode = MeasureSpec.getMode(widthMeasureSpec);      //取出宽度的测量模式

    int heightsize = MeasureSpec.getSize(heightMeasureSpec);    //取出高度的确切数值
    int heightmode = MeasureSpec.getMode(heightMeasureSpec);    //取出高度的测量模式

    //对View的宽高进行修改了,不要调用 super.onMeasure( widthMeasureSpec, heightMeasureSpec);
    setMeasuredDimension( widthsize, heightsize);
}

onSizeChange

视图大小发生改变时调用。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    Log.i("235","onMeasure");
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    Log.i("235","onSizeChanged");
    super.onSizeChanged(w, h, oldw, oldh);
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    Log.i("235","onLayout");
    super.onLayout(changed, left, top, right, bottom);
}

@Override
protected void onDraw(Canvas canvas) {
    Log.i("235","onDraw");
}

依次执行顺序:

08-08 12:10:34.066 21861-21861/com.mytest.myapplication I/235: onMeasure
08-08 12:10:34.081 21861-21861/com.mytest.myapplication I/235: onSizeChanged
08-08 12:10:34.081 21861-21861/com.mytest.myapplication I/235: onLayout
08-08 12:10:34.143 21861-21861/com.mytest.myapplication I/235: onMeasure
08-08 12:10:34.143 21861-21861/com.mytest.myapplication I/235: onLayout
08-08 12:10:34.145 21861-21861/com.mytest.myapplication I/235: onDraw

onMeasure方法:作用是计算各控件的大小。系统在渲染页面时会调用各view的onMeasure方法,各控件的onMeasure方法执行顺序是从内到外,即先调用子控件的onMeasure方法,在执行父布局的onMeasure方法。

onLayout方法:根据获取到的尺寸信息渲染这个view。onMeasure方法执行完后会回调onLayout方法。onSizeChanged方法执行完也会回调onLayout方法。执行顺序也是从内到外,即先调用子控件的onLayout方法,在执行父布局的onLayout方法。

关于Activity初始化时的三次measure,两次layout但只一次draw,可以了解下。

onLayout

确定布局的函数,它用于确定子View的位置,在自定义ViewGroup中会用到,他调用的是子View的layout函数。

onDraw

绘制View的函数,包括canvas、paint、path等的用法。

Activity 重新启动页面的时候(如home键退出应用),和Activity 执行生命周期的方法的顺序:

扫描二维码关注公众号,回复: 2424504 查看本文章
08-08 14:16:29.256 3226-3226/com.mytest.myapplication I/235: onPause
08-08 14:16:32.653 3226-3226/com.mytest.myapplication I/235: onRestart
08-08 14:16:32.654 3226-3226/com.mytest.myapplication I/235: onStart
08-08 14:16:32.654 3226-3226/com.mytest.myapplication I/235: onResume
08-08 14:16:32.687 3226-3226/com.mytest.myapplication I/235: onDraw

在执行完onResume方法之后执行onDraw,另外,调用 invalidata 方法,也会重新执行onDraw,具体为(图片来自大神GcsSloop):
图片来自大神GcsSloop

仅为学习笔记,以供翻阅。

猜你喜欢

转载自blog.csdn.net/justiceofheaven/article/details/76903320