編輯:關於Android編程
在搜集Android view繪制流程的相關知識時,發現這裡面的流程還是有些復雜的,准備了好幾天,才敢提起筆來。下面就直入主題吧!
view繪制流程是從ViewRoot的performTraversals()方法中開始的,在該方法中會執行view繪制的三部曲,即:measure(測量視圖的大小),layout(確定視圖的位置)draw(繪制視圖的內容)。下面這張圖明確的展示了該過程:
1、measure的過程
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ... onMeasure(widthMeasureSpec, heightMeasureSpec); ... }可以看到該方法是final的,所以不需要子類重寫,裡面的實現主要就是調用了onMeasure。那麼傳入的兩個參數是什麼呢?那就涉及到MeasureSpec了,MeasureSpec由specMode(規格)和specSize(大小)組成,規格有三種,它跟大小對應關系如下:
private int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }這個函數傳入的參數是窗口大小和MATCH_PARENT,這就是為什麼根視圖總是鋪滿屏幕的原因。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }onMeasure裡面主要是使用setMeasuredDimension來設置視圖的大小,這樣就完成了一次measure的過程,當然,一個布局中一般都會包含多個子視圖,每個子視圖都需要經歷一次measure過程。
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { final int size = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); } } }裡面循環調用了measureChild,其實現為:
protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { ... child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }這裡面又調用到了view的measure方法,所以這其實是個遞歸調用,不斷的去測量設置子視圖的大小,直至全部測完。
public void layout(int l, int t, int r, int b) { ... setFrame(l, t, r, b); ... onLayout(changed, l, t, r, b); ... }主要是調用了setFrame(用來設置坐標)和onLayout方法,View裡面OnLayout是空實現,因為onLayout()過程是為了確定視圖在布局中的位置,而這個操作應該是由布局來完成的,即父視圖決定子視圖的顯示位置。而ViewGroup裡面的是抽象方法,也就是需要其子類去實現。
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (mOrientation == VERTICAL) { layoutVertical(l, t, r, b); } else { layoutHorizontal(l, t, r, b); } } void layoutVertical(int left, int top, int right, int bottom) { ... for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); if (child == null) { childTop += measureNullChild(i); } else if (child.getVisibility() != GONE) {// final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); ... setChildFrame(child, childLeft, childTop + getLocationOffset(child), childWidth, childHeight); } } } private void setChildFrame(View child, int left, int top, int width, int height) { child.layout(left, top, left + width, top + height); }可以看到其實是遍歷子view,然後又去調用layout,這樣就不停的循環,直到遍歷完所有子view。由於view過程調用了setFrame方法,可以設置視圖的位置,就跟measure的功能重合了,所以這裡設置的話有可能會使之前measure的計算失效。
public void draw(Canvas canvas) { ... // Step 1, draw the background, if needed int saveCount; if (!dirtyOpaque) { final Drawable background = mBackground; if (background != null) { final int scrollX = mScrollX; final int scrollY = mScrollY; if (mBackgroundSizeChanged) { background.setBounds(0, 0, mRight - mLeft, mBottom - mTop); mBackgroundSizeChanged = false; } if ((scrollX | scrollY) == 0) { ckground.draw(canvas); } else { canvas.translate(scrollX, scrollY); background.draw(canvas); canvas.translate(-scrollX, -scrollY); } } } ... // Step 3, draw the content if (!dirtyOpaque) onDraw(canvas); ... // Step 4, draw the children dispatchDraw(canvas); ... // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); return; }這其中最主要的是調用了onDraw和dispatchDraw方法。onDraw是一個空方法,需要子view自己去實現,而ViewGroup的dispatchDraw()方法主要是遍歷子view,然後調用drawChild方法,而drawChild又是調用的draw方法,這樣就又構成了一個循環調用。 總結一下: 1、這三個過程都是從上而下,從父到子的,即:先設置父視圖,然後遍歷子視圖,並對其設置。 2、自定義view時,我們可以重寫onMeasure(非必須)和onDraw方法,在onMeasure的實現裡調用setMeasuredDimension或者super.onMeasure來設置視圖大小。 3、自定義ViewGroup時,我們可以重寫onLayout(必須)方法,在裡面調用view的layout方法設置視圖的位置。
字符串操作JNI把Java字符串當成引用類型來處理,JNI提供了java字符串與C字符串之間相互轉換的必要函數。因為java字符串對象是不可變的,因此JNI不提供任何修改
簡介這個輪子是對RecyclerView的封裝,主要完成了下拉刷新、上拉加載更多、RecyclerView頭部。在我的Material Design學習項目中使用到了項目
Android基礎入門教程——8.3.15 Paint API之——Typeface(字型)標簽(空格分隔): Andro
有時候關閉了手機qq還是能收到信息,手機qq如何完全退出呢?下面我們就一起來看看吧! 手機QQ推出登陸教程方法一、退出QQ程序 第一步:打開手機QQ 第二步