淺談Android之Activity 視窗顯示流程介紹(一)
7 Activity 視窗顯示流程介紹
Activity 視窗顯示,其實就是Décor View繪製並顯示的過程,但是在繪製之前,Décor View需要先做下面兩件事情:
1) 確定Décor View的大小
2) 對Décor View進行佈局操作,也就是確定DécorView所有child views的顯示位置
由於Décor View的LayoutParams中寬高預設設定的是MatchParent,所以Décor View初始狀態下,肯定是想要塞滿整個螢幕的,也就是寬高都是螢幕的尺寸,但是想歸想,實際上真正能拿到多大的區域,這個還是要看WMS這個大總管怎麼分配了
簡單點說就是,Décor View可以告知WMS視窗想要顯示的大小,但是WMS會根據狀態列,輸入法視窗是否顯示等等,來對這個大小做適當的裁剪,然後將最終結果告知到Décor View,
我們可以稱這個過程叫Décor View顯示大小確認,這個是通過呼叫WindowSession. Relayout來完成的
ViewRootImpl. performTraversals來觸發完成的,接下去通過程式碼做簡單介紹
7.1 Activity視窗大小獲取
ViewRootImpl.performTraverals函式完整的程式碼太長了,這裡就不全部貼出,接下去分析時會貼出區域性程式碼片段來介紹
首先看ViewRootImpl定義的三個變數:
final Rect mWinFrame; // frame given by window manager. int mWidth; int mHeight; |
mWinFrame,mWidth和mHeight儲存的,都是WMS返回的視窗大小,如果是第一次執行(mFirst=True), 這三個值預設都為空,如果不是第一次執行,則為當前視窗的顯示大小
接下去設定Activity Window的desire rectangle:
Rect frame = mWinFrame; if (mFirst) { mFullRedrawNeeded = true; mLayoutRequested = true; if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) { // NOTE -- system code, won't try to do compat mode. …… } else { DisplayMetrics packageMetrics = mView.getContext().getResources().getDisplayMetrics(); desiredWindowWidth = packageMetrics.widthPixels; desiredWindowHeight = packageMetrics.heightPixels; } …… } else { desiredWindowWidth = frame.width(); desiredWindowHeight = frame.height(); if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) { mFullRedrawNeeded = true; mLayoutRequested = true; windowSizeMayChange = true; } } |
如果mFirst為true,將desiredWindowWidth和desiredWindowHeight設定為螢幕寬高,否則就將其設定為mWinFrame中儲存的當前視窗的大小值
接著基於desiredWindowWidth和desiredWindowHeight對Décor View做一次measure,確認Décor View的大小:
boolean layoutRequested = mLayoutRequested && !mStopped; if (layoutRequested) { final Resources res = mView.getContext().getResources(); …… // Ask host how big it wants to be windowSizeMayChange |= measureHierarchy(host, lp, res, desiredWindowWidth, desiredWindowHeight); } |
Décor View的大小確認後,這個只不過是Activity希望請求到的視窗大小,接著還需要呼叫relayoutWindow-> mWindowSession.relayout並傳入請求的大小,然後WMS會根據當前視窗情況返回最終分配的視窗大小,比如,現在有一個輸入法視窗已經彈出,並且Activity設定的windowSoftInputMode為adjustResize,在這種情況下,Activity期望的高度是肯定無法滿足的,WMS會返回減去輸入法視窗的高度,並將結果返回到Activity
接著看mWindowSession.relayout的呼叫:
int relayoutResult = mWindowSession.relayout( mWindow, mSeq, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, mPendingStableInsets, mPendingConfiguration, mSurface); |
mWindow是對應的視窗,第三和第四個引數就是視窗期望的寬和高,這裡加上0.5f用以實現四捨五入,WMS那邊調整後的寬高資料會返回到mWinFrame中
mWinFrame儲存的就是這次Activity視窗顯示的最終大小,接著儲存資料到mWidth和
mHeight:
if (mWidth != frame.width() || mHeight != frame.height()) { mWidth = frame.width(); mHeight = frame.height(); } |
接著重新設定ViewRootImpl關聯的Surface的frame size,以便調整graphic buffer size:
if (mSurfaceHolder != null) { // The app owns the surface; tell it about what is going on. if (mSurface.isValid()) { // XXX .copyFrom() doesn't work! //mSurfaceHolder.mSurface.copyFrom(mSurface); mSurfaceHolder.mSurface = mSurface; } mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight); } |
最後,如果WMS返回的最終視窗大小和之前通過desire Width和desire height測量出來的décor view的mesure width和height不一致,那décor view需要基於最終的視窗大小再次做測量操作:
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || contentInsetsChanged) { int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); …… // Ask host how big it wants to be performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); …… } |
Activity視窗大小的獲取介紹結束,接下去介紹Décor View是怎麼測量自身以及childviews.
7.2 Activity Décorview大小測量(measure)
上頭呼叫performMeasure觸發measure操作,對每個View的寬高來說,必須設定 Measure Mode,主要有三個mode:
Mode name |
Desc |
MeasureSpec.AT_MOST |
對應WRAP_CONTENT |
MeasureSpec.EXACTLY |
對應MATCH_PARENT |
MeasureSpec .UNSPECIFIED |
未定義 |
由於layout預設必須要配置View的寬高屬性,所以MeasureSpec .UNSPECIFIED基本是用不到的
如果要呼叫view.measure對某個view執行measure操作,必須要傳入寬高以及對應的measure mode值,android將寬/高的值以及measure mode合併儲存到int裡頭,具體的資料分佈是這樣的:
Int的0-30位儲存的是寬高的值,30-32位儲存measure mode
對此,Android專門定義了MeasureSpec輔助類用於將measuremode和value合併成int,或者從int裡頭取出value或者measure mode:
Method name |
Desc |
MeasureSpec. makeMeasureSpec |
基於value和measure mode合成measure spec |
MeasureSpec.getMode |
從measure spec取出高2位的值,也就是mode的值 |
MeasureSpec.getSize |
從measure spec中取出低30位的值,也是就寬/高的值 |
先來看下ViewRootImpl中getRootMeasureSpec的實現:
private static int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; } |
從函式中可以看出,MATCH_PARENT對應MeasureSpec.EXACTLY,WRAP_CONTENT則對應
MeasureSpec.AT_MOST,並且,不管是MeasureSpec.EXACTLY還是MeasureSpec.AT_MOST,其對應的value都是the max size of parentview.
接下去看performMeasure:
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } |
直接呼叫décor view的measure函式,接下去看viewmeasure的完整流程:
由於DecorView是一個FrameLayout,所以接下去專門分析下FrameLayout的measure程式碼,它先呼叫DecorView.measure,由於DecorView沒有實現這個函式,所以最終呼叫View.measure:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { …… // Suppress sign extension for the low bytes long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL; if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2); final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; final boolean isExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY; final boolean matchingSize = isExactly && getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec); if (forceLayout || !matchingSize && (widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec)) { // first clears the measured dimension flag mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; resolveRtlPropertiesIfNeeded(); int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = mMeasureCache.valueAt(cacheIndex); // Casting a long to int drops the high 32 bits, no mask needed setMeasuredDimensionRaw((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } …… mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension } |
mPrivateFlags變數基於位運算儲存各種標籤值,比如PFLAG_MEASURED_DIMENSION_SET,用於標識view是否已經measure;mOldWidthMeasureSpec和mOldHeightMeasureSpec儲存上一次請求完成的measure spec;forceLayout標明這次measure是否是由View.requestLayout觸發的,如果為true,則強制執行onMeasure
如果measure行為不是由View.requestLayout觸發的,也就是forceLayout為false,那只有如下兩種情況,會觸發View呼叫onMeasure做重新測量:
1) Measure mode不全是MeasureSpec.EXACTLY,也就是說,寬和高有一個或者全部被設定成WRAP_CONTENT,這種情況下,isExactly就會為false,還有就是widthMeasureSpec !=mOldWidthMeasureSpec ||
heightMeasureSpec!= mOldHeightMeasureSpec 為true,最後就是mMeasureCache不存在快取資料
2) Measure mode全是MeasureSpec.EXACTLY,也就是時候,寬和高都被設定成了
MATCH_PARENT,然後measuare spec裡設定的寬高跟getMeasuredWidth()和
getMeasuredHeight()的不一致,這個時候matchingSize肯定為false,
widthMeasureSpec!= mOldWidthMeasureSpec和
heightMeasureSpec!= mOldHeightMeasureSpec肯定也為true,最後同樣的,mMeasureCache不存在快取資料
總結下,如果反過來說呢,如果measure被重複呼叫,什麼情況下onMeasure不會被重複執行:
1) 不是通過requestLayout觸發的
2) 如果widht和height mode都是MATCH_PARENT,並且請求高度和寬度無變化,那這次請求就被會忽略
3) 如果width或者height存在一個mode為WRAP_CONTENT,那就要看請求measure spec是否是一致的,如果一致,那這次請求會被忽略
上面介紹的是onMeasure的入口控制,避免不必要的measure被重複執行進而影響系統執行效率,接下去介紹onMeasure的內部實現
我們都知道整個View是一個樹形結構,這個結構的頂端是Décor View,每一個樹節點只有兩種型別,一個是leaf view,還有就是ViewGroup,leaf view是末端節點,由於其不包含childview,其內部只處理自身邏輯就好了,相對來說會比較簡單;ViewGroup則不一樣,因為它是View的群組,那它肯定就包含child view,child view可能是leaf view或者View Group,相對來說就會複雜點。
所以,如果要從Décor View遍歷整個樹形資料,最重要就是ViewGroup的處理,因為它起到承上啟下的作用。
Android定義的ViewGroup有很多,比如RelativeLayout,LinearLayout,FrameLayout,每個ViewGroup的最大的不同之處,表現在對childview的measure,layout的處理
由於Décor View是一個FrameLayout,所以接下去重點介紹FrameLayout.onMeasure實現,其餘幾個ViewGroup大家可自行研究
由於FrameLayout所有的child view的位置都是相對於FrameLayout的左上角座標原點來放置的,如果FrameLayout定義的寬高為MATCH_PARENT,那所有child view所能達到的最大寬高就是FrameLayout的寬高,反之,如果FrameLayout的寬高定義為WRAP_CONTENT,那FrameLayout的寬高則會被設定成所有child view中size最大的那個。
接下去看程式碼:
//FrameLayout.java protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; mMatchParentChildren.clear(); int maxHeight = 0; int maxWidth = 0; int childState = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() != GONE) { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } } } // Account for padding too maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); // Check against our minimum height and width maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); // Check against our foreground's minimum height and width final Drawable drawable = getForeground(); if (drawable != null) { maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); } setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT)); count = mMatchParentChildren.size(); if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); int childWidthMeasureSpec; int childHeightMeasureSpec; if (lp.width == LayoutParams.MATCH_PARENT) { childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingLeftWithForeground() - getPaddingRightWithForeground() - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY); } else { childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, getPaddingLeftWithForeground() + getPaddingRightWithForeground() + lp.leftMargin + lp.rightMargin, lp.width); } if (lp.height == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTopWithForeground() - getPaddingBottomWithForeground() - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, getPaddingTopWithForeground() + getPaddingBottomWithForeground() +lp.topMargin + lp.bottomMargin, lp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } } |
measureMatchParentChildren如果為true,說明FrameLayout存在寬/高定義為
WRAP_CONTENT,對於同樣是WRAP_CONTENT的child view沒問題,但是對於那些
MATCH_PARENT的child view,則需要在FrameLayout的寬/高確定後,需要再一次執行measure操作
函式先迴圈遍歷all child views,然後呼叫measureChildWithMargins測量child view的寬高,然後通過view的measure width加上對應的margin size得到其在FrameLayout中需要佔用的實際大小,然後將allchild views中,寬度和高度最大的儲存到maxWidth和maxHeight中
如果measureMatchParentChildren為true,還需要將寬高為MATCH_PARENT的child view儲存到mMatchParentChildren中
接著將得到的maxWidth和maxHeight再加上FrameLayout的padding數值,然後再通過
getForeGround拿到fore ground drawable,如果drawable的最小寬高比maxWidth和maxHeight要大,則將其作為maxWidth和maxHeight的大小
maxWidth和maxHeight到目前為止,都是基於childview的資料加上frameLayout的padding和foreground資料算出的,這個可以做為frameLayout的measure result?當然不行,因為child view給出的資料,有可能會超過framelayout對應measure spec的值,所以還需要呼叫
resolveSizeAndState做調整:
//View.java public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { 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: if (specSize < size) { result = specSize | MEASURED_STATE_TOO_SMALL; } else { result = size; } break; case MeasureSpec.EXACTLY: result = specSize; break; } return result | (childMeasuredState&MEASURED_STATE_MASK); } |
這個函式很簡單,如果FrameLayout設定的是AT_MOST,即WRAP_CONTENT,如果size比specSize小,那就用size,反之則用specSize;如果FrameLayout設定的是EXACTLY,也就是MATCH_PARENT,則直接設定成specSize
也就是說maxWidth和maxHeight的值,只在寬高為WRAP_CONTENT並且maxWidth和
maxHeight值比對應measure spec的value值小時才有意義
接著呼叫setMeasuredDimension設定resolveSizeAndState返回的寬高結果
到這裡,FrameLayout的measure width/height確定後,對寬高為MATCH_PARENT的child view再一次執行measure操作,由於FrameLayout的measure width/height已定,這裡再得到child view可佔用的最大寬高就很簡單,直接減去FrameLayout的padding和child view的margin值就可以了。
接下去回過頭來分析measureChildWithMargins的實現,由於FrameLayout沒有實現該函式,所以呼叫ViewGroup中的實現:
//ViewGroup.java 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); } |
通過呼叫getChildMeasureSpec得到child view的measure spec,這個函式傳入是第一個引數是parent measure spec,第二個引數是padding,對應FrameLayout的padding值加上child view的margin值再加上已經使用的寬/高度值,由於FrameLayout所有child view都是基於左上角座標的,所以這裡widhtUsed和heightUsed都是0,最後一個引數是layout中設定的widht和height值
接著看getChildMeasureSpec的實現:
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 us case 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 us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = 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 be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); } |
這個函式先拿到FrameLayout的measure spec,然後再將measurespec的value減去padding儲存到size中,其即為child view的desire size
這個函式其實就處理兩種情況
1) 如果FrameLayout的measure spec為EXACTLY,接著判斷child view的width/height值;如果值大於0,說明其是一個確定的值,直接將其值儲存到resultSize並且將resultMode儲存為EXACTLY就可以了;如果值為MATCH_PARENT或者WRAP_CONTENT,則將result設定為size然後對應的值儲存到resultMode就好了
2) 如果FrameLayout的measure spec為AT_MOST,對於child view的width/height值大於0或者WRAP_CONTENT,處理跟上面都是一樣的;唯一不同的是,當值為MATCH_PARENT時,由於FrameLayout這個parent view在目前measure width/height都未確定,作為child view,你這裡MATCH_PARENT就沒任何意義,所以這裡會把result mode改成AT_MOST
函式中呼叫child.measure執行child view的measure操作,也就是本節介紹的內容,如此反覆,直到所有child view都measure完畢為止
相關推薦
淺談Android之Activity 視窗顯示流程介紹(一)
7 Activity 視窗顯示流程介紹 Activity 視窗顯示,其實就是Décor View繪製並顯示的過程,但是在繪製之前,Décor View需要先做下面兩件事情: 1) 確定Décor View的大小 2) 對Décor View進行佈局操作,也就是確定Déc
淺談Android之Activity 視窗顯示流程介紹(二)
7.3 Activity Décorview佈局(layout) Measure確定Décor View以及child views的大小,layout則是確定child view在其parent view中的顯示區域,只有layout結束,view的left,right,t
淺談Android之Activity Decor View建立流程介紹
6 Activity DecorView建立流程介紹 上頭已經完整的介紹了Activity的啟動流程,Activity是如何繫結Window,Window的décor view是如何通過ViewRootImpl與WMS建立關聯的,也就是說,整個框架已經有了,唯一缺的就是Ac
淺談Android之Activity觸控事件傳輸機制介紹
8 Activity觸控事件傳輸機制介紹 當我們觸控式螢幕幕的時候,程式會收到對應的觸控事件,這個事件是在app端去讀取的嗎?肯定不是,如果app能讀取,那會亂套的,所以app不會有這個許可權,系統按鍵的讀取以及分發都是通過WindowManagerService來完成
淺談Android之App視窗檢視管理
5 App視窗檢視管理 WindowManagerGlobal負責管理App所有要新增到WMS的視窗,介面即為上頭的addView 首先,對於App本地視窗來說,其最核心的資料無非就兩個,一個是Window Parameters,另一個就是視窗的DécorView,一個負
Android使用Font Awesome顯示小圖示(一)
Android中傳統的顯示圖示的方式 在平常的開發中,如果我們需要在介面上顯示某個小圖示,比如搜尋按鈕,返回按鈕,這時我們需要美工給我們切對應的png圖片,並放進對應的drawable資料夾中,這樣隨著圖示的越來越多,APK體積也會越來越大。 什麼是Fo
Android OTA升級原理和流程分析(一)
這篇及以後的篇幅將通過分析update.zip包在具體Android系統升級的過程,來理解Android系統中Recovery模式服務的工作原理。我們先從update.zip包的製作開始,然後是Android系統的啟動模式分析,Recovery工作原理,如何從
淺談Atlassian產品搭建的敏捷管理體系(一)
Dream big, work smart, deliver fast 使用Atlassian的產品已經有三年多,但是大部分主要以JIRA和Confluence為主,今年年初加入一創業團隊負責技術團隊的搭建,從零開始通過部署Atlassian產品、制
Android用surface直接顯示yuv資料(一)
轉自:http://blog.csdn.net/tung214/article/details/36887041 研究了一段時間Android的surface系統,一直執著地認為所有在surface或者螢幕上顯示的畫面,必須要轉換成RGB才能顯示,yuv資料也要通過顏色
詳解android之Animation監聽方法AnimationListener(一)
先寫一個類繼承AnimationListener,看看具體方法: 具體方法大家也已經從圖片中也有些瞭解了,那接下來就看看實戰中,又怎麼使用呢: 1. 先看看佈局檔案和效果圖:
C++效能系列之靜態程式碼檢查工具介紹(一)
FxCop Integrator允許將獨立的FxCop(1.36或10.0)和Code Metrics PowerTool 10.0整合到VS2010中。 最新版本(2.0.0 RTW)包含以下新功能: 支援使用程式碼度量PowerTool的計算程式碼
Android AdapterView及子類的介紹(一)
AdapterView是一組重要的元件,AdapterView本身是一個抽象基類,它派生的子類在用法上十分相似,只是顯示介面有一定的區別,因此把它們歸為一類,針對它們的共性集中講解,並突出介紹它們的區別。 AdapterView具有如下特徵: (一)AdapterView繼
淺談SpringMVC之架構與工作流程
MVC模式是在Java的Web應用開發中非常常用的模式。MVC全名是Model View Controller,是模型(model)-檢視(view)-控制器(controller)的縮寫,一種軟體設計典範,用一種業務邏輯、資料、介面顯示分離的方法組織程式碼,將
淺談Android之SurfaceFlinger相關介紹(三)
3.3 Surface Java層相關封裝 主要介紹三個類,對應如下: Java C++ SurfaceSession.java SurfaceComposeClient 對應JNI檔案為: android_view_surfacesession.cpp
Android 端天氣預報APP的實現(一)天氣顯示介面之上下滑動
最近參加了一個小比賽,選了天氣預報APP這個題目,最初選擇它,是想練練網路資料獲取和資料解析方面的知識,後來發現倒是學到了不少介面的東西。下面我來一步步講解一下我是怎麼完成的吧~ 首先,天氣預報最直觀的部分應該就是天氣顯示介面了,這裡,我想做成可以有上下滑動的
Android開發之實現滑動RecyclerView,浮動按鈕的顯示和隱藏(一)
本篇部落格,主要講解了滑動RecyclerView實現FloatingActionButton的顯示和隱藏的動畫。 -------------------------------分割線----------------------------- 效果圖展示: -------
淺談HTTP中Get與Post的區別(轉)
Http定義了與伺服器互動的不同方法,最基本的方法有4種,分別是GET,POST,PUT,DELETE。URL全稱是資源描述符,我們可以這樣認為:一個URL地址,它用於描述一個網路上的資源,而HTTP中的GET,POST,PUT,DELETE就對應著對這個
Android9.0 Activity啟動流程分析(一)
1、ActivityRecord、TaskRecord、ActivityStack和ActivityDisplay介紹 本篇文章是基於Android refs/tags/android-9.0.0_r8分支的程式碼進行分析的 在分析Activity啟動的原始碼之前先介紹一下Act
HBase原始碼分析之HRegion上compact流程分析(二)
2016年03月03日 21:38:04 辰辰爸的技術部落格 閱讀數:2767 版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/lipeng_bigdata/article/details/50791205
linux學習筆記之流程控制(一)if分支語句
流程控制 if語句 單分支if語句 程式 fi``` ```if [ 判斷 ] then 程式 fi``` 例項程式碼指令碼: #!/bin/bash