View的setWillNotDraw()方法作用

Quibbler 2022-3-25 2044

View的setWillNotDraw()方法作用


        仅View一个类代码就超过3万行,从头到尾看一遍难度太大。除了记住常用的方法,还有一些不那么常用的。本篇将要介绍的是setWillNotDraw()方法。



1、setWillNotDraw() 方法介绍

        如果调用ViewsetWillNotDraw(true)方法,也就是给View设置WILL_NOT_DRAW标志,会跳过当前View的绘制,对应ViewonDraw()不会执行。

    /**
     * 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);
    }

        如何判断View有没有设置过此标记呢?提供了willNotDraw()方法判断View是否设置了WILL_NOT_DRAW。自定义View / ViewGroup时,如果遇到不绘制的问题,可以先检查此标记。

    /**
     * Returns whether or not this View draws on its own.
     *
     * @return true if this view has nothing to draw, false otherwise
     */
    @ViewDebug.ExportedProperty(category = "drawing")
    public boolean willNotDraw() {
        return (mViewFlags & DRAW_MASK) == WILL_NOT_DRAW;
    }

        注意,PFLAG_SKIP_DRAWWILL_NOT_DRAW的值是一样的,在后面源码中的作用等效:

    static final int PFLAG_SKIP_DRAW     = 0x00000080;
    static final int WILL_NOT_DRAW 	 = 0x00000080;



2、ViewGroup默认开启WILL_NOT_DRAW

        ViewGroup为了提高自身的绘制性能,默认会设置WILL_NOT_DRAW标记。在ViewGroup的构造方法中:

    public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initViewGroup();
        initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
    }

        调用initViewGroup()初始化ViewGroup,添加WILL_NOT_DRAW标志位:

    private void initViewGroup() {
        // ViewGroup 默认不绘制
        if (!isShowingLayoutBounds()) {
            setFlags(WILL_NOT_DRAW, DRAW_MASK);
        }
        ...
    }

        注意到isShowingLayoutBounds()这个方法,对应的是开发者选项中的显示布局边界选项。如果打开了此选项,就不会给ViewGroup设置WILL_NOT_DRAW

    /**
     * Returns {@code true} when the View is attached and the system developer setting to show
     * the layout bounds is enabled or {@code false} otherwise.
     */
    public final boolean isShowingLayoutBounds() {
        return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout;
    }

        所以,当继承ViewGroup,实现自定义ViewGroup容器时。注意是否需要去掉这个标记位, 否则在自定义ViewGroup中重写的onDraw() 不会被执行。有两种方法可以达到这种效果:

        ①设置ViewGroup的背景色, 会设置PFLAG_SKIP_DRAW ;它和WILL_NOT_DRAW 其实是同一个值。

        ②使用setWillNotDraw(false) 去掉WILL_NOT_DRAW标志。

        之前在布局优化一文中,提到通过给ViewGroup等容器设置null背景,起到一定优化作用。其实原理在这里,没有背景的ViewGroup减少一个绘制自身的步骤,如果设置了重复多余的背景,反而增加了绘制任务。



3、setWillNotDraw() 如何起作用

        在View的绘制流程一文中详细的了解View的绘制流程,可以获知在ViewGroupdrawChild()方法中绘制子View

    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

        调用Viewdraw(Canvas canvas, ViewGroup parent, long drawingTime)方法,在该方法中判断当前View有没有设置PFLAG_SKIP_DRAW,如果设置了,那么不会走Viewdraw(),进而onDraw()不会被调用。

    /**
     * This method is called by ViewGroup.drawChild() to have each child view draw itself.
     *
     * This is where the View specializes rendering behavior based on layer type,
     * and hardware acceleration.
     */
    boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
        ...
        // Fast path for layouts with no backgrounds
        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
            dispatchDraw(canvas);
        } else {
            draw(canvas);
        }
        ...
    }

        而是执行dispatchDraw(Canvas canvas)方法。在ViewGroup就是绘制子View,而在View中默认实现是空方法:

    protected void dispatchDraw(Canvas canvas) {
    }



参考博客:

        关于onDraw()方法不被执行的解决方法(setWillNotDraw)

        

不忘初心的阿甘
最新回复 (0)
    • 安卓笔记本
      2
        登录 注册 QQ
返回
仅供学习交流,切勿用于商业用途。如有错误欢迎指出:fluent0418@gmail.com