ViewGroup的職能為:給childView計算出建議的寬和高和測量模式 ;決定childView的位置;為什么只是建議的寬和高,而不是直接確定呢,別忘了childView寬和高可以設置為wrap_content,這樣只有childView才能計算出自己的寬和高。
View的職責:根據測量模式和ViewGroup給出的建議的寬和高,計算出自己的寬和高;同時還有個更重要的職責是:在ViewGroup為其指定的區域內繪制自己的形態。
在自定義view中:
1、自定義View的屬性
通過attrs.xml 添加
1 <declare-styleable name="CustomTextView"> 2 <attr name="titleText" /> 3 <attr name="titleTextColor" /> 4 <attr name="titleTextSize" /> 5 </declare-styleable>
2、在View的構造方法中獲得我們自定義的屬性
通過Context.obtainStyledAttributes獲取xml屬性。
3、重寫onMesure
android提供MeasureSpec類幫助測量view。MeasureSpec是一個32位的int值,其中高兩位為測量模式,低30位為測量大小。
1、EXACTLY
精確值模式,當我們的控件的layout_width屬性和layout_height屬性指定為具體數值時,如android:layout_width="100dp"或者為match_parent時系統使用的是EXACTLY模式。
父控件可以通過MeasureSpec.getSize(measureSpec)
直接得到子控件的尺寸。
父View給自定義View確定了一個范圍,在這個范圍內,自定義view的大小是給出的具體的值,比如 width =100dp,height=200dp,但是如果給出的任何一個數值超過了父View的限制值,他最大是父View的限制值
2、AT_MOST
最大值模式,當控件layout_width屬性和layout_height屬性為wrap_content時,控件大小隨控件子控件或內容變化,此時控件的尺寸只要不超過父控件允許的最大尺寸即可。
這種模式下,父控件無法確定子 View 的尺寸,只能由子控件自己根據需求去計算自己的尺寸,這種模式就是我們自定義視圖需要實現測量邏輯的情況。
3、UNSPECIFIED
不指定大小測量模式。父視圖不對子視圖有任何約束,它可以達到所期望的任意尺寸,比如 ListView、ScrollView,一般自定義 View 中用不到。
View類默認的onMeasure()方法只支持EXACTLY模式,如果想讓控件支持wrap_content屬性就必須重寫onMeasure()方法
重寫onMeasure()方法后需調用setMeasuredDimension(int measuredWidth,int measuredHeight)
設置了WRAP_CONTENT時,我們需要自己進行測量,必須重寫onMeasure()方法
// 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; }
4、重寫onDraw
*********主要View的執行過程*********:
(1)構造方法 (2)onFinishInflate (3)onSizeChanged(4)onDraw
在自定義ViewGroup中:
1. onMeasure中計算childView的測量值以及模式,以及設置自己的寬和高
2. onLayout對其所有childView進行定位(設置childView的繪制區域)
通過getChildCount()獲取總子view,getChildAt獲取childview調用各自的layout(int, int, int, int)方法。
ViewGroup不會執行onDraw說明:
1)ViewGroup默認情況下,會被設置成WILL_NOT_DRAW,這是從性能考慮,這樣一來,onDraw就不會被調用了。
2)如果我們要重要一個ViweGroup的onDraw方法,有兩種方法:
1,在構造函數里面,給其設置一個顏色,如#00000000。
2,在構造函數里面,調用setWillNotDraw(false),去掉其WILL_NOT_DRAW flag。
注意,自定義的View在使用的時候一定要寫出完整的包名,不然系統將無法找到這個View。
xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"
Android中實現view的更新有兩組方法
一組是invalidate,另一組是postInvalidate,其中前者是在UI線程自身中使用,而后者在非UI線程中使用。
前面要利用Handler結合使用和利用postInvalidate()來實現在線程中刷新界面。
1,利用invalidate()刷新界面
實例化一個Handler對象,並重寫handleMessage方法調用invalidate()實現界面刷新;而在線程中通過sendMessage發送界面更新消息。
2,使用postInvalidate()刷新界面
使用postInvalidate則比較簡單,不需要handler,直接在線程中調用postInvalidate即可。 (*****源碼也是通過handler去執行*****)