日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

View的绘制-draw流程详解

發布時間:2025/3/20 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 View的绘制-draw流程详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

作用

根據 measure 測量出的寬高,layout 布局的位置,渲染整個 View 樹,將界面呈現出來。

具體分析

以下源碼基于版本27

DecorView 的draw 流程

在《View的繪制-measure流程詳解》中說過,View 的繪制流程是從 ViewRootViewImpl 中的 performMeasure()、performLayout、performDraw 開始的。在執行完 performMeasure() 、performLayout 后,開始執行 performDraw 方法:(以下源碼有所刪減)

//ViewRootViewImpl 類private void performDraw() {....draw(fullRedrawNeeded);....} ------------------------------------------------------------------------- //ViewRootViewImpl 類 private void draw(boolean fullRedrawNeeded) {....mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);.... }------------------------------------------------------------------------- //ThreadedRenderer 類 void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {....updateRootDisplayList(view, callbacks);.... } ------------------------------------------------------------------------- //ThreadedRenderer 類 private void updateRootDisplayList(View view, DrawCallbacks callbacks) {....updateViewTreeDisplayList(view);.... } ------------------------------------------------------------------------- //ThreadedRenderer 類 private void updateViewTreeDisplayList(View view) {view.mPrivateFlags |= View.PFLAG_DRAWN;view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)== View.PFLAG_INVALIDATED;view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;//這里調用了 View 的 updateDisplayListIfDirty 方法 //這個 View 其實就是 DecorViewview.updateDisplayListIfDirty();view.mRecreateDisplayList = false; } 復制代碼

接下來查看 View 的 updateDisplayListIfDirty 方法:

//View 類public RenderNode updateDisplayListIfDirty() {....if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {dispatchDraw(canvas);drawAutofilledHighlight(canvas);if (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().draw(canvas);}if (debugDraw()) {debugDrawFocus(canvas);}} else {/*最終調用了 DecorView 的 draw 方法,為什么沒有走上面的 dispatchDraw(canvas)我也母雞啊,我是Debug 斷點調試曉得走這里的,哈哈*/draw(canvas);}.... } ------------------------------------------------------------------------- //DecorView 重寫了 draw 方法。所以走到了 DecorView 的 draw 方法 @Override public void draw(Canvas canvas) {//調用父類 (View)的 draw 方法super.draw(canvas);if (mMenuBackground != null) {mMenuBackground.draw(canvas);} } 復制代碼

以上流程,推薦兩篇文章:ViewRootImpl的performDraw過程 ~~~~~~~~~~~~~~~~~淺談ondraw的前世今身

View 的 draw 流程

就這樣, View 的繪制就開始啦。主要有四個步驟:

  • drawBackground 繪制背景色
  • onDraw 繪制內容
  • dispatchDraw 繪制 children
  • onDrawForeground 繪制裝飾(前景,滾動條)
//View 類 /***手動將此視圖(及其所有子項)渲染到給定的Canvas。在調用此函數前,視圖必須已經完成了完整布局(layout)。*一般我們在自定義控件繼承 View 的時候,不要重寫 draw 方法,只需重寫 onDraw 方法*/ public void draw(Canvas canvas) {....// Step 1, draw the background, if neededint saveCount;//繪制背景if (!dirtyOpaque) {drawBackground(canvas);}// skip step 2 & 5 if possible (common case)final int viewFlags = mViewFlags;boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;if (!verticalEdges && !horizontalEdges) {// Step 3, draw the content// 繪制內容if (!dirtyOpaque) onDraw(canvas);// Step 4, draw the children//繪制 children dispatchDraw(canvas);drawAutofilledHighlight(canvas);// Overlay is part of the content and draws beneath Foregroundif (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().dispatchDraw(canvas);}// Step 6, draw decorations (foreground, scrollbars)//繪制裝飾 (前景色,滾動條)onDrawForeground(canvas);// Step 7, draw the default focus highlightdrawDefaultFocusHighlight(canvas);if (debugDraw()) {debugDrawFocus(canvas);}// we're done...return;}.... } 復制代碼

我們對四個步驟進行分析:

//View 類 //繪制背景 private void drawBackground(Canvas canvas) {final Drawable background = mBackground;//如果沒有設置背景,就不進行繪制if (background == null) {return;}//如果設置了背景嗎,且背景的大小發生了改變,//就用 layout 計算出的四個邊界值來確定背景的邊界setBackgroundBounds();// Attempt to use a display list if requested.if (canvas.isHardwareAccelerated() && mAttachInfo != null&& mAttachInfo.mThreadedRenderer != null) {mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);final RenderNode renderNode = mBackgroundRenderNode;if (renderNode != null && renderNode.isValid()) {setBackgroundRenderNodeProperties(renderNode);((DisplayListCanvas) canvas).drawRenderNode(renderNode);return;}}final int scrollX = mScrollX;final int scrollY = mScrollY;if ((scrollX | scrollY) == 0) {//調用 Drawable 的 draw 方法來進行背景的繪制background.draw(canvas);} else {//平移畫布canvas.translate(scrollX, scrollY);//調用 Drawable 的 draw 方法來進行背景的繪制background.draw(canvas);canvas.translate(-scrollX, -scrollY);} } ------------------------------------------------------------------------- //View 類 //繪制內容 protected void onDraw(Canvas canvas) {/*View 中的 onDraw 是一個空實現。也不難理解,當我們自定義控件繼承 View 的時候,需要重寫 onDraw 方法,通過 Canvas 和 Paint 來進行內容的繪制*/ } ------------------------------------------------------------------------- //View 類 //繪制 children protected void dispatchDraw(Canvas canvas) {/*View 中的 dispatchDraw 也是一個空實現。因為單獨一個 View 本身是沒有子元素的,不需要繪制 children */ }------------------------------------------------------------------------- //View 類 //繪制裝飾 public void onDrawForeground(Canvas canvas) {//繪制指示器onDrawScrollIndicators(canvas);//繪制滾動條onDrawScrollBars(canvas);final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;if (foreground != null) {if (mForegroundInfo.mBoundsChanged) {mForegroundInfo.mBoundsChanged = false;final Rect selfBounds = mForegroundInfo.mSelfBounds;final Rect overlayBounds = mForegroundInfo.mOverlayBounds;if (mForegroundInfo.mInsidePadding) {selfBounds.set(0, 0, getWidth(), getHeight());} else {selfBounds.set(getPaddingLeft(), getPaddingTop(),getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());}final int ld = getLayoutDirection();Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(),foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld);foreground.setBounds(overlayBounds);}//調用 Drawable 的 draw 方法,繪制前景色foreground.draw(canvas);} } 復制代碼

以上就是 View 的繪制流程了。ViewGroup 本身是繼承 View 的,它的基本繪制流程也是通過父類 View 進行的,只不過它重寫了 dispatchDraw 方法,來進行子元素的繪制。下面我們來進行具體分析:

ViewGroup 的繪制 dispatchDraw 流程

//ViewGroup 類 @Override protected void dispatchDraw(Canvas canvas) {....for (int i = 0; i < childrenCount; i++) {while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {final View transientChild = mTransientViews.get(transientIndex);if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||transientChild.getAnimation() != null) {more |= drawChild(canvas, transientChild, drawingTime);}transientIndex++;if (transientIndex >= transientCount) {transientIndex = -1;}}final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {//調用 drawChild 方法,進行繪制子元素more |= drawChild(canvas, child, drawingTime);}}.... } ------------------------------------------------------------------------- //ViewGroup 類 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {//調用 View 的 draw 方法,這里要注意,調用的是 View 的三個參數的 draw 方法return child.draw(canvas, this, drawingTime); }復制代碼

在 View 中還有一個 draw(Canvas canvas) 的重載方法,就是 draw(Canvas canvas, ViewGroup parent, long drawingTime):

//View 類 /*** ViewGroup.drawChild()調用此方法以使每個子視圖自己繪制。* 這是View專門根據圖層類型和硬件加速來渲染行為的地方。*/ boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {....//是否支持硬件加速boolean drawingWithRenderNode = mAttachInfo != null&& mAttachInfo.mHardwareAccelerated&& hardwareAcceleratedCanvas;if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) {if (layerType != LAYER_TYPE_NONE) {//未開啟//調用 View 的 buildDrawingCache 方法buildDrawingCache(true);}cache = getDrawingCache(true);}//開啟了硬件加速if (drawingWithRenderNode) {//調用 View 的 updateDisplayListIfDirty 方法renderNode = updateDisplayListIfDirty();if (!renderNode.isValid()) {// Uncommon, but possible. If a view is removed from the hierarchy during the call// to getDisplayList(), the display list will be marked invalid and we should not// try to use it again.renderNode = null;drawingWithRenderNode = false;}}.... } 復制代碼

分別查看 buildDrawingCache 和 updateDisplayListIfDirty 方法:

//View 類 public void buildDrawingCache(boolean autoScale) {....buildDrawingCacheImpl(autoScale);.... } ------------------------------------------------------------------------- //View 類 private void buildDrawingCacheImpl(boolean autoScale) {....// 如果不需要進行自身繪制,就直接調用 dispatchDraw 繪制子 Children//否則就直接調用 View 的 draw 方法if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {mPrivateFlags &= ~PFLAG_DIRTY_MASK;dispatchDraw(canvas);drawAutofilledHighlight(canvas);if (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().draw(canvas);}} else {draw(canvas);}.... } -------------------------------------------------------------------------//View 類 public RenderNode updateDisplayListIfDirty() {....// 如果不需要進行自身繪制,就直接調用 dispatchDraw 繪制子 Children//否則就直接調用 View 的 draw 方法if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {dispatchDraw(canvas);drawAutofilledHighlight(canvas);if (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().draw(canvas);}if (debugDraw()) {debugDrawFocus(canvas);}} else {draw(canvas);}.... } 復制代碼

如此,從頂層 DecorView 的 draw 方法開始,然后調用 dispatchDraw 方法循環遍歷繪制子元素,如果子元素是繼承了 ViewGroup ,就再次循環調用 dispatchDraw 方法,一層層往下遞歸調用,直到每一個子元素都被繪制完成,整個 draw 流程也就結束了。

tips: 在我們使用真機進行源碼斷點調試的時候,可能會出現源碼不能打斷點的情況,或者斷點沒有走在該走的地方。這是因為國內手機廠商基本都是定制系統,可能修改了源碼。這個時候可以使用模擬器進行斷點調試。注意:模擬器版本號要和項目編譯版本號一致!

setWillNotDraw 解析

在 View 中有一個方法是 setWillNotDraw:

//View 類 /*** If this view doesn't do any drawing on its own, set this flag to* allow further optimizations. By default, this flag is not set on* View, but could be set on some View subclasses such as ViewGroup.** Typically, if you override {@link #onDraw(android.graphics.Canvas)}* you should clear this flag.** @param willNotDraw whether or not this View draw on its own*/ public void setWillNotDraw(boolean willNotDraw) {setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK); } 復制代碼

從注釋上看,如果此視圖本身不執行任何繪制,就設置為 true,系統會進行一些繪制優化。View 本身是默認設置為 false 的,沒有啟動這個優化標記(這也不難理解,因為一般我們自定義控件繼承 View 的時候,是要重寫 onDraw 方法進行繪制的)。ViewGroup 默認是開啟這個優化標記的。當然如果明確 ViewGroup 是要通過 onDraw 方法進行繪制的時候,要手動關閉這個標記( setWillNotDraw(false) )。

示例:

我們自定義一個控件,繼承 ViewGroup,重寫 onDraw 方法。

public class MyViewGroup extends ViewGroup {public MyViewGroup(Context context) {super(context);setWillNotDraw(false);}public MyViewGroup(Context context, AttributeSet attrs) {super(context, attrs);//這里如果不調用這句話,我們在使用的時候,onDraw 方法不會被調用setWillNotDraw(false);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {//onLayout 在這里必須重寫,因為在 ViewGroup 中 onLayout是一個抽象方法}//重寫 onDraw 方法@Overrideprotected void onDraw(Canvas canvas) {canvas.drawColor(Color.BLACK);} } 復制代碼

xml 中使用

<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><com.ownchan.miclock.study.MyViewGroupandroid:layout_width="match_parent"android:background="@color/black"android:layout_height="match_parent"/> </FrameLayout> 復制代碼

當我們的自定義控件在繼承 ViewGroup 的時候,如果需要重寫 onDraw 方法進行繪制,需要執行 setWillNotDraw(false) 。

推薦一個詳解 draw 和 onDraw 調用時機好文: 你真的了解Android ViewGroup的draw和onDraw的調用時機嗎

總結

參考文檔

《Android開發藝術探索》第四章-View的工作原理

自定義View Draw過程- 最易懂的自定義View原理系列(4)

ViewRootImpl的performDraw過程

你真的了解Android ViewGroup的draw和onDraw的調用時機嗎

淺談ondraw的前世今身

轉載于:https://juejin.im/post/5cc17280e51d4514df4206b4

總結

以上是生活随笔為你收集整理的View的绘制-draw流程详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产一国产二国产三 | 91视频看看 | 最新av观看 | 吻胸摸激情床激烈视频大胸 | 妞干网精品 | 国产露脸国语对白在线 | 无码国模国产在线观看 | 帮我拍拍漫画全集免费观看 | 在线观看日韩 | 日韩一区二区三区中文字幕 | 欧美阿v| 老牛影视av老牛影视av | www日本com | 日韩欧美v | 一区二区av在线 | 农村妇女一区二区 | 好了av在线 | 怡红院最新网址 | 丰满熟妇人妻av无码区 | 日本黄色片免费 | 色乱码一区二区三区 | 亚洲精品乱码久久久久久久 | 日韩中文在线视频 | 亚洲一二三四在线 | 三级视频网址 | 91视频网址 | 豆花视频成人 | 日韩欧美一区二区区 | 国产乱淫av一区二区三区 | 91精品国产日韩91久久久久久 | 一级黄色片毛片 | 亚洲精品一区二区三区蜜桃 | 成人在线播放av | 欧美一级啪啪 | 国产三级国产精品国产专区50 | 国产精品无码专区av在线播放 | 91久久精品www人人做人人爽 | 轻点好疼好大好爽视频 | 国产又爽又黄的视频 | 久一在线 | 草草在线视频 | 日韩欧美亚洲一区二区三区 | 女人脱裤子让男人捅 | 久久精品小视频 | 一区二区日韩电影 | 91操碰| 蜜桃在线一区二区 | 打屁股av| 综合色88 | 国产一及片 | 欧美黑吊大战白妞欧美大片 | 人妻互换一区二区激情偷拍 | 精品视频亚洲 | 女女互慰吃奶互揉调教捆绑 | 97视频 | 处破痛哭a√18成年片免费 | 成年人黄色片网站 | 日本午夜视频在线观看 | 天降女子在线 | 在线免费观看一区二区三区 | 国产精品网友自拍 | 无码日韩人妻精品久久蜜桃 | 国产精品高潮呻吟久久aⅴ码 | 国产精九九网站漫画 | 国产剧情久久久 | 久久99精品久久只有精品 | 日韩的一区二区 | 免费亚洲网站 | 这里只有精品视频在线观看 | 日本黄色免费网站 | 在线超碰av| 黄色裸体片 | 国产福利一区二区三区在线观看 | 武侠古典av | 亚洲精品久久久中文字幕痴女 | 国产五区 | 黑人性xxx| 日韩专区在线播放 | 波多野结衣免费观看视频 | 跪求黄色网址 | 免费黄色欧美 | 激情小说在线观看 | 成人av免费在线播放 | 四色在线 | 亚洲精品中文字幕在线播放 | 国产大学生视频 | 亚洲视频1区 | 亚欧毛片 | 相亲对象是问题学生动漫免费观看 | 男生插女生视频在线观看 | 亚洲三级大片 | 超碰99热| 久久免费小视频 | 色综合狠狠爱 | 日本视频在线观看 | free性欧美hd另类 | 中文字幕一区二区三区在线视频 | 亚洲先锋影音 | 成人在线观看你懂的 |