android 自定義 view 和 ViewGroup


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去執行*****)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM