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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

绘制机制

發布時間:2024/4/13 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 绘制机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

繪制流程

  • ViewRootImpl.performTraversals 起點
  • Measure 決定view大小
  • Layout 決定view的位置
  • Draw 繪制

performTraversals

依次執行measure,layout,draw

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");try {mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}} private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {...host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());...} private void performDraw() {...} private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,boolean scalingRequired, Rect dirty) {...}

measure

父容器傳遞measureSpec至子容器。子容器根據measureSpec,并結合自身的layoutParams,測量出大小。父容器最后根據子容器的測量值,再測量出自身規格.

  • 元素visibility為GONE的不參與measure
  • 當viewGroup的子類的屬性有margin或padding時,子類會執行viewGroup中的measureChildWithMargins,最終測量的大小會specSize - padding。
  • View中的源碼
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));} //獲取的是android:minHeight屬性的值或者View背景圖片的大小 protected int getSuggestedMinimumHeight() {return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());} //獲取默認的大小,size為自身大小,measureSpec為父元素傳下來的,可見一個view的大小是由父元素的measureSpec和view的layoutParams共通決定 public static int getDefaultSize(int size, int measureSpec) {int result = size;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) {case MeasureSpec.UNSPECIFIED:result = size;break;case MeasureSpec.AT_MOST:case MeasureSpec.EXACTLY:result = specSize;break;}return result;}
  • 其它view的子類有各自的實現方式,不是直接拿measurespec和layoutParams進行測量
  • ViewGroup中沒有實現onMeasure,都由ViewGroup中的子類進行實現,如FrameLayout等,ViewGroup的子類在onMeasure過程中,遞歸所有子view,執行measureChildWithMargins、measure、onMeasure。測量完所有子View后,再通過用view中的setMeasuredDimension進行測量。

measureChildWithMargins

  • 如果view有固定值,則view的measureSpec的mode為EXACTLY,size為該固定值。
  • 在父元素為EXACTLY的情況下。子元素若為match_parent,則子元素的measureSpec,mode則為EXACTLY,size為父元素size。子元素若為wrap_content,則子元素的measureSpec,mode為AT_MOST,size為Math.max(0, specSize - padding)
  • 在父元素為AT_MOST的情況下。子元素無論layoutParams是match_parent還是wrap_content,子元素的measureSpec,mode都為AT_MOST,size都為Math.max(0, specSize - padding)
  • 在父元素為UNSPECIFIED時,子元素無論是match還是wrap,子元素的measureSpec,mode為UNSPECIFIED,size為View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed) {final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin+ widthUsed, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin+ heightUsed, lp.height);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);} public static int getChildMeasureSpec(int spec, int padding, int childDimension) {int specMode = MeasureSpec.getMode(spec);int specSize = MeasureSpec.getSize(spec);int size = Math.max(0, specSize - padding);int resultSize = 0;int resultMode = 0;switch (specMode) {// Parent has imposed an exact size on uscase MeasureSpec.EXACTLY:if (childDimension >= 0) {resultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size. So be it.resultSize = size;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent has imposed a maximum size on uscase MeasureSpec.AT_MOST:if (childDimension >= 0) {// Child wants a specific size... so be itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size, but our size is not fixed.// Constrain child to not be bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent asked to see how big we want to becase MeasureSpec.UNSPECIFIED:if (childDimension >= 0) {// Child wants a specific size... let him have itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size... find out how big it should// beresultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;resultMode = MeasureSpec.UNSPECIFIED;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size.... find out how// big it should beresultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;resultMode = MeasureSpec.UNSPECIFIED;}break;}//noinspection ResourceTypereturn MeasureSpec.makeMeasureSpec(resultSize, resultMode);}

layout

  • 最先執行的是viewGroup中的layout,判斷當前有沒有正在執行的layoutTransition,沒有就調用view中的layout,否則,就等transition執行完后,繼續調用。
  • view的layout中,有兩種設置view位置的方法,一種是setOpticalFrame,另外一種是setFrame。調用哪個,取決于父對象是不是viewGroup,且是否可見,若全是,則調用setOpticalFrame,getOpticalInsets則是獲取drawable的insets。
private boolean setOpticalFrame(int left, int top, int right, int bottom) {Insets parentInsets = mParent instanceof View ?((View) mParent).getOpticalInsets() : Insets.NONE;Insets childInsets = getOpticalInsets();return setFrame(left + parentInsets.left - childInsets.left,top + parentInsets.top - childInsets.top,right + parentInsets.left + childInsets.right,bottom + parentInsets.top + childInsets.bottom);}
  • setFrame結束后,還會執行OnLayoutChangeListener.onLayoutChange函數,若需要監聽,layout的變化,可addOnLayoutChangeListener。
  • layout的四個參數,默認是建立在measure的width和height基礎上的,當也可以重寫。并不依賴measure。如果重寫ltrb,則getMeasuredWidth和getWidth取得的值不一致。

draw

ViewGroup中并沒有重寫draw,則執行view中draw

  • 如果有設置背景,則繪制背景 Draw the background
  • 保存canvas層 If necessary, save the canvas' layers to prepare for fading
  • 繪制自身內容 Draw view's content
  • 如果有子元素則繪制子元素 Draw children
  • 繪制效果 If necessary, draw the fading edges and restore layers
  • 繪制裝飾品(scrollbars) Draw decorations (scrollbars for instance)
    • canvas對象由ViewRootImpl的drawSoftware建立,傳遞至view中。
    • 每當調用invalite時,都會將之前view區域設為dirty
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,boolean scalingRequired, Rect dirty) {...canvas = mSurface.lockCanvas(dirty);...} public Canvas lockCanvas(Rect inOutDirty)throws Surface.OutOfResourcesException, IllegalArgumentException {synchronized (mLock) {checkNotReleasedLocked();if (mLockedObject != 0) {// Ideally, nativeLockCanvas() would throw in this situation and prevent the// double-lock, but that won't happen if mNativeObject was updated. We can't// abandon the old mLockedObject because it might still be in use, so instead// we just refuse to re-lock the Surface.throw new IllegalArgumentException("Surface was already locked");}mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);return mCanvas;}}

    surfaceView

    measureSpec

    • 一個measureSpec包含了父容器傳遞給子容器的布局要求。
    • 包含了mode與size的組合int值,高兩位為mode,后面是size。
    public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,@MeasureSpecMode int mode) {if (sUseBrokenMakeMeasureSpec) {return size + mode;} else {return (size & ~MODE_MASK) | (mode & MODE_MASK);}}
    • 三種模式
      • UPSPECIFIED : 父容器對于子容器沒有任何限制,子容器想要多大就多大
      • EXACTLY: 父容器已經為子容器設置了尺寸,子容器應當服從這些邊界,不論子容器想要多大的空間。
      • AT_MOST:子容器可以是聲明大小內的任意大小

    轉載于:https://www.cnblogs.com/qinhe/p/6370858.html

    總結

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

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