关闭

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

标签: 绘图遍历onDrawp margin-bottom 0
22人阅读 评论(0) 收藏 举报
分类:

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);
        }	
}
0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

Android View原理(View树遍历,View重绘,View动画)

一、屏幕绘图基础 Android中的GUI系统是客户端和服务端配合的窗口系统,即后台运行了一个绘制服务,每个应用程序都是该服务端的一个客户端,当客户端需要绘制时,首先请求服务端创建一个窗口,然后...
  • wtyvhreal
  • wtyvhreal
  • 2015-04-01 10:36
  • 2847

Android Animation各种动画详解

1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今天来一发Android应用的各种Animation大集合。英文厉害的请直接移步参考A...
  • wangbaochu
  • wangbaochu
  • 2015-10-28 00:22
  • 4326

图(2)--图的遍历和图的连通性

一:图的遍历       1.概念: 从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次(图的遍历算法是求解图的连通性问题、拓扑排序和求关键路径等算法的基础。)        2.深度优先...
  • u013360022
  • u013360022
  • 2014-02-19 12:34
  • 1738

Android GUI系统-ViewTree的遍历(四)

那些情况会引起ViewTree的遍历 1)应用程序刚启动时,会在构造出整棵ViewTree后,执行第一次遍历。 public void setView(View view, WindowMan...
  • lin20044140410
  • lin20044140410
  • 2017-12-26 07:09
  • 29

Android GUI系统-ViewTree的创建(二)

View树的创建过程 当AMS通知应用进程来启动一个Activity任务时,最终这个请求会转化为ActivityThread中的一个消息LAUNCH_ACTIVITY,同类型的消息还是RESUME_...
  • lin20044140410
  • lin20044140410
  • 2017-12-21 22:38
  • 42

Android GUI系统-ViewTree的管理者(三)

作为ViewTree的管理者ViewRoot,是怎么工作的。 一,ViewRootImpl跟WMS间的通信 ViewRootImpl.java在构造的时候,需要建立跟WMS的通信的双向...
  • lin20044140410
  • lin20044140410
  • 2017-12-24 18:16
  • 41

App的启动过程(5)ViewTree遍历中最后一步的Draw

以上是WMS端窗口的添加,下面接着ViewTree遍历中最后一步的Draw的分析。 /* ViewRootImpl.java */ private boolean drawSoftware()à ca...
  • lin20044140410
  • lin20044140410
  • 2017-06-01 17:15
  • 254

Android View系统源码分析(六)—— 导致View树重新遍历的时机

Android View系统源码分析(六)—— 导致View树重新遍历的时机
  • u014608640
  • u014608640
  • 2016-07-04 10:15
  • 345

Android图形用户界面开发之ViewTree和DecorView详细介绍

Android图形用户界面开发之ViewTree和DecorView详细介绍 出处:西西整理 作者:西西 日期:2013/1/4 0:23:10 [大 中 小] 评论: 0 | 我要发表看法...
  • murongbingxiao
  • murongbingxiao
  • 2015-07-10 10:09
  • 303

Android图形用户界面开发之ViewTree和DecorView详细介绍

图形用户界面(GUI)是Android应用程序开发不可或缺的一部分。其不仅能为用户提供输入,还能够根据(用户)执行的动作,提供相应的反馈。因此,作为开发人员,能够理解UI(用户界面)是如何创建以及跟新...
  • wxlinwzl
  • wxlinwzl
  • 2014-02-01 23:05
  • 521
    个人资料
    • 访问:35517次
    • 积分:1686
    • 等级:
    • 排名:千里之外
    • 原创:130篇
    • 转载:38篇
    • 译文:0篇
    • 评论:9条
    最新评论