1 概述
Android从3.0(API Level 11)开始,在绘制View的时候支持硬件加速,充分利用GPU的特性,使得绘制更加平滑。
实质上就是Android3.0以前,几乎所有的图形绘制都是由Skia完成,Skia是一个向量绘图库,使用CPU来进行运算;所以从Android3.0 开始,Google用hwui取代了Skia,准确的说,是推荐取代,因为Opengl的支持不完全,有少量图形api仍由Skia完成,多数view的绘制通过HWUI模块使用openGL的函数来实现。
由于硬件加速自身并非完美无缺,所以Android提供选项来打开或者关闭硬件加速,默认是关闭。一般我们会在两个级别上打开或者关闭硬件加速:
Application级别:<applicationandroid:hardwareAccelerated="true" ...>
Activity级别:<activity android:hardwareAccelerated="false" ...>
下面我们从代码级别来理解系统是如何实现硬件加速的。
2 开启硬件加速
在开始分析之前,我们先来看下ViewRootImpl中的一个基本流程图:
如上图所示,其实过程2就是打开硬件加速的过程,在ViewRootImpl的SetView函数中,通过调用enableHardwareAcceleration函数来开启硬件加速,我们来看下enableHardwareAcceleration的代码:
[cpp] view plain copy
1. private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
2. false;
3. false;
4. final boolean hardwareAccelerated =
5. (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
6. if (hardwareAccelerated) {
7. if (!HardwareRenderer.isAvailable()) {
8. return;
9. }
10. // Persistent processes (including the system) should not do
11. // accelerated rendering on low-end devices. In that case,
12. // sRendererDisabled will be set. In addition, the system process
13. // itself should never do accelerated rendering. In that case, both
14. // sRendererDisabled and sSystemRendererDisabled are set. When
15. // sSystemRendererDisabled is set, PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
16. // can be used by code on the system process to escape that and enable
17. // HW accelerated drawing. (This is basically for the lock screen.)
18.
19. final boolean fakeHwAccelerated = (attrs.privateFlags &
20. WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0;
21. final boolean forceHwAccelerated = (attrs.privateFlags &
22. WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;
23.
24. if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled
25. && forceHwAccelerated)) {
26. // Don't enable hardware acceleration when we're not on the main thread
27. if (!HardwareRenderer.sSystemRendererDisabled &&
28. Looper.getMainLooper() != Looper.myLooper()) {
29. "Attempting to initialize hardware "
30. "acceleration outside of the main thread, aborting");
31. return;
32. }
33.
34. if (mAttachInfo.mHardwareRenderer != null) {
35. true);
36. }
37.
38. final boolean translucent = attrs.format != PixelFormat.OPAQUE;
39. mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent);
40. if (mAttachInfo.mHardwareRenderer != null) {
41. mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());
42. mAttachInfo.mHardwareAccelerated =
43. true;
44. }
45. else if (fakeHwAccelerated) {
46. // The window had wanted to use hardware acceleration, but this
47. // is not allowed in its process. By setting this flag, it can
48. // still render as if it was accelerated. This is basically for
49. // the preview windows the window manager shows for launching
50. // applications, so they will look more like the app being launched.
51. true;
52. }
53. }
54. }
上面代码不长,而且注释很完善,所以我们主要来看下函数的最主体部分即可。
[cpp] view plain copy
1. static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
2. switch (glVersion) {
3. case 2:
4. return Gl20Renderer.create(translucent);
5. }
6. throw new IllegalArgumentException("Unknown GL version: " + glVersion);
7. }
8. static HardwareRenderer create(boolean translucent) {
9. if (GLES20Canvas.isAvailable()) {
10. return new Gl20Renderer(translucent);
11. }
12. return null;
13. }
14. int glVersion, boolean translucent) {
15. mGlVersion = glVersion;
16. mTranslucent = translucent;
17. loadSystemProperties(null);
18. }
我们发现这段代码最主要的作用就是创建了一个Gl20Renderer(继承自GlRenderer和HardwareRenderer)。创建时调用了通过loadSystemProperties函数加载了一些属性和调试选项,这里不展开,有兴趣可以详细研究一下。
创建好的Gl20Renderer保存在了mAttachInfo.mHardwareRenderer中,后面我们在使用硬件加速时就会用到。
3 硬件绘制
从前面图中我们可以知道,在ViewRootImpl绘制时,调用了draw函数。有两种选择,一种是软件绘制,一种是硬件绘制。
[cpp] view plain copy
1. if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
2. // Draw with hardware renderer.
3. false;
4. mHardwareYOffset = yoff;
5. mResizeAlpha = resizeAlpha;
6.
7. mCurrentDirty.set(dirty);
8. dirty.setEmpty();
9.
10. this,
11. animating ? null : mCurrentDirty);
12. else {
13. // If we get here with a disabled & requested hardware renderer, something went
14. // wrong (an invalidate posted right before we destroyed the hardware surface
15. // for instance) so we should just bail out. Locking the surface with software
16. // rendering at this point would lock it forever and prevent hardware renderer
17. // from doing its job when it comes back.
18. // Before we request a new frame we must however attempt to reinitiliaze the
19. // hardware renderer if it's in requested state. This would happen after an
20. // eglTerminate() for instance.
21. if (attachInfo.mHardwareRenderer != null &&
22. !attachInfo.mHardwareRenderer.isEnabled() &&
23. attachInfo.mHardwareRenderer.isRequested()) {
24.
25. try {
26. attachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight,
27. mHolder.getSurface());
28. catch (OutOfResourcesException e) {
29. handleOutOfResourcesException(e);
30. return;
31. }
32.
33. true;
34. scheduleTraversals();
35. return;
36. }
37.
38. if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) {
39. return;
40. }
41. }
42. }
软件绘制不在我们分析范围内,这里我们将在下一小节中来研究一下attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,animating ? null : mCurrentDirty)这个调用。
我们前面已经知道mHardwareRenderer其实是一个Gl20Renderer,那么我们来看下它的draw函数。
[cpp] view plain copy
1. void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
2. Rect dirty) {
3. if (canDraw()) {
4. if (surfaceState != SURFACE_STATE_ERROR) {
5. HardwareCanvas canvas = mCanvas;
6. attachInfo.mHardwareCanvas = canvas;
7. dirty = beginFrame(canvas, dirty, surfaceState);
8. DisplayList displayList = buildDisplayList(view, canvas);
9. int saveCount = 0;
10. int status = DisplayList.STATUS_DONE;
11. try {
12. status = prepareFrame(dirty);
13. if(mSurface.isValid() && dirty != null) {
14. mSurface.setDirtyRect(dirty);
15. }
16. saveCount = canvas.save();
17. callbacks.onHardwarePreDraw(canvas);
18.
19. if (displayList != null) {
20. status |= drawDisplayList(attachInfo, canvas, displayList, status);
21. }
22. catch (Exception e) {
23. "An error has occurred while drawing:", e);
24. } finally {
25. callbacks.onHardwarePostDraw(canvas);
26. canvas.restoreToCount(saveCount);
27. false;
28.
29. if (mDrawDelta > 0) {
30. mFrameCount++;
31. debugOverdraw(attachInfo, dirty, canvas, displayList);
32. debugDirtyRegions(dirty, canvas);
33. drawProfileData(attachInfo);
34. }
35. }
36. onPostDraw();
37. swapBuffers(status);
38. }
39. }
40. }
上面这几十行代码,就是硬件绘制的最重要的几个流程调用,我们看到draw函数主要分为如下几步,beginFrame,buildDisplayList,prepareFrame,drawDisplayList,onHardwarePostDraw,onPostDraw,swapBuffers。整个详细的调用过程如下图:
从上图中我们可以看出,整个内部详细的调用过程还是比较复杂的,我们这里仔细的一一讲解一下。
1 beginFrame
这个函数的作用很简单:Notifies EGL that the frame is about to be rendered。
功能主要通过android_view_HardwareRenderer_beginFrame这个JNI函数实现:
[cpp] view plain copy
1. static void android_view_HardwareRenderer_beginFrame(JNIEnv* env, jobject clazz,
2. jintArray size) {
3. EGLDisplay display = eglGetCurrentDisplay();
4. EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);
5. if (size) {
6. EGLint value;
7. jint* storage = env->GetIntArrayElements(size, NULL);
8. eglQuerySurface(display, surface, EGL_WIDTH, &value);
9. storage[0] = value;
10. eglQuerySurface(display, surface, EGL_HEIGHT, &value);
11. storage[1] = value;
12. env->ReleaseIntArrayElements(size, storage, 0);
13. }
14. eglBeginFrame(display, surface);
在BeginFrame() 里获取EGLDisplay(用于显示) 和一个EGLSurface,OpenGL将在这个Surface上进行绘图。
eglBeginFrame主要是校验参数的合法性。
2 buildDisplayList
buildDisplayList是一个很重要也比较复杂的函数,通过这个函数,我们真正完成了视图绘制的录制工作,并将操作指令保存到了native层的DisplayList中,我们这里重点讲一下。
[cpp] view plain copy
1. private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
2. if (mDrawDelta <= 0) {//如果根本没有新的绘制发生,使用之前的displaylist即可
3. return view.mDisplayList;
4. }
5. view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
6. == View.PFLAG_INVALIDATED;
7. view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
8. long buildDisplayListStartTime = startBuildDisplayListProfiling();
9. canvas.clearLayerUpdates();
10. "getDisplayList");
11. DisplayList displayList = view.getDisplayList();
12. Trace.traceEnd(Trace.TRACE_TAG_VIEW);
13. endBuildDisplayListProfiling(buildDisplayListStartTime);
14. return displayList;
15. }
其实主要逻辑是通过view类的getDisplayList()方法实现的,这个方法通过递归的方式获得了整个View树的DisplayList:
[cpp] view plain copy
1. private DisplayList getDisplayList(DisplayList displayList, boolean isLayer) {
2. if (((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 ||
3. displayList == null || !displayList.isValid() ||
4. (!isLayer && mRecreateDisplayList))) {
5. // Don't need to recreate the display list, just need to tell our
6. // children to restore/recreate theirs
7. if (displayList != null && displayList.isValid() &&
8. !isLayer && !mRecreateDisplayList) {
9. mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
10. mPrivateFlags &= ~PFLAG_DIRTY_MASK;
11. dispatchGetDisplayList();
12. return displayList;
13. }
14. if (!isLayer) {
15. // If we got here, we're recreating it. Mark it as such to ensure that
16. // we copy in child display lists into ours in drawChild()
17. true;
18. }
19. if (displayList == null) {
20. displayList = mAttachInfo.mHardwareRenderer.createDisplayList(getClass().getName());
21. // If we're creating a new display list, make sure our parent gets invalidated
22. // since they will need to recreate their display list to account for this
23. // new child display list.
24. invalidateParentCaches();
25. }
26. false;
27. int width = mRight - mLeft;
28. int height = mBottom - mTop;
29. int layerType = getLayerType();
30. final HardwareCanvas canvas = displayList.start(width, height);
31. ...
上面函数相当长,我们首先分析前半部分。暂时只看displaylist新创建时会走的逻辑:函数前半部分的流程基本是createDisplayList,invalidateParentCaches,displayList.start,我们来一点点分析。
2.1 createDisplayList
首先我们要创建一个displaylist,这里实际上只是创建了一个GLES20DisplayList类。
[cpp] view plain copy
1. public DisplayList createDisplayList(String name) {
2. return new GLES20DisplayList(name);
3. }
GLES20DisplayList是DisplayList的子类,这是一个影子类,没有实际用途,主要用来管理Native层的DisplayList的销毁。Native层的DisplayList在后面的start函数中才被真正创建。
2.2 invalidateParentCaches
这个函数的作用主要是,一旦我们重建了DisplayList,那么这个view必须清空之前的缓存,我们通过这个函数调用来清空缓存。
[cpp] view plain copy
1. protected void invalidateParentCaches() {
2. if (mParent instanceof View) {
3. ((View) mParent).mPrivateFlags |= PFLAG_INVALIDATED;
4. }
5. }
2.3 displayList.start
下面看下displayList.start函数:
[cpp] view plain copy
1. public HardwareCanvas start(int width, int height) {
2. false;
3. this);
4. mCanvas.start();
5. mCanvas.setViewport(width, height);
6. // The dirty rect should always be null for a display list
7. mCanvas.onPreDraw(null);
8. return mCanvas;
9. }
2.3.1 GLES20RecordingCanvas.obtain
start函数真正开始构建DisplayList的上下文,先来看下obtain函数:
[cpp] view plain copy
1. static GLES20RecordingCanvas obtain(GLES20DisplayList displayList) {
2. GLES20RecordingCanvas canvas = sPool.acquire();
3. if (canvas == null) {
4. new GLES20RecordingCanvas();
5. }
6. canvas.mDisplayList = displayList;
7. return canvas;
8. }
obtain函数的作用从名字中就可以看出来,主要作用是获取到一个GLES20RecordingCanvas,这个类是 HardwareCanvas和GLES20Canvas的子类。
HardwareCanvas是一个抽象类,如果系统属性和应用程序指定使用硬件加速(现已成为默认),它将会被View(通过AttachInfo,如 下图所示) 引用来完成所有的图形绘制工作。GLES20Canvas 则是hardwareCanvas的实现,但它也只是一层封装而已,真正的实现在Native 层,通过jni (andriod_view_gles20Canvas.cpp)接口来访问底层的Renderer, 进而执行OpenGL的命令。 此外,GLES20Canvas还提供了一些静态接口,用于创建各类Renderer对象。
GLES20RecordingCanvas 继承GLES20Canvas, 通过它调用的OpenGL命令将会存储在DisplayList里面,而不会立即执行。
[cpp] view plain copy
1. protected GLES20Canvas(boolean record, boolean translucent) {
2. mOpaque = !translucent;
3. if (record) {
4. mRenderer = nCreateDisplayListRenderer();
5. else {
6. mRenderer = nCreateRenderer();
7. }
8. setupFinalizer();
9. }
我们看到GLES20Canvas在创建时,由于我们现在需要录制命令,所以我们这里调用了nCreateDisplayListRenderer函数,创建的真正的用于渲染的本地对象DisplayListRenderer:
[cpp] view plain copy
1. static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env,
2. jobject clazz) {
3. return new DisplayListRenderer;
4. }
DisplayListRenderer是OpenGLRenderer的子类,相比于OpenGLRenderer是直接绘制,DisplayListRenderer则是将绘制命令保存在它内部的mDisplayListData中,mDisplayListData的类型是DisplayListData:
[cpp] view plain copy
1. class DisplayListData : public LightRefBase<DisplayListData> {
2. public:
3. LinearAllocator allocator;
4. Vector<DisplayListOp*> displayListOps;
5. };
我们看到这里的重点必然是这个容器displayListOps,一定是它存储了绘制命令DisplayListOp,至于DisplayListOp的详细内容,我们后面再展开。
这样,通过nCreateDisplayListRenderer函数,我们创建了一个本地的DisplayListRenderer对象,他会将绘制命令保存在一个DisplayListData类的DisplayListOp容器里。
现在我们再次回到GLES20Canvas的创建过程中来,调用完nCreateDisplayListRenderer后,程序又继续调用了setupFinalizer,这个我们从名字就可以猜出来这是用于这个canvas回收的,这里我们就不再展开。
2.3.2 GLES20RecordingCanvas.start
回到displayList.start函数中来,创建好GLES20RecordingCanvas之后,继续调用了它的start函数。这个函数主要是清空自己和它的孩子的所有display的引用,这里不再展开。
2.3.3 GLES20RecordingCanvas.setViewport
继续调用setViewport来设置视口:
[cpp] view plain copy
1. public void setViewport(int width, int height) {
2. mWidth = width;
3. mHeight = height;
4. nSetViewport(mRenderer, width, height);
5. }
6. static void android_view_GLES20Canvas_setViewport(JNIEnv* env, jobject clazz,
7. OpenGLRenderer* renderer, jint width, jint height) {
8. renderer->setViewport(width, height);
9. }
10. void DisplayListRenderer::setViewport(int width, int height) {
11. mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
12. mWidth = width;
13. mHeight = height;
14. }
15.
16. void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) {
17. loadIdentity();
18. data[kScaleX] = 2.0f / (right - left);
19. data[kScaleY] = 2.0f / (top - bottom);
20. data[kScaleZ] = -2.0f / (far - near);
21. data[kTranslateX] = -(right + left) / (right - left);
22. data[kTranslateY] = -(top + bottom) / (top - bottom);
23. data[kTranslateZ] = -(far + near) / (far - near);
24. mType = kTypeTranslate | kTypeScale | kTypeRectToRect;
25. }
我们看到上层调用了视口,下层实际上还是通过DisplayListRenderer做了具体实现,主要是做了一个正交投影,具体计算我们这里暂时先不研究。
其实大多数opengl在Canvas层面上的调用都是这样,底层通过render实现了类似OpenGl的实现,后面类似函数我们将不再展开。
2.3.4 GLES20RecordingCanvas.onPreDraw
GLES20RecordingCanvas.onPreDraw函数其实也很简单,先来看代码:
[cpp] view plain copy
1. public int onPreDraw(Rect dirty) {
2. if (dirty != null) {
3. return nPrepareDirty(mRenderer, dirty.left, dirty.top, dirty.right, dirty.bottom,
4. mOpaque);
5. else {
6. return nPrepare(mRenderer, mOpaque);
7. }
8. }
9. static int android_view_GLES20Canvas_prepare(JNIEnv* env, jobject clazz,
10. OpenGLRenderer* renderer, jboolean opaque) {
11. return renderer->prepare(opaque);
12. }
13.
14. bool opaque) {
15. return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
16. }
17. float left, float top,
18. float right, float bottom, bool opaque) {
19. setupFrameState(left, top, right, bottom, opaque);
20. // Layer renderers will start the frame immediately
21. // The framebuffer renderer will first defer the display list
22. // for each layer and wait until the first drawing command
23. // to start the frame
24. if (mSnapshot->fbo == 0) {
25. syncState();
26. updateLayers();
27. else {
28. return startFrame();
29. }
30. return DrawGlInfo::kStatusDone;
31. }
本身这个onPreDraw其实是很简单的,无非是通过canvas来设置要显示的脏区域。最终逻辑依然是通过native层的OpenGLRenderer来实现,当然,这个实现最后变得有点复杂,因为这里又引入了一个新的概念,SnapShot:
[cpp] view plain copy
1. void OpenGLRenderer::setupFrameState(float left, float top,
2. float right, float bottom, bool opaque) {
3. mCaches.clearGarbage();
4. mOpaque = opaque;
5. new Snapshot(mFirstSnapshot,
6. SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
7. mSnapshot->fbo = getTargetFbo();
8. mSaveCount = 1;
9. mSnapshot->setClip(left, top, right, bottom);
10. mTilingClip.set(left, top, right, bottom);
11. }
我们这章引入的新概念实在过多,因此我们这里先跳过这个概念,可以参考这篇博客来先了解一下这个快照的概念,我们后面将单独开章节讲这个问题。
2.4 getDisplayList中段小结
由于getDisplayList实在是逻辑比较长,涉及的新概念比较多,我们这里暂时先暂停一下,回顾下前半部分的逻辑::函数前半部分的流程基本是createDisplayList,invalidateParentCaches,displayList.start。createDisplayList的作用是创建了一个java层的GLES20DisplayList,而displayList.start重点则是创建了一个native层GLES20RecordingCanvas,在创建Canvas的同时创建了DisplayListRenderer,DisplayListRenderer将需要执行的命令保存在内部的displaylist列表中,然后设置了视口,设置了脏区域,这里我们跳过了Snapshot这个新概念。
下面我们继续来看下getDisplayList后半段的代码。
[cpp] view plain copy
1. {
2. ...
3. try {
4. if (!isLayer && layerType != LAYER_TYPE_NONE) {
5. if (layerType == LAYER_TYPE_HARDWARE) {
6. final HardwareLayer layer = getHardwareLayer();
7. if (layer != null && layer.isValid()) {
8. canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
9. else {
10. if (layer != null && !layer.isValid()) Log.e("ViewSystem", "View #2 getHardwareLayer() is not valid.");
11. canvas.saveLayer(0, 0, mRight - mLeft, mBottom - mTop, mLayerPaint,
12. Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
13. Canvas.CLIP_TO_LAYER_SAVE_FLAG);
14. }
15. true;
16. else {
17. true);
18. true);
19. if (cache != null) {
20. canvas.drawBitmap(cache, 0, 0, mLayerPaint);
21. true;
22. }
23. }
24. else {
25. computeScroll();
26. canvas.translate(-mScrollX, -mScrollY);
27. if (!isLayer) {
28. mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
29. mPrivateFlags &= ~PFLAG_DIRTY_MASK;
30. }
31. // Fast path for layouts with no backgrounds
32. if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
33. dispatchDraw(canvas);
34. if (mOverlay != null && !mOverlay.isEmpty()) {
35. mOverlay.getOverlayView().draw(canvas);
36. }
37. else {
38. draw(canvas);
39. }
40. }
41. }
42. ...
2.5 开始生成绘制命令
上面这段代码涉及到两条分支,第一条分支又涉及到了新概念,layerType和HardwareLayer,这条分支比较复杂,我们单独放在下一章中单独讲解,我们现在重点看下第二条分支的代码:
[cpp] view plain copy
1. {
2. ...
3. else {
4. computeScroll();
5. canvas.translate(-mScrollX, -mScrollY);
6. if (!isLayer) {
7. mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
8. mPrivateFlags &= ~PFLAG_DIRTY_MASK;
9. }
10. // Fast path for layouts with no backgrounds
11. if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
12. dispatchDraw(canvas);
13. if (mOverlay != null && !mOverlay.isEmpty()) {
14. mOverlay.getOverlayView().draw(canvas);
15. }
16. else {
17. draw(canvas);
18. }
19. }
20. }
computeScroll计算滚动位置,和Graphic关系不大,不展开。
translate函数我们在上一节 Android 4.4 Graphic系统详解(4) 一个view的绘制之旅 中已经讲过了,这里不再展开。
其实就是创建了一个Op类的对象,然后将它保存在了DisplayListData的displayListOps容器里面,便于后面的执行。
回到view类getdisplaylist函数中来,说完了translate,下面就是重要的绘制生成流程了。
[cpp] view plain copy
1. {
2. if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
3. dispatchDraw(canvas);
4. if (mOverlay != null && !mOverlay.isEmpty()) {
5. mOverlay.getOverlayView().draw(canvas);
6. }
7. else {
8. draw(canvas);
9. }
10. }
其中dispatchDraw是通知view的所有孩子进行绘制(?),其实流程应该都是类似的,因此我们直接看当前view的draw流程,此函数将把View自身及其所有的子子孙孙绘制到给定的画布上。其画图过程主要分为以下六步:
1) 画背景
2) 如果需要,保存画布的层为未来的淡入淡出做好准备
3) 画View本身的内容
4) 画它的孩子
5) 如果需要,画淡入淡出的边缘并恢复层
6) 画装饰部分(如:滚动条)
上述六步中,2,5步常规情况下不需要,因此我们暂时跳过,只重点分析1,3,6步。
[cpp] view plain copy
1. public void draw(Canvas canvas) {
2. // Step 1, draw the background, if needed
3. int saveCount;
4.
5. if (!dirtyOpaque) {
6. final Drawable background = mBackground;
7. if (background != null) {
8. int scrollX = mScrollX;
9. int scrollY = mScrollY;
10. if (mBackgroundSizeChanged) {
11. background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
12. false;
13. }
14. if ((scrollX | scrollY) == 0) {
15. background.draw(canvas);
16. else {
17. canvas.translate(scrollX, scrollY);
18. background.draw(canvas);
19. canvas.translate(-scrollX, -scrollY);
20. }
21. }
22. }
23.
24. // skip step 2 & 5 if possible (common case)
25. int viewFlags = mViewFlags;
26. boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
27. boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
28. if (!verticalEdges && !horizontalEdges) {
29. // Step 3, draw the content
30. if (!dirtyOpaque) onDraw(canvas);
31. // Step 4, draw the children
32. dispatchDraw(canvas);
33. // Step 6, draw decorations (scrollbars)
34. onDrawScrollBars(canvas);
35. if (mOverlay != null && !mOverlay.isEmpty()) {
36. mOverlay.getOverlayView().dispatchDraw(canvas);
37. }
38. // we're done...
39. return;
40. }
41. }
2.5.1 绘制背景
绘制背景很简单,只是调用了 background.draw(canvas)函数,其中background是一个Drawable类型,关于Drawable的绘制,依然可以参考“一个view的绘制之旅”一章中的Drawable的绘制小节讲解,这里不再展开。
2.5.2 onDraw 绘制内容
这一过程比较复杂,其实也就是我们在上一章“一个view的绘制之旅”中讲到的全部内容,具体内容可参考上一章。
2.5.3 绘制滚动条
绘制滚动条的代码比较长,但是按照我们一贯的习惯,我们只关注和Graphic相关的部分,在onDrawScrollBars函数中有横向和纵向ScrollBar的区别,但是对于下层的Graphic并无这种区别,都是调用ScrollBarDrawable的draw函数:
[cpp] view plain copy
1. public void draw(Canvas canvas) {
2. true;
3. Rect r = getBounds();
4. if (drawTrack) {
5. drawTrack(canvas, r, vertical);
6. }
7. if (drawThumb) {
8. int size = vertical ? r.height() : r.width();
9. int thickness = vertical ? r.width() : r.height();
10. int length = Math.round((float) size * extent / range);
11. int offset = Math.round((float) (size - length) * mOffset / (range - extent));
12. drawThumb(canvas, r, offset, length, vertical);
13. }
14. }
drawThumb中逻辑转来转去,最终其实还是在drawThumb中再次调用了Drawable的draw函数,这里不再详述。
最终,通过漫长的过程,我们终于完成了displaylist的构建,在完成了buildDisplayList的调用之后,我们回到Gl20Renderer类,继续来看下的draw函数,接下来就该Displaylist的真正执行了。