Android(安卓)4.4 Graphic系统详解(4)HWUI概述
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
- privatevoidenableHardwareAcceleration(WindowManager.LayoutParamsattrs){
- mAttachInfo.mHardwareAccelerated=false;
- mAttachInfo.mHardwareAccelerationRequested=false;
- finalbooleanhardwareAccelerated=
- (attrs.flags&WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)!=0;
- if(hardwareAccelerated){
- if(!HardwareRenderer.isAvailable()){
- return;
- }
- //Persistentprocesses(includingthesystem)shouldnotdo
- //acceleratedrenderingonlow-enddevices.Inthatcase,
- //sRendererDisabledwillbeset.Inaddition,thesystemprocess
- //itselfshouldneverdoacceleratedrendering.Inthatcase,both
- //sRendererDisabledandsSystemRendererDisabledareset.When
- //sSystemRendererDisabledisset,PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
- //canbeusedbycodeonthesystemprocesstoescapethatandenable
- //HWaccelerateddrawing.(Thisisbasicallyforthelockscreen.)
- finalbooleanfakeHwAccelerated=(attrs.privateFlags&
- WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED)!=0;
- finalbooleanforceHwAccelerated=(attrs.privateFlags&
- WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED)!=0;
- if(!HardwareRenderer.sRendererDisabled||(HardwareRenderer.sSystemRendererDisabled
- &&forceHwAccelerated)){
- //Don'tenablehardwareaccelerationwhenwe'renotonthemainthread
- if(!HardwareRenderer.sSystemRendererDisabled&&
- Looper.getMainLooper()!=Looper.myLooper()){
- Log.w(HardwareRenderer.LOG_TAG,"Attemptingtoinitializehardware"
- +"accelerationoutsideofthemainthread,aborting");
- return;
- }
- if(mAttachInfo.mHardwareRenderer!=null){
- mAttachInfo.mHardwareRenderer.destroy(true);
- }
- finalbooleantranslucent=attrs.format!=PixelFormat.OPAQUE;
- mAttachInfo.mHardwareRenderer=HardwareRenderer.createGlRenderer(2,translucent);
- if(mAttachInfo.mHardwareRenderer!=null){
- mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());
- mAttachInfo.mHardwareAccelerated=
- mAttachInfo.mHardwareAccelerationRequested=true;
- }
- }elseif(fakeHwAccelerated){
- //Thewindowhadwantedtousehardwareacceleration,butthis
- //isnotallowedinitsprocess.Bysettingthisflag,itcan
- //stillrenderasifitwasaccelerated.Thisisbasicallyfor
- //thepreviewwindowsthewindowmanagershowsforlaunching
- //applications,sotheywilllookmoreliketheappbeinglaunched.
- mAttachInfo.mHardwareAccelerationRequested=true;
- }
- }
- }
上面代码不长,而且注释很完善,所以我们主要来看下函数的最主体部分即可。
[cpp] view plain copy
- staticHardwareRenderercreateGlRenderer(intglVersion,booleantranslucent){
- switch(glVersion){
- case2:
- returnGl20Renderer.create(translucent);
- }
- thrownewIllegalArgumentException("UnknownGLversion:"+glVersion);
- }
- staticHardwareRenderercreate(booleantranslucent){
- if(GLES20Canvas.isAvailable()){
- returnnewGl20Renderer(translucent);
- }
- returnnull;
- }
- GlRenderer(intglVersion,booleantranslucent){
- mGlVersion=glVersion;
- mTranslucent=translucent;
- loadSystemProperties(null);
- }
我们发现这段代码最主要的作用就是创建了一个Gl20Renderer(继承自GlRenderer和HardwareRenderer)。创建时调用了通过loadSystemProperties函数加载了一些属性和调试选项,这里不展开,有兴趣可以详细研究一下。
创建好的Gl20Renderer保存在了mAttachInfo.mHardwareRenderer中,后面我们在使用硬件加速时就会用到。
3 硬件绘制
从前面图中我们可以知道,在ViewRootImpl绘制时,调用了draw函数。有两种选择,一种是软件绘制,一种是硬件绘制。
[cpp] view plain copy
- if(attachInfo.mHardwareRenderer!=null&&attachInfo.mHardwareRenderer.isEnabled()){
- //Drawwithhardwarerenderer.
- mIsAnimating=false;
- mHardwareYOffset=yoff;
- mResizeAlpha=resizeAlpha;
- mCurrentDirty.set(dirty);
- dirty.setEmpty();
- attachInfo.mHardwareRenderer.draw(mView,attachInfo,this,
- animating?null:mCurrentDirty);
- }else{
- //Ifwegetherewithadisabled&requestedhardwarerenderer,somethingwent
- //wrong(aninvalidatepostedrightbeforewedestroyedthehardwaresurface
- //forinstance)soweshouldjustbailout.Lockingthesurfacewithsoftware
- //renderingatthispointwouldlockitforeverandpreventhardwarerenderer
- //fromdoingitsjobwhenitcomesback.
- //Beforewerequestanewframewemusthoweverattempttoreinitiliazethe
- //hardwarerendererifit'sinrequestedstate.Thiswouldhappenafteran
- //eglTerminate()forinstance.
- if(attachInfo.mHardwareRenderer!=null&&
- !attachInfo.mHardwareRenderer.isEnabled()&&
- attachInfo.mHardwareRenderer.isRequested()){
- try{
- attachInfo.mHardwareRenderer.initializeIfNeeded(mWidth,mHeight,
- mHolder.getSurface());
- }catch(OutOfResourcesExceptione){
- handleOutOfResourcesException(e);
- return;
- }
- mFullRedrawNeeded=true;
- scheduleTraversals();
- return;
- }
- if(!drawSoftware(surface,attachInfo,yoff,scalingRequired,dirty)){
- return;
- }
- }
- }
软件绘制不在我们分析范围内,这里我们将在下一小节中来研究一下attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,animating ? null : mCurrentDirty)这个调用。
我们前面已经知道mHardwareRenderer其实是一个Gl20Renderer,那么我们来看下它的draw函数。
[cpp] view plain copy
- voiddraw(Viewview,View.AttachInfoattachInfo,HardwareDrawCallbackscallbacks,
- Rectdirty){
- if(canDraw()){
- if(surfaceState!=SURFACE_STATE_ERROR){
- HardwareCanvascanvas=mCanvas;
- attachInfo.mHardwareCanvas=canvas;
- dirty=beginFrame(canvas,dirty,surfaceState);
- DisplayListdisplayList=buildDisplayList(view,canvas);
- intsaveCount=0;
- intstatus=DisplayList.STATUS_DONE;
- try{
- status=prepareFrame(dirty);
- if(mSurface.isValid()&&dirty!=null){
- mSurface.setDirtyRect(dirty);
- }
- saveCount=canvas.save();
- callbacks.onHardwarePreDraw(canvas);
- if(displayList!=null){
- status|=drawDisplayList(attachInfo,canvas,displayList,status);
- }
- }catch(Exceptione){
- Log.e(LOG_TAG,"Anerrorhasoccurredwhiledrawing:",e);
- }finally{
- callbacks.onHardwarePostDraw(canvas);
- canvas.restoreToCount(saveCount);
- view.mRecreateDisplayList=false;
- if(mDrawDelta>0){
- mFrameCount++;
- debugOverdraw(attachInfo,dirty,canvas,displayList);
- debugDirtyRegions(dirty,canvas);
- drawProfileData(attachInfo);
- }
- }
- onPostDraw();
- swapBuffers(status);
- }
- }
- }
上面这几十行代码,就是硬件绘制的最重要的几个流程调用,我们看到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
- staticvoidandroid_view_HardwareRenderer_beginFrame(JNIEnv*env,jobjectclazz,
- jintArraysize){
- EGLDisplaydisplay=eglGetCurrentDisplay();
- EGLSurfacesurface=eglGetCurrentSurface(EGL_DRAW);
- if(size){
- EGLintvalue;
- jint*storage=env->GetIntArrayElements(size,NULL);
- eglQuerySurface(display,surface,EGL_WIDTH,&value);
- storage[0]=value;
- eglQuerySurface(display,surface,EGL_HEIGHT,&value);
- storage[1]=value;
- env->ReleaseIntArrayElements(size,storage,0);
- }
- eglBeginFrame(display,surface);
在BeginFrame() 里获取EGLDisplay(用于显示) 和一个EGLSurface,OpenGL将在这个Surface上进行绘图。
eglBeginFrame主要是校验参数的合法性。
2 buildDisplayList
buildDisplayList是一个很重要也比较复杂的函数,通过这个函数,我们真正完成了视图绘制的录制工作,并将操作指令保存到了native层的DisplayList中,我们这里重点讲一下。
[cpp] view plain copy
- privateDisplayListbuildDisplayList(Viewview,HardwareCanvascanvas){
- if(mDrawDelta<=0){//如果根本没有新的绘制发生,使用之前的displaylist即可
- returnview.mDisplayList;
- }
- view.mRecreateDisplayList=(view.mPrivateFlags&View.PFLAG_INVALIDATED)
- ==View.PFLAG_INVALIDATED;
- view.mPrivateFlags&=~View.PFLAG_INVALIDATED;
- longbuildDisplayListStartTime=startBuildDisplayListProfiling();
- canvas.clearLayerUpdates();
- Trace.traceBegin(Trace.TRACE_TAG_VIEW,"getDisplayList");
- DisplayListdisplayList=view.getDisplayList();
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- endBuildDisplayListProfiling(buildDisplayListStartTime);
- returndisplayList;
- }
其实主要逻辑是通过view类的getDisplayList()方法实现的,这个方法通过递归的方式获得了整个View树的DisplayList:
[cpp] view plain copy
- privateDisplayListgetDisplayList(DisplayListdisplayList,booleanisLayer){
- if(((mPrivateFlags&PFLAG_DRAWING_CACHE_VALID)==0||
- displayList==null||!displayList.isValid()||
- (!isLayer&&mRecreateDisplayList))){
- //Don'tneedtorecreatethedisplaylist,justneedtotellour
- //childrentorestore/recreatetheirs
- if(displayList!=null&&displayList.isValid()&&
- !isLayer&&!mRecreateDisplayList){
- mPrivateFlags|=PFLAG_DRAWN|PFLAG_DRAWING_CACHE_VALID;
- mPrivateFlags&=~PFLAG_DIRTY_MASK;
- dispatchGetDisplayList();
- returndisplayList;
- }
- if(!isLayer){
- //Ifwegothere,we'rerecreatingit.Markitassuchtoensurethat
- //wecopyinchilddisplaylistsintooursindrawChild()
- mRecreateDisplayList=true;
- }
- if(displayList==null){
- displayList=mAttachInfo.mHardwareRenderer.createDisplayList(getClass().getName());
- //Ifwe'recreatinganewdisplaylist,makesureourparentgetsinvalidated
- //sincetheywillneedtorecreatetheirdisplaylisttoaccountforthis
- //newchilddisplaylist.
- invalidateParentCaches();
- }
- booleancaching=false;
- intwidth=mRight-mLeft;
- intheight=mBottom-mTop;
- intlayerType=getLayerType();
- finalHardwareCanvascanvas=displayList.start(width,height);
- ...
上面函数相当长,我们首先分析前半部分。暂时只看displaylist新创建时会走的逻辑:函数前半部分的流程基本是createDisplayList,invalidateParentCaches,displayList.start,我们来一点点分析。
2.1 createDisplayList
首先我们要创建一个displaylist,这里实际上只是创建了一个GLES20DisplayList类。
[cpp] view plain copy
- publicDisplayListcreateDisplayList(Stringname){
- returnnewGLES20DisplayList(name);
- }
GLES20DisplayList是DisplayList的子类,这是一个影子类,没有实际用途,主要用来管理Native层的DisplayList的销毁。Native层的DisplayList在后面的start函数中才被真正创建。
2.2 invalidateParentCaches
这个函数的作用主要是,一旦我们重建了DisplayList,那么这个view必须清空之前的缓存,我们通过这个函数调用来清空缓存。
[cpp] view plain copy
- protectedvoidinvalidateParentCaches(){
- if(mParentinstanceofView){
- ((View)mParent).mPrivateFlags|=PFLAG_INVALIDATED;
- }
- }
2.3 displayList.start
下面看下displayList.start函数:
[cpp] view plain copy
- publicHardwareCanvasstart(intwidth,intheight){
- mValid=false;
- mCanvas=GLES20RecordingCanvas.obtain(this);
- mCanvas.start();
- mCanvas.setViewport(width,height);
- //Thedirtyrectshouldalwaysbenullforadisplaylist
- mCanvas.onPreDraw(null);
- returnmCanvas;
- }
2.3.1 GLES20RecordingCanvas.obtain
start函数真正开始构建DisplayList的上下文,先来看下obtain函数:
[cpp] view plain copy
- staticGLES20RecordingCanvasobtain(GLES20DisplayListdisplayList){
- GLES20RecordingCanvascanvas=sPool.acquire();
- if(canvas==null){
- canvas=newGLES20RecordingCanvas();
- }
- canvas.mDisplayList=displayList;
- returncanvas;
- }
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
- protectedGLES20Canvas(booleanrecord,booleantranslucent){
- mOpaque=!translucent;
- if(record){
- mRenderer=nCreateDisplayListRenderer();
- }else{
- mRenderer=nCreateRenderer();
- }
- setupFinalizer();
- }
我们看到GLES20Canvas在创建时,由于我们现在需要录制命令,所以我们这里调用了nCreateDisplayListRenderer函数,创建的真正的用于渲染的本地对象DisplayListRenderer:
[cpp] view plain copy
- staticOpenGLRenderer*android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv*env,
- jobjectclazz){
- returnnewDisplayListRenderer;
- }
DisplayListRenderer是OpenGLRenderer的子类,相比于OpenGLRenderer是直接绘制,DisplayListRenderer则是将绘制命令保存在它内部的mDisplayListData中,mDisplayListData的类型是DisplayListData:
[cpp] view plain copy
- classDisplayListData:publicLightRefBase<DisplayListData>{
- public:
- LinearAllocatorallocator;
- Vector<DisplayListOp*>displayListOps;
- };
我们看到这里的重点必然是这个容器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
- publicvoidsetViewport(intwidth,intheight){
- mWidth=width;
- mHeight=height;
- nSetViewport(mRenderer,width,height);
- }
- staticvoidandroid_view_GLES20Canvas_setViewport(JNIEnv*env,jobjectclazz,
- OpenGLRenderer*renderer,jintwidth,jintheight){
- renderer->setViewport(width,height);
- }
- voidDisplayListRenderer::setViewport(intwidth,intheight){
- mOrthoMatrix.loadOrtho(0,width,height,0,-1,1);
- mWidth=width;
- mHeight=height;
- }
- voidMatrix4::loadOrtho(floatleft,floatright,floatbottom,floattop,floatnear,floatfar){
- loadIdentity();
- data[kScaleX]=2.0f/(right-left);
- data[kScaleY]=2.0f/(top-bottom);
- data[kScaleZ]=-2.0f/(far-near);
- data[kTranslateX]=-(right+left)/(right-left);
- data[kTranslateY]=-(top+bottom)/(top-bottom);
- data[kTranslateZ]=-(far+near)/(far-near);
- mType=kTypeTranslate|kTypeScale|kTypeRectToRect;
- }
我们看到上层调用了视口,下层实际上还是通过DisplayListRenderer做了具体实现,主要是做了一个正交投影,具体计算我们这里暂时先不研究。
其实大多数opengl在Canvas层面上的调用都是这样,底层通过render实现了类似OpenGl的实现,后面类似函数我们将不再展开。
2.3.4 GLES20RecordingCanvas.onPreDraw
GLES20RecordingCanvas.onPreDraw函数其实也很简单,先来看代码:
[cpp] view plain copy
- publicintonPreDraw(Rectdirty){
- if(dirty!=null){
- returnnPrepareDirty(mRenderer,dirty.left,dirty.top,dirty.right,dirty.bottom,
- mOpaque);
- }else{
- returnnPrepare(mRenderer,mOpaque);
- }
- }
- staticintandroid_view_GLES20Canvas_prepare(JNIEnv*env,jobjectclazz,
- OpenGLRenderer*renderer,jbooleanopaque){
- returnrenderer->prepare(opaque);
- }
- status_tOpenGLRenderer::prepare(boolopaque){
- returnprepareDirty(0.0f,0.0f,mWidth,mHeight,opaque);
- }
- status_tOpenGLRenderer::prepareDirty(floatleft,floattop,
- floatright,floatbottom,boolopaque){
- setupFrameState(left,top,right,bottom,opaque);
- //Layerrendererswillstarttheframeimmediately
- //Theframebufferrendererwillfirstdeferthedisplaylist
- //foreachlayerandwaituntilthefirstdrawingcommand
- //tostarttheframe
- if(mSnapshot->fbo==0){
- syncState();
- updateLayers();
- }else{
- returnstartFrame();
- }
- returnDrawGlInfo::kStatusDone;
- }
本身这个onPreDraw其实是很简单的,无非是通过canvas来设置要显示的脏区域。最终逻辑依然是通过native层的OpenGLRenderer来实现,当然,这个实现最后变得有点复杂,因为这里又引入了一个新的概念,SnapShot:
[cpp] view plain copy
- voidOpenGLRenderer::setupFrameState(floatleft,floattop,
- floatright,floatbottom,boolopaque){
- mCaches.clearGarbage();
- mOpaque=opaque;
- mSnapshot=newSnapshot(mFirstSnapshot,
- SkCanvas::kMatrix_SaveFlag|SkCanvas::kClip_SaveFlag);
- mSnapshot->fbo=getTargetFbo();
- mSaveCount=1;
- mSnapshot->setClip(left,top,right,bottom);
- mTilingClip.set(left,top,right,bottom);
- }
我们这章引入的新概念实在过多,因此我们这里先跳过这个概念,可以参考这篇博客来先了解一下这个快照的概念,我们后面将单独开章节讲这个问题。
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
- {
- ...
- try{
- if(!isLayer&&layerType!=LAYER_TYPE_NONE){
- if(layerType==LAYER_TYPE_HARDWARE){
- finalHardwareLayerlayer=getHardwareLayer();
- if(layer!=null&&layer.isValid()){
- canvas.drawHardwareLayer(layer,0,0,mLayerPaint);
- }else{
- if(layer!=null&&!layer.isValid())Log.e("ViewSystem","View#2getHardwareLayer()isnotvalid.");
- canvas.saveLayer(0,0,mRight-mLeft,mBottom-mTop,mLayerPaint,
- Canvas.HAS_ALPHA_LAYER_SAVE_FLAG|
- Canvas.CLIP_TO_LAYER_SAVE_FLAG);
- }
- caching=true;
- }else{
- buildDrawingCache(true);
- Bitmapcache=getDrawingCache(true);
- if(cache!=null){
- canvas.drawBitmap(cache,0,0,mLayerPaint);
- caching=true;
- }
- }
- }else{
- computeScroll();
- canvas.translate(-mScrollX,-mScrollY);
- if(!isLayer){
- mPrivateFlags|=PFLAG_DRAWN|PFLAG_DRAWING_CACHE_VALID;
- mPrivateFlags&=~PFLAG_DIRTY_MASK;
- }
- //Fastpathforlayoutswithnobackgrounds
- if((mPrivateFlags&PFLAG_SKIP_DRAW)==PFLAG_SKIP_DRAW){
- dispatchDraw(canvas);
- if(mOverlay!=null&&!mOverlay.isEmpty()){
- mOverlay.getOverlayView().draw(canvas);
- }
- }else{
- draw(canvas);
- }
- }
- }
- ...
2.5 开始生成绘制命令
上面这段代码涉及到两条分支,第一条分支又涉及到了新概念,layerType和HardwareLayer,这条分支比较复杂,我们单独放在下一章中单独讲解,我们现在重点看下第二条分支的代码:
[cpp] view plain copy
- {
- ...
- else{
- computeScroll();
- canvas.translate(-mScrollX,-mScrollY);
- if(!isLayer){
- mPrivateFlags|=PFLAG_DRAWN|PFLAG_DRAWING_CACHE_VALID;
- mPrivateFlags&=~PFLAG_DIRTY_MASK;
- }
- //Fastpathforlayoutswithnobackgrounds
- if((mPrivateFlags&PFLAG_SKIP_DRAW)==PFLAG_SKIP_DRAW){
- dispatchDraw(canvas);
- if(mOverlay!=null&&!mOverlay.isEmpty()){
- mOverlay.getOverlayView().draw(canvas);
- }
- }else{
- draw(canvas);
- }
- }
- }
computeScroll计算滚动位置,和Graphic关系不大,不展开。
translate函数我们在上一节 Android 4.4 Graphic系统详解(4) 一个view的绘制之旅 中已经讲过了,这里不再展开。
其实就是创建了一个Op类的对象,然后将它保存在了DisplayListData的displayListOps容器里面,便于后面的执行。
回到view类getdisplaylist函数中来,说完了translate,下面就是重要的绘制生成流程了。
[cpp] view plain copy
- {
- if((mPrivateFlags&PFLAG_SKIP_DRAW)==PFLAG_SKIP_DRAW){
- dispatchDraw(canvas);
- if(mOverlay!=null&&!mOverlay.isEmpty()){
- mOverlay.getOverlayView().draw(canvas);
- }
- }else{
- draw(canvas);
- }
- }
其中dispatchDraw是通知view的所有孩子进行绘制(?),其实流程应该都是类似的,因此我们直接看当前view的draw流程,此函数将把View自身及其所有的子子孙孙绘制到给定的画布上。其画图过程主要分为以下六步:
1) 画背景 2) 如果需要,保存画布的层为未来的淡入淡出做好准备 3) 画View本身的内容 4) 画它的孩子 5) 如果需要,画淡入淡出的边缘并恢复层 6) 画装饰部分(如:滚动条)
上述六步中,2,5步常规情况下不需要,因此我们暂时跳过,只重点分析1,3,6步。
[cpp] view plain copy
- publicvoiddraw(Canvascanvas){
- //Step1,drawthebackground,ifneeded
- intsaveCount;
- if(!dirtyOpaque){
- finalDrawablebackground=mBackground;
- if(background!=null){
- finalintscrollX=mScrollX;
- finalintscrollY=mScrollY;
- if(mBackgroundSizeChanged){
- background.setBounds(0,0,mRight-mLeft,mBottom-mTop);
- mBackgroundSizeChanged=false;
- }
- if((scrollX|scrollY)==0){
- background.draw(canvas);
- }else{
- canvas.translate(scrollX,scrollY);
- background.draw(canvas);
- canvas.translate(-scrollX,-scrollY);
- }
- }
- }
- //skipstep2&5ifpossible(commoncase)
- finalintviewFlags=mViewFlags;
- booleanhorizontalEdges=(viewFlags&FADING_EDGE_HORIZONTAL)!=0;
- booleanverticalEdges=(viewFlags&FADING_EDGE_VERTICAL)!=0;
- if(!verticalEdges&&!horizontalEdges){
- //Step3,drawthecontent
- if(!dirtyOpaque)onDraw(canvas);
- //Step4,drawthechildren
- dispatchDraw(canvas);
- //Step6,drawdecorations(scrollbars)
- onDrawScrollBars(canvas);
- if(mOverlay!=null&&!mOverlay.isEmpty()){
- mOverlay.getOverlayView().dispatchDraw(canvas);
- }
- //we'redone...
- return;
- }
- }
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
- publicvoiddraw(Canvascanvas){
- booleandrawTrack=true;
- Rectr=getBounds();
- if(drawTrack){
- drawTrack(canvas,r,vertical);
- }
- if(drawThumb){
- intsize=vertical?r.height():r.width();
- intthickness=vertical?r.width():r.height();
- intlength=Math.round((float)size*extent/range);
- intoffset=Math.round((float)(size-length)*mOffset/(range-extent));
- drawThumb(canvas,r,offset,length,vertical);
- }
- }
drawThumb中逻辑转来转去,最终其实还是在drawThumb中再次调用了Drawable的draw函数,这里不再详述。
最终,通过漫长的过程,我们终于完成了displaylist的构建,在完成了buildDisplayList的调用之后,我们回到Gl20Renderer类,继续来看下的draw函数,接下来就该Displaylist的真正执行了。
更多相关文章
- 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
- GitHub 标星 2.5K+!教你通过玩游戏的方式学习 VIM!
- 如何在后台运行Linux命令?
- No.11 使用firewall配置的防火墙策略的生效模式
- Cocos2d-x +Android(安卓)+ Windows 7环境配置
- Android直播开发之旅(15):libjpeg库的编译移植与使用
- android 模拟器和PC用sockets通讯
- Android应用开发笔记(12):Android应用的自动升级、更新模块的实现
- [置顶] 玩转pandaboard之linaro对于Android的编译上的一些优化