自定义View没有调用onDraw
我们在自定义View的时候有时会发现,重写的onDraw没有生被调用?这是为什么呢?大概率是因为你的自定义View继承自ViewGroup或者其子类,并只重写了onDraw方法。
原因是ViewGoup以及子类默认关闭自动渲染,onDraw()方法不会被调用;View默认开启主动渲染, onDraw()会主动调用一次。针对ViewGroup自定义view不会被调用的情况,如下三种解决方法。
- 1:自定义view在初始化时设置setWillNotDraw(false)
- 2:自定义view在初始化的时候设置背景颜色(backgroud)
- 3:自定义view在初始化的时候设置前景色(foreground)
先看一下View的源码setWillNotDraw,
/**
* If this view doesn't do any drawing on its own, set this flag to
* allow further optimizations. By default, this flag is not set on
* View, but could be set on some View subclasses such as ViewGroup.
*
* Typically, if you override {@link #onDraw(android.graphics.Canvas)}
* you should clear this flag.
*
* @param willNotDraw whether or not this View draw on its own
*/
public void setWillNotDraw(boolean willNotDraw) {
setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}
上面的解释就是如果布局本身不需要自己动手绘制,你就可以把willNotDraw设置为true。默认情况下View的willNotDraw是false,因为View一般需要绘制自己(onDraw),但是一些View的子类如ViewGroup,默认设置willNotDraw是true(因为一般ViewGroup的绘制都交给了子View去绘制)。所以你要是重写onDraw的话,一定会要清除willNotDraw这个flag,设置其为false。
至于为什么设置背景(background)和设置前景(foreground)也能调用onDraw呢?通过查看View的源码,我们可以看到,View中有一个标志为PFLAG_SKIP_DRAW
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
...
} else {
draw(canvas);
}
这个flag控制着绘制的流程是dispatchDraw还是draw(draw函数内部会调用onDraw方法),而PFLAG_SKIP_DRAW则是由willNotDraw以及View的背景、前景决定的,
if ((mViewFlags & WILL_NOT_DRAW) != 0 && mBackground == null
&& (mForegroundInfo == null || mForegroundInfo.mDrawable == null)) {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
只有当View的背景和前景都为null,并且willNotDraw是true的情况下,PFLAG_SKIP_DRAW才被设置,这个时候就不会走draw流程,自然不会调用onDraw。但是有伙伴可能会问咯,这个时候会走dispatchDraw啊,没错,这个时候如果你需要进行绘制,就需要重写dispatchDraw进行操作了。
总结一下
如果自定义View继承自ViewGroup或者它的子类,如果想进行绘制,总共有两种方法可供选择:
- 1 重写onDraw。然后设置setWillNotDraw(false),或者给View设置背景或者前景。
- 2 直接重写dispatchDraw,不需要其他操作了。