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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android自定义控件_View的绘制流程

發布時間:2025/6/17 Android 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android自定义控件_View的绘制流程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

每一個View/ViewGroup的顯示都會經過三個過程:
1、measure過程(測量View顯示的大小,位置);
2、layout過程(布局view的位置);
3、draw過程(上一篇文章說到的通過canvas繪制到界面上顯示,形成了各色的View)

下面分析一下各個過程:
measure過程:

因為DecorView實際上是派生自FrameLayout的類,也即一個ViewGroup實例,該ViewGroup內部的ContentViews又是一個ViewGroup實例,依次內嵌View或ViewGroup形成一個View樹。所以measure函數的作用是為整個View樹計算實際的大小,設置每個View對象的布局大小(“窗口”大小)。實際對應屬性就是View中的mMeasuredHeight(高)和mMeasureWidth(寬)。
View在Measure過程中,最主要的三個方法:
public final void measure(int widthMeasureSpec, int heightMeasureSpec)
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight)
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight)。(此方法在onMesare方法里面調用,如果沒有調用需要手動調用)
前面兩個函數都是final類型的,不能重載,為此在ViewGroup派生的非抽象類中我們必須重載onMeasure函數,實現measure的原理是:假如View還有子View,則measure子View,直到所有的子View完成measure操作之后,再measure自己。ViewGroup中提供的measureChild或measureChildWithMargins就是實現這個功能的。各個View/ViewGroup最終調用setMeasuredDimension方法,設置View的大小,如果沒有調用它,測量是不起作用的。

在具體介紹測量原理之前還是先了解些基礎知識,即measure函數的參數由類measureSpec的makeMeasureSpec函數方法生成的一個32位整數,該整數的高兩位表示模式(Mode),低30位則是具體的尺寸大小(specSize)。
MeasureSpec有三種模式分別是UNSPECIFIED, EXACTLY和AT_MOST,各表示的意義如下:
如果是AT_MOST,specSize代表的是最大可獲得的尺寸;
如果是EXACTLY,specSize代表的是精確的尺寸;
如果是UNSPECIFIED,對于控件尺寸來說,沒有任何參考意義。
那么對于一個View的上述Mode和specSize值默認是怎么獲取的呢,他們是根據View的LayoutParams參數來獲取的:
參數為fill_parent/match_parent時,Mode為EXACTLY,specSize為剩余的所有空間;
參數為具體的數值,比如像素值(px或dp),Mode為EXACTLY,specSize為傳入的值;
參數為wrap_content,Mode為AT_MOST,specSize運行時決定

具體測量原理

上面提供的Mode和specSize只是程序對View的一個期望尺寸,最終一個View對象能從父視圖得到多大的允許尺寸則由子視圖期望尺寸和父視圖能力尺寸(可提供的尺寸)兩方面決定。關于期望尺寸的設定,可以通過在布局資源文件中定義的android:layout_width和android:layout_height來設定,也可以通過代碼在addView函數調用時傳入的LayoutParams參數來設定。父View的能力尺寸歸根到最后就是DecorView尺寸,這個尺寸是全屏,由手機的分辨率決定。期望尺寸、能力尺寸和最終允許尺寸的關系,我們可以通過閱讀measureChild或measureChildWithMargins都會調用的getChildMeasureSpec函數的源碼來獲得。
根據源碼View的OnMeasure函數調用的getDefaultSize函數獲知,默認情況下,控件都有一個最小尺寸,該值可以通過設置android:minHeight和android:minWidth來設置(無設置時缺省為0);在設置了背景的情況下,背景drawable的最小尺寸與前面設置的最小尺寸比較,兩者取大者,作為控件的最小尺寸。在UNSPECIFIED情況下就選用這個最小尺寸,其它情況則根據允許尺寸來。

layout過程:
上述measure過程達到的結果是設定了視圖的高和寬,layout過程的作用就是設定視圖在父視圖中的四個點(分別對應View四個成員變量mLeft,mTop,mRight,mBottom)。同樣layout也是被fianl修飾符限定為不能重載,不過在ViewGroup中onLayout函數被abstract修飾,即所有派生自ViewGroup的類必須實現onLayout函數,從而實現對其包含的所有子視圖的布局設定。

我們來看任意一個Layout布局,此就用LinearLayout為例,onLayout(layoutVertical方法)方法部分源碼:

void layoutVertical() {final int paddingLeft = mPaddingLeft;int childTop;int childLeft;// Where right end of child should gofinal int width = mRight - mLeft;int childRight = width - mPaddingRight;// Space available for childint childSpace = width - paddingLeft - mPaddingRight;final int count = getVirtualChildCount();final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;switch (majorGravity) {case Gravity.BOTTOM:// mTotalLength contains the padding alreadychildTop = mPaddingTop + mBottom - mTop - mTotalLength;break;// mTotalLength contains the padding alreadycase Gravity.CENTER_VERTICAL:childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2;break;case Gravity.TOP:default:childTop = mPaddingTop;break;}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();final LinearLayout.LayoutParams lp =(LinearLayout.LayoutParams) child.getLayoutParams();int gravity = lp.gravity;if (gravity < 0) {gravity = minorGravity;}final int layoutDirection = getResolvedLayoutDirection();final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {case Gravity.CENTER_HORIZONTAL:childLeft = paddingLeft + ((childSpace - childWidth) / 2)+ lp.leftMargin - lp.rightMargin;break;case Gravity.RIGHT:childLeft = childRight - childWidth - lp.rightMargin;break;case Gravity.LEFT:default:childLeft = paddingLeft + lp.leftMargin;break;}if (hasDividerBeforeChildAt(i)) {childTop += mDividerHeight;}childTop += lp.topMargin;setChildFrame(child, childLeft, childTop + getLocationOffset(child),childWidth, childHeight);childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);i += getChildrenSkipCount(child, i);}}} View Code

從代碼顯然可知具體layout布局時,就是根據measure過程設置的高和寬,結合視圖在父視圖中的起始位置,再外加視圖的layoutgravity屬性來設置四個點的具體位置(在LinearLayout中還會增加對layoutweight屬性的考慮)。這個過程相對沒有measure那么復雜。我們在寫控件的時候,我們可以根據自己的需要利用measure計算的結果來控制各個控件的位置,實現各種絢麗的動畫交互。

這是我在項目中寫的一個利用measure+layout寫的一個LineLayout,實現顯示一行,左邊顯示文字,右邊顯示Icon;當文字照過一屏的時候,自動截取打省略號,右邊Icon需要一直顯示;當然還有諸多缺陷與待改進的地方,期望指出與改進。

http://download.csdn.net/detail/huangbiao86/7521721

?

轉載于:https://www.cnblogs.com/PDW-Android/p/3796748.html

總結

以上是生活随笔為你收集整理的Android自定义控件_View的绘制流程的全部內容,希望文章能夠幫你解決所遇到的問題。

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