ViewTree的绘图遍历
绘图遍历也就是ViewTree遍历过程的最后一步。这个过程有3个核心的步骤:
1)canvas= mSurface.lockCanvas(dirty);
2)mView.draw(canvas);
3)surface.unlockCanvasAndPost(canvas);
和View直接交互的是canvas,可以认为是作画的工具集。应用程序跟surfaceflinger间的数据交互依靠的是surface。所以canvas和surface之间必然有关联。究竟什么关联,从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.java的unlockCanvasAndPost,然后调用到Surface.cpp的unlockAndPost。
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成功lock到Canvas后,通过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.java,TextView.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);
}
}