成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Android源碼進階之深入理解View的繪制流程(Draw)機制

開發 前端
三大工作流程始于ViewRootImpl#performTraversals,在這個方法內部會分別調用performMeasure,performLayout,performDraw三個方法來分別完成測量,布局,繪制流程。那么我們現在先從performDraw方法看起。

[[426758]]

前言

前幾篇文章,講述了measure,layout流程等,接下來將詳細分析繪制流程。

測量流程決定了View的大小,布局流程決定了View的位置,那么繪制流程將決定View的樣子,一個View該顯示什么由繪制流程完成;

那我們就開始開車了;

一、performDraw

三大工作流程始于ViewRootImpl#performTraversals,在這個方法內部會分別調用performMeasure,performLayout,performDraw三個方法來分別完成測量,布局,繪制流程。那么我們現在先從performDraw方法看起;

performDraw

  1. private void performDraw() { 
  2.     //... 
  3.     final boolean fullRedrawNeeded = mFullRedrawNeeded; 
  4.     try { 
  5.         draw(fullRedrawNeeded); 
  6.     } finally { 
  7.         mIsDrawing = false
  8.         Trace.traceEnd(Trace.TRACE_TAG_VIEW); 
  9.     } 

里面又調用了ViewRootImpl#draw方法,我們來看看ViewRootImpl#draw:

  1. private void draw(boolean fullRedrawNeeded) { 
  2.     ... 
  3.     //獲取mDirty,該值表示需要重繪的區域 
  4.     final Rect dirty = mDirty; 
  5.     if (mSurfaceHolder != null) { 
  6.         // The app owns the surface, we won't draw. 
  7.         dirty.setEmpty(); 
  8.         if (animating) { 
  9.             if (mScroller != null) { 
  10.                 mScroller.abortAnimation(); 
  11.             } 
  12.             disposeResizeBuffer(); 
  13.         } 
  14.         return
  15.     } 
  16.     //如果fullRedrawNeeded為真,則把dirty區域置為整個屏幕,表示整個視圖都需要繪制 
  17.     //第一次繪制流程,需要繪制所有視圖 
  18.     if (fullRedrawNeeded) { 
  19.         mAttachInfo.mIgnoreDirtyState = true
  20.         dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); 
  21.     } 
  22.     //... 
  23.     if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) { 
  24.                 return
  25.         } 

根據fullRedrawNeeded來判斷是否需要重置dirty區域,最后調用了ViewRootImpl#drawSoftware方法,并把相關參數傳遞進去,包括dirty區域,我們接著看該方法的源碼;

  1. private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, 
  2.             boolean scalingRequired, Rect dirty) { 
  3.     // Draw with software renderer. 
  4.     final Canvas canvas; 
  5.     try { 
  6.         final int left = dirty.left
  7.         final int top = dirty.top
  8.         final int right = dirty.right
  9.         final int bottom = dirty.bottom; 
  10.         //鎖定canvas區域,由dirty區域決定 
  11.         canvas = mSurface.lockCanvas(dirty); 
  12.         // The dirty rectangle can be modified by Surface.lockCanvas() 
  13.         //noinspection ConstantConditions 
  14.         if (left != dirty.left || top != dirty.top || right != dirty.right 
  15.                 || bottom != dirty.bottom) { 
  16.             attachInfo.mIgnoreDirtyState = true
  17.         } 
  18.         canvas.setDensity(mDensity); 
  19.     }  
  20.     try { 
  21.         if (!canvas.isOpaque() || yoff != 0 || xoff != 0) { 
  22.             canvas.drawColor(0, PorterDuff.Mode.CLEAR); 
  23.         } 
  24.         dirty.setEmpty(); 
  25.         mIsAnimating = false
  26.         attachInfo.mDrawingTime = SystemClock.uptimeMillis(); 
  27.         mView.mPrivateFlags |= View.PFLAG_DRAWN; 
  28.         try { 
  29.             canvas.translate(-xoff, -yoff); 
  30.             if (mTranslator != null) { 
  31.                 mTranslator.translateCanvas(canvas); 
  32.             } 
  33.             canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0); 
  34.             attachInfo.mSetIgnoreDirtyState = false
  35.             //正式開始繪制 
  36.             mView.draw(canvas); 
  37.         } 
  38.     }  
  39.     return true

實例化了Canvas對象,然后鎖定該canvas的區域,由dirty區域決定,接著對canvas進行一系列的屬性賦值,最后調用了mView.draw(canvas)方法,

mView就是DecorView,也就是說從DecorView開始繪制;

二、draw源碼詳解

由于ViewGroup沒有重寫draw方法,因此所有的View都是調用View#draw方法,因此,我們直接看它的源碼

  1. public void draw(Canvas canvas) { 
  2.     ....  
  3.     // 1. 繪制本身View背景 
  4.     if (!dirtyOpaque) { 
  5.         drawBackground(canvas); 
  6.     } 
  7.     if (!verticalEdges && !horizontalEdges) { 
  8.         // Step 3, draw the content 
  9.         // 2. 繪制內容,默認空實現 需復寫 
  10.         if (!dirtyOpaque) onDraw(canvas); 
  11.         // 3. 繪制 children  
  12.         dispatchDraw(canvas); 
  13.         drawAutofilledHighlight(canvas); 
  14.         // 4. 分發Draw (單一View空實現,ViewGroup見下面分析) 
  15.         if (mOverlay != null && !mOverlay.isEmpty()) { 
  16.             mOverlay.getOverlayView().dispatchDraw(canvas); 
  17.         } 
  18.         // 5. 繪制裝飾 (前景色,滾動條) 
  19.         onDrawForeground(canvas); 
  20.         return
  21.     } 
  22.     .... 

可以看到,draw過程比較復雜,但是邏輯十分清晰。首先來看一開始的標記位dirtyOpaque,

該標記位的作用是判斷當前View是否是透明的,如果View是透明的,那么根據下面的邏輯可以看出,將不會執行一些步驟,比如繪制背景、繪制內容等;

繪制流程的五個步驟:

  • 對View的背景進行繪制;
  • 繪制View的內容;
  • 對View的子View進行繪制(如果有子View);
  • 分發Draw;

繪制View的裝飾(例如:前景色,滾動條);

1、繪制背景

  1. //繪制背景 
  2. private void drawBackground(Canvas canvas) { 
  3.     final Drawable background = mBackground; 
  4.     if (background == null) { 
  5.         return
  6.     } 
  7.     // 根據在 layout 過程中獲取的 View 的位置參數,來設置背景的邊界 
  8.     setBackgroundBounds(); 
  9.     // 先嘗試用HWUI繪制 
  10.     if (canvas.isHardwareAccelerated() && mAttachInfo != null 
  11.             && mAttachInfo.mThreadedRenderer != null) { 
  12.         mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode); 
  13.         final RenderNode renderNode = mBackgroundRenderNode; 
  14.         if (renderNode != null && renderNode.isValid()) { 
  15.             setBackgroundRenderNodeProperties(renderNode); 
  16.             ((DisplayListCanvas) canvas).drawRenderNode(renderNode); 
  17.             return
  18.         } 
  19.     } 
  20.     final int scrollX = mScrollX; 
  21.     final int scrollY = mScrollY; 
  22.     if ((scrollX | scrollY) == 0) { 
  23.         //調用 Drawable 的 draw 方法來進行背景的繪制 
  24.         background.draw(canvas); 
  25.     } else { 
  26.         // 若 mScrollX 和 mScrollY 有值,則對 canvas 的坐標進行偏移平移畫布 
  27.         canvas.translate(scrollX, scrollY); 
  28.         //調用 Drawable 的 draw 方法來進行背景的繪制 
  29.         background.draw(canvas); 
  30.         canvas.translate(-scrollX, -scrollY); 
  31.     } 

2、繪制View的內容

  1. // 繪制View本身內容,空實現,子類必須復寫 
  2. protected void onDraw(Canvas canvas) { 

這里調用了View#onDraw方法,View中該方法是一個空實現,因為不同的View有著不同的內容,這需要我們自己去實現,即在自定義View中重寫該方法來實現;

3、子View進行繪制

當前的View是一個ViewGroup類型,那么就需要繪制它的子View,這里調用了dispatchDraw,而View中該方法是空實現,實際是ViewGroup重寫了這個方法,那么我們來看看;

  1. @Override 
  2. protected void dispatchDraw(Canvas canvas) { 
  3.     ... 
  4.     //  遍歷子View 
  5.     final int childrenCount = mChildrenCount; 
  6.     ... 
  7.     for (int i = 0; i < childrenCount; i++) { 
  8.         while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) { 
  9.             final View transientChild = mTransientViews.get(transientIndex); 
  10.             if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || 
  11.                     transientChild.getAnimation() != null) { 
  12.                 more |= drawChild(canvas, transientChild, drawingTime); 
  13.             } 
  14.             transientIndex++; 
  15.             if (transientIndex >= transientCount) { 
  16.                 transientIndex = -1; 
  17.             } 
  18.         } 
  19.         final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); 
  20.         final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); 
  21.         if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 
  22.             // 調用 drawChild 方法,進行子元素繪制 
  23.             more |= drawChild(canvas, child, drawingTime); 
  24.         } 
  25.     } 
  26.     .... 

4、分發Draw

  1. @Override 
  2. protected void dispatchDraw(Canvas canvas) { 
  3.     ... 
  4.     // 1. 遍歷子View 
  5.     final int childrenCount = mChildrenCount; 
  6.     ... 
  7.     for (int i = 0; i < childrenCount; i++) { 
  8.         while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) { 
  9.             final View transientChild = mTransientViews.get(transientIndex); 
  10.             if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || 
  11.                     transientChild.getAnimation() != null) { 
  12.                 more |= drawChild(canvas, transientChild, drawingTime); 
  13.             } 
  14.             transientIndex++; 
  15.             if (transientIndex >= transientCount) { 
  16.                 transientIndex = -1; 
  17.             } 
  18.         } 
  19.         final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); 
  20.         final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); 
  21.         if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { 
  22.             // 調用 drawChild 方法,進行子元素繪制 
  23.             more |= drawChild(canvas, child, drawingTime); 
  24.         } 
  25.     } 
  26.     .... 

5、繪制View

所謂的繪制裝飾,就是指View除了背景、內容、子View的其余部分,例如滾動條等,我們看View#onDrawForeground

  1. public void onDrawForeground(Canvas canvas) { 
  2.     //繪制指示器 
  3.     onDrawScrollIndicators(canvas); 
  4.     //繪制滾動條 
  5.     onDrawScrollBars(canvas); 
  6.     final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null
  7.     if (foreground != null) { 
  8.         if (mForegroundInfo.mBoundsChanged) { 
  9.             mForegroundInfo.mBoundsChanged = false
  10.             final Rect selfBounds = mForegroundInfo.mSelfBounds; 
  11.             final Rect overlayBounds = mForegroundInfo.mOverlayBounds; 
  12.             if (mForegroundInfo.mInsidePadding) { 
  13.                 selfBounds.set(0, 0, getWidth(), getHeight()); 
  14.             } else { 
  15.                 selfBounds.set(getPaddingLeft(), getPaddingTop(), 
  16.                         getWidth() - getPaddingRight(), getHeight() - getPaddingBottom()); 
  17.             } 
  18.             final int ld = getLayoutDirection(); 
  19.             Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(), 
  20.                     foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld); 
  21.             foreground.setBounds(overlayBounds); 
  22.         } 
  23.         //調用 Drawable 的 draw 方法,繪制前景色 
  24.         foreground.draw(canvas); 
  25.     } 

到目前為止,View的繪制流程也講述完畢了;

總結

其實繪制這塊還是很重要的,下次還是要繼續講解下;

學如逆水行舟,不進則退;心似平原走馬,易放難收;

一起加油老鐵們

本文轉載自微信公眾號「Android開發編程」

【編輯推薦】

 

責任編輯:姜華 來源: Android開發編程
相關推薦

2021-09-16 06:44:04

Android進階流程

2021-10-15 09:19:17

AndroidSharedPrefe分析源碼

2021-09-17 06:55:50

AndroidLayoutView

2021-09-08 06:51:52

AndroidRetrofit原理

2021-08-24 07:53:28

AndroidActivity生命周期

2021-09-15 07:31:33

Android窗口管理

2021-09-24 08:10:40

Java 語言 Java 基礎

2021-09-10 07:31:54

AndroidAppStartup原理

2021-09-18 06:56:01

JavaCAS機制

2017-05-03 17:00:16

Android渲染機制

2024-12-30 08:02:40

2022-10-11 07:43:34

AndroidSyncGradle 構建

2021-08-17 13:41:11

AndroidView事件

2017-01-13 22:42:15

iosswift

2014-07-15 17:17:31

AdapterAndroid

2017-08-08 09:15:41

前端JavaScript頁面渲染

2021-02-17 11:25:33

前端JavaScriptthis

2017-07-12 14:58:21

AndroidInstant Run

2016-10-26 20:49:24

ReactJavascript前端

2024-06-06 09:58:13

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 97精品超碰一区二区三区 | 色婷婷综合网站 | 日韩a | 九九导航 | 久久99精品久久久久久国产越南 | 国产成人精品久久二区二区91 | 99在线免费观看视频 | 日日夜夜精品视频 | 精品国产免费一区二区三区五区 | 国产一区二区毛片 | 福利视频1000| 91在线网站 | 成人性视频免费网站 | 99免费| 国产精品免费一区二区三区四区 | 亚洲图片视频一区 | 久久久久亚洲国产| 国产精品久久久久久久久久久久久 | 国产区精品 | 成人福利在线观看 | 日韩激情在线 | 夜夜草| 天天干天天爽 | 免费精品| 午夜免费电影院 | 午夜天堂精品久久久久 | 久www| 国产99视频精品免费播放照片 | 久操伊人 | 99热播精品| 91精品国产91久久久久久 | 亚洲天堂精品一区 | 免费看一级毛片 | 在线视频a | 国产美女久久久 | 午夜精品久久久久久久久久久久久 | 国产精品久久福利 | 国产视频三级 | 久久最新精品 | 在线观看国产精品一区二区 | 超碰97在线免费 |