ViewGroup onDraw调用和不调用

ViewGroup onDraw调用和不调用

view 的绘制一般都是,测量(onMeasure),布局(onLayout)和绘制(onDraw)。自定义View一般是复写上述三个方法。但是自定义View如果是继承ViewGroup,会发现onDraw不会调用。
android View的绘制一般是从 draw或者dispatchDraw开始的。

绘制跟踪

view绘制调用,一般是parent的dispatchDraw开始.具体View的整个绘制请参考其他文章,本文只是找到为什么不调用onDraw。

class ViewGroup{
.....
 protected void dispatchDraw(Canvas canvas) {
   	 ...
   	  more |= drawChild(canvas, transientChild, drawingTime);//主要是这句,这句在这个方法有很多调用,主要是涉及到绘制顺序相关的东西,本文不描述。
		 .....
 }
 ...
  protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
       return child.draw(canvas, this, drawingTime);//注意这个方法,调用view的draw
   }

}

上面可以看到,parent调用了view的draw方法,AS直接调整会过去,然后发现这里调用了draw,为什么viewGroup不会调用呢????这个地方困惑我好久,然后我发现细心才是最重要的,细心。
注意上面drawChild调用的子view的draw方法,是3个参数的,而我们一般说的draw方法其实是:

class View{
	........
	public void draw(Canvas canvas) {//注意这个是单参数的,单纯的绘制
		.......
	}
	......
	   /**
     * 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) {//方法明确注释了,这个才是viewGroup调用子类的draw,这个是复写不了的。这个方法没有加修饰就是default的
	......
	//前面是判断硬件加速和layer type的逻辑。关于硬件加速和layer type我会在后续文章说明
	.....
	renderNode = updateDisplayListIfDirty();//关键方法来了
	........
	}
	......
	/**
     * Gets the RenderNode for the view, and updates its DisplayList (if needed and supported)
     * @hide
     */
    @NonNull
    public RenderNode updateDisplayListIfDirty() {
        final RenderNode renderNode = mRenderNode;
/**
     * Gets the RenderNode for the view, and updates its DisplayList (if needed and supported)
     * @hide
     */
    @NonNull
    public RenderNode updateDisplayListIfDirty() {
        ........
            try {
                if (layerType == LAYER_TYPE_SOFTWARE) {
                    buildDrawingCache(true);
                    Bitmap cache = getDrawingCache(true);
                    if (cache != null) {
                        canvas.drawBitmap(cache, 0, 0, mLayerPaint);
                    }
                } else {
                    computeScroll();

                    canvas.translate(-mScrollX, -mScrollY);
                    mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;

                    // Fast path for layouts with no backgrounds
                    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {//看方法注释
                        dispatchDraw(canvas);
                        drawAutofilledHighlight(canvas);
                        if (mOverlay != null && !mOverlay.isEmpty()) {
                            mOverlay.getOverlayView().draw(canvas);
                        }
                        if (debugDraw()) {
                            debugDrawFocus(canvas);
                        }
                    } else {
                        draw(canvas);
                    }
                }
            } finally {
                renderNode.end(canvas);
                setDisplayListProperties(renderNode);
            }
        } else {
            mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
        }
        return renderNode;
    }
    ...
    .....
    }
    .........


	}
}

所以就是这句:mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW
mPrivateFlags 这个属性,绘制和显示都是用它来来的,上面说的layer type会影响到这个。
然后哪里设置的呢?ViewGroup初始化的时候

 private void initViewGroup() {
        // ViewGroup doesn't draw by default
        if (!debugDraw()) {
            setFlags(WILL_NOT_DRAW, DRAW_MASK);
        }
 		........
 }

默认会调用这个设置不调用,但是为啥有backgroud的时候会调用draw呢?这个时候是必须得画自己的,继续看 setFlags方法。这个方法在View里面

/**
     * Set flags controlling behavior of this view.
     *
     * @param flags Constant indicating the value which should be set
     * @param mask Constant indicating the bit range that should be changed
     */
    void setFlags(int flags, int mask) {
	.....
	....
	 if ((changed & DRAW_MASK) != 0) {
            if ((mViewFlags & WILL_NOT_DRAW) != 0) {
                if (mBackground != null //看这个判断条件,或,有一个成立就行
                        || mDefaultFocusHighlight != null
                        || (mForegroundInfo != null && mForegroundInfo.mDrawable != null)) {
                    mPrivateFlags &= ~PFLAG_SKIP_DRAW;
                } else {
                    mPrivateFlags |= PFLAG_SKIP_DRAW;
                }
            } else {
                mPrivateFlags &= ~PFLAG_SKIP_DRAW;
            }
            requestLayout();
            invalidate(true);
        }
        .....
        .....

}

所以原因明确了:ViewGroup的初始化的时候,只设置成PFLAG_SKIP_DRAW,如果有背景那么这个设置不会成立,但是没有背景的时候这个属性就会设置成功。
然后在绘制流程中调用View.draw(Canvas canvas, ViewGroup parent, long drawingTime) 里面会根据这个标志来决定是走 draw还是dispatchDraw。
draw最后也会调用dispatchDraw。所以复写view的时候,draw不一定走,但是dispatchDraw一定会走。
所以自定义viewGroup想画点别的东西又不想是背景的时候,得加一些骚操作,具体的自己研究去吧。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值