当前位置: 首页 > 工具软件 > lin-view-ui > 使用案例 >

Android GUI系统-ViewTree的绘图遍历(六)

游皓
2023-12-01

ViewTree的绘图遍历

绘图遍历也就是ViewTree遍历过程的最后一步。这个过程有3个核心的步骤:

1canvas= mSurface.lockCanvas(dirty);

2mView.draw(canvas);

3surface.unlockCanvasAndPost(canvas);


View直接交互的是canvas,可以认为是作画的工具集。应用程序跟surfaceflinger间的数据交互依靠的是surface。所以canvassurface之间必然有关联。究竟什么关联,从mSurface.lockCanvas看起。

ViewRootImpl中获取一个canvas的方式是通过mSurface.lockCanvas(dirty)


public Canvas lockCanvas(Rect inOutDirty)@Surface.java{
//这是个jni调用,java层的mCanvas,会关联到本地层的canvas对象。
	mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
}

static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj, jobject dirtyRectObj)@android_view_Surface.cpp {
//native层的surface对象。
	sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
//用于存储数据的buffer,这个结构体的bits指针就是存储数据的地方。
	ANativeWindow_Buffer outBuffer;
//获取锁定一个buffer。
	status_t err = surface->lock(&outBuffer, dirtyRectPtr);

//SKBitmap会封装存储数据的buffer,也就是通过 setPixels设置真正存储图像数据的内存块 outBuffer.bits。
	SkBitmap bitmap;
	bitmap.setPixels(outBuffer.bits);

// canvasObj实际是java层canvas对象,通过java层的canvas对象生成本地的 nativeCanvas,实际类型是SkiaCanvas,并且设置了 SkBitmap。这样surface跟canvas就产生了关联,surface的职责是负责管理surfaceflinger分配的内存块,就是那个bits,而canvas负责往这个内存块中写入数据。
	Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
	nativeCanvas->setBitmap(bitmap);
}

在上面的步骤中,surface通过lock申请一个buffer

status_t Surface::lock(
        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)@Surface.cpp{
// mLockedBuffer表示当前已经被锁定的buffer是GraphicBuffer类型的,如果不为null就直接返回。当UI绘图结束mLockedBuffer会被置NULL,同时用mPostedBuffer记录最近的操作。
	if (mLockedBuffer != 0) {
		return INVALID_OPERATION;
	}

//判断是否建立到IGraphicBufferProducer->connect的连接,如果没有就执行连接,然后设置内存块的用途。
	if (!mConnectedToCpu) {
		int err = Surface::connect(NATIVE_WINDOW_API_CPU);
		setUsage(GRALLOC_USAGE_SW_READ_OFTEN | 
			GRALLOC_USAGE_SW_WRITE_OFTEN);
	}

//向surfaceflinger申请一个buffer。
    ANativeWindowBuffer* out;
    int fenceFd = -1;
    status_t err = dequeueBuffer(&out, &fenceFd);

// backBuffer表示当前要处理的buffer。
	sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));

//锁定这个buffer,把 mLockedBuffer赋值为 backBuffer。
        void* vaddr;
        status_t res = backBuffer->lockAsync(
                GRALLOC_USAGE_SW_READ_OFTEN | 
			GRALLOC_USAGE_SW_WRITE_OFTEN,
	                newDirtyRegion.bounds(), &vaddr, fenceFd);
	mLockedBuffer = backBuffer;
}

UI绘图完成后,将这个buffer解锁,然后提交给surfaceflinger。先是通过Surface.javaunlockCanvasAndPost,然后调用到Surface.cppunlockAndPost

status_t Surface::unlockAndPost()@Surface.cpp{
//当前被锁定buffer,如果为null,表示出错了。
	if (mLockedBuffer == 0) {
		return INVALID_OPERATION;
	}
//解除对buffer的锁定。
    int fd = -1;
    status_t err = mLockedBuffer->unlockAsync(&fd);
//将这个buffer入队到Bufferqueue,由BufferQueuecore通知consumer可以消费这个buffer了。
	err = queueBuffer(mLockedBuffer.get(), fd);

//将 mLockedBuffer清0,用 mPostedBuffer记录最近的post操作。
    mPostedBuffer = mLockedBuffer;
    mLockedBuffer = 0;
}

最后看下绘图遍历的draw的实现,在ViewRootImpl成功lockCanvas后,通过ViewTree的根元素逐步把这个Canvas沿ViewTree往下传,所以调用mView.draw(canvas)实际对应的DecorView的代码,DecorView是一个FrameLayout,因为ViewGroup不重载draw方法,所以最终的实现是在View.java中。

public void draw(Canvas canvas)@View.java {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == 			
		PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;	
//第一步,绘制背景。
	if (!dirtyOpaque) {
		drawBackground(canvas);
	}

        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
//绘制内容,View的子类应该重载onDraw方法,实现对自身内容的绘制。
            if (!dirtyOpaque) onDraw(canvas);
//绘制子对象,针对ViewGroup来说。
            dispatchDraw(canvas);

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }
//绘制装饰条,如srollbars。
            onDrawForeground(canvas);
//结束返回。这是View在水平、垂直方法都没有添加Fading特效的情况下。否则,执行下面的绘制过程。
            return;
        }

//首先保存Canvas的layers,以备后面fading需要。
	int solidColor = getSolidColor();
	if (solidColor == 0) {
		final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
            if (drawTop) {
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }
            if (drawBottom) {
                canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
            }
	……...
	}

//绘制内容,调用View子类的实现。
        if (!dirtyOpaque) onDraw(canvas);
//绘制子对象。
	dispatchDraw(canvas);

        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;
//绘制fading特效,
        if (drawTop) {
            matrix.setScale(1, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, right, top + length, p);
        }
//恢复Canvas。
	canvas.restoreToCount(saveCount);
//绘制装饰条。
	onDrawForeground(canvas);
}

一个具体的View的子类如ImageView.javaTextView.java,它的onDraw实现是怎样的?

protected void onDraw(Canvas canvas) @ImageView.java{
        super.onDraw(canvas);

//mDrawable不能为null,Drawable是图像数据的抽象,它表达了希望在屏幕上绘制的图像,比如Bitmap文件就是一个具体的drawable,它承载了图片的数据;再比如表示按钮状态的normal、pressed等状态的States,也是一个Drawable,表示了每种状态所对象的图像。
        if (mDrawable == null) {
            return; // couldn't resolve the URI
        }	

//在不需要坐标变换的情况,直接调用mDrawable.draw(canvas),把Drawable中数据绘制到Canvas中。
        if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) {
            mDrawable.draw(canvas);
        } else {
            final int saveCount = canvas.getSaveCount();
            canvas.save();
//先执行必要的坐标变换,然后在绘制Drawable中内容,最后恢复Canvas。
            if (mCropToPadding) {
                final int scrollX = mScrollX;
                final int scrollY = mScrollY;
                canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
                        scrollX + mRight - mLeft - mPaddingRight,
                        scrollY + mBottom - mTop - mPaddingBottom);
            }

            canvas.translate(mPaddingLeft, mPaddingTop);

            if (mDrawMatrix != null) {
                canvas.concat(mDrawMatrix);
            }
            mDrawable.draw(canvas);
            canvas.restoreToCount(saveCount);
        }	
}
 类似资料: