AndroidGroup的使用

轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/38339817 , 本文出自:【張鴻洋的博客】

1、ViewGroup的職責是啥?

public abstract class ViewGroup
extends Viewimplements ViewParent, ViewManager A ViewGroup is a special view that can contain other views (called children.) The view group is the base class for layouts and views containers. This class also defines the ViewGroup.LayoutParams class which serves as the base class for layouts parameters

ViewGroup相當於一個放置View的容器,並且我們在寫佈局xml的時候,會告訴容器(凡是以layout爲開頭的屬性,都是爲用於告訴容器的),我們的寬度(layout_width)、高度(layout_height)、對齊方式(layout_gravity)等;當然還有margin等;於是乎,ViewGroup的職能爲:給childView計算出建議的寬和高和測量模式 ;決定childView的位置;爲什麼只是建議的寬和高,而不是直接確定呢,別忘了childView寬和高可以設置爲wrap_content,這樣只有childView才能計算出自己的寬和高。

2、View的職責是啥?

View的職責,根據測量模式和ViewGroup給出的建議的寬和高,計算出自己的寬和高;同時還有個更重要的職責是:在ViewGroup爲其指定的區域內繪製自己的形態。

View 類是 text 包極爲重要的一部分。顧名思義,其表示文本模型的一個視圖或者文本模型的一部分。負責文本組件外觀的正是此類。View 無意成爲用戶必須學習的全新內容,相反其更像是一個輕量級組件。

在默認情況下,視圖是很輕量的。它包含一個對父視圖的引用,由此能獲取許多內容而無需保持狀態,它還包含一個對模型 (Element) 某部分的引用。視圖無需精確地表示模型中的元素,而只是使用一個典型而便利的映射。視圖可以選擇性地維護一對 Position 對象,從而維護其在模型中的位置(即表示一個元素段)。這通常是將視圖拆分爲片的格式化的結果。與元素堅固聯繫的便利之處在於使其更容易地構建工廠來生成視圖,還能使其在模型更改時更容易地跟蹤視圖片,並且視圖一定會更改視圖來反映該模型。因此,簡單的視圖直接表示一個 Element,而複雜的視圖並不如此。

3、ViewGroup和LayoutParams之間的關係?

大家可以回憶一下,當在LinearLayout中寫childView的時候,可以寫layout_gravity,layout_weight屬性;在RelativeLayout中的childView有layout_centerInParent屬性,卻沒有layout_gravity,layout_weight,這是爲什麼呢?這是因爲每個ViewGroup需要指定一個LayoutParams,用於確定支持childView支持哪些屬性,比如LinearLayout指定LinearLayout.LayoutParams等。如果大家去看LinearLayout的源碼,會發現其內部定義了LinearLayout.LayoutParams,在此類中,你可以發現weight和gravity的身影。

2、View的3種測量模式

上面提到了ViewGroup會爲childView指定測量模式,下面簡單介紹下三種測量模式:

EXACTLY:表示設置了精確的值,一般當childView設置其寬、高爲精確值、match_parent時,ViewGroup會將其設置爲EXACTLY;

AT_MOST:表示子佈局被限制在一個最大值內,一般當childView設置其寬、高爲wrap_content時,ViewGroup會將其設置爲AT_MOST;

UNSPECIFIED:表示子佈局想要多大就多大,一般出現在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此種模式比較少見。

注:上面的每一行都有一個一般,意思上述不是絕對的,對於childView的mode的設置還會和ViewGroup的測量mode有一定的關係;當然了,這是第一篇自定義ViewGroup,而且絕大部分情況都是上面的規則,所以爲了通俗易懂,暫不深入討論其他內容。

3、從API角度進行淺析

上面敘述了ViewGroup和View的職責,下面從API角度進行淺析。

View的根據ViewGroup傳人的測量值和模式,對自己寬高進行確定(onMeasure中完成),然後在onDraw中完成對自己的繪製。

ViewGroup需要給View傳入view的測量值和模式(onMeasure中完成),而且對於此ViewGroup的父佈局,自己也需要在onMeasure中完成對自己寬和高的確定。此外,需要在onLayout中完成對其childView的位置的指定。

4、完整的例子

需求:我們定義一個ViewGroup,內部可以傳入0到4個childView,分別依次顯示在左上角,右上角,左下角,右下角。

1、決定該ViewGroup的LayoutParams

對於我們這個例子,我們只需要ViewGroup能夠支持margin即可,那麼我們直接使用系統的MarginLayoutParams

  1. @Override  
  2.     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)  
  3.     {  
  4.         return new MarginLayoutParams(getContext(), attrs);  
  5.     }  

重寫父類的該方法,返回MarginLayoutParams的實例,這樣就爲我們的ViewGroup指定了其LayoutParams爲MarginLayoutParams。

2、onMeasure

在onMeasure中計算childView的測量值以及模式,以及設置自己的寬和高:

  1. /** 
  2.      * 計算所有ChildView的寬度和高度 然後根據ChildView的計算結果,設置自己的寬和高 
  3.      */  
  4.     @Override  
  5.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
  6.     {  
  7.         /** 
  8.          * 獲得此ViewGroup上級容器爲其推薦的寬和高,以及計算模式 
  9.          */  
  10.         int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  11.         int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  12.         int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);  
  13.         int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);  
  14.   
  15.   
  16.         // 計算出所有的childView的寬和高  
  17.         measureChildren(widthMeasureSpec, heightMeasureSpec);  
  18.         /** 
  19.          * 記錄如果是wrap_content是設置的寬和高 
  20.          */  
  21.         int width = 0;  
  22.         int height = 0;  
  23.   
  24.         int cCount = getChildCount();  
  25.   
  26.         int cWidth = 0;  
  27.         int cHeight = 0;  
  28.         MarginLayoutParams cParams = null;  
  29.   
  30.         // 用於計算左邊兩個childView的高度  
  31.         int lHeight = 0;  
  32.         // 用於計算右邊兩個childView的高度,最終高度取二者之間大值  
  33.         int rHeight = 0;  
  34.   
  35.         // 用於計算上邊兩個childView的寬度  
  36.         int tWidth = 0;  
  37.         // 用於計算下面兩個childiew的寬度,最終寬度取二者之間大值  
  38.         int bWidth = 0;  
  39.   
  40.         /** 
  41.          * 根據childView計算的出的寬和高,以及設置的margin計算容器的寬和高,主要用於容器是warp_content時 
  42.          */  
  43.         for (int i = 0; i < cCount; i++)  
  44.         {  
  45.             View childView = getChildAt(i);  
  46.             cWidth = childView.getMeasuredWidth();  
  47.             cHeight = childView.getMeasuredHeight();  
  48.             cParams = (MarginLayoutParams) childView.getLayoutParams();  
  49.   
  50.             // 上面兩個childView  
  51.             if (i == 0 || i == 1)  
  52.             {  
  53.                 tWidth += cWidth + cParams.leftMargin + cParams.rightMargin;  
  54.             }  
  55.   
  56.             if (i == 2 || i == 3)  
  57.             {  
  58.                 bWidth += cWidth + cParams.leftMargin + cParams.rightMargin;  
  59.             }  
  60.   
  61.             if (i == 0 || i == 2)  
  62.             {  
  63.                 lHeight += cHeight + cParams.topMargin + cParams.bottomMargin;  
  64.             }  
  65.   
  66.             if (i == 1 || i == 3)  
  67.             {  
  68.                 rHeight += cHeight + cParams.topMargin + cParams.bottomMargin;  
  69.             }  
  70.   
  71.         }  
  72.           
  73.         width = Math.max(tWidth, bWidth);  
  74.         height = Math.max(lHeight, rHeight);  
  75.   
  76.         /** 
  77.          * 如果是wrap_content設置爲我們計算的值 
  78.          * 否則:直接設置爲父容器計算的值 
  79.          */  
  80.         setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth  
  81.                 : width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight  
  82.                 : height);  
  83.     }  

10-14行,獲取該ViewGroup父容器爲其設置的計算模式和尺寸,大多情況下,只要不是wrap_content,父容器都能正確的計算其尺寸。所以我們自己需要計算如果設置爲wrap_content時的寬和高,如何計算呢?那就是通過其childView的寬和高來進行計算。

17行,通過ViewGroup的measureChildren方法爲其所有的孩子設置寬和高,此行執行完成後,childView的寬和高都已經正確的計算過了

43-71行,根據childView的寬和高,以及margin,計算ViewGroup在wrap_content時的寬和高。

80-82行,如果寬高屬性值爲wrap_content,則設置爲43-71行中計算的值,否則爲其父容器傳入的寬和高。

3、onLayout對其所有childView進行定位(設置childView的繪製區域)

  1. // abstract method in viewgroup  
  2.     @Override  
  3.     protected void onLayout(boolean changed, int l, int t, int r, int b)  
  4.     {  
  5.         int cCount = getChildCount();  
  6.         int cWidth = 0;  
  7.         int cHeight = 0;  
  8.         MarginLayoutParams cParams = null;  
  9.         /** 
  10.          * 遍歷所有childView根據其寬和高,以及margin進行佈局 
  11.          */  
  12.         for (int i = 0; i < cCount; i++)  
  13.         {  
  14.             View childView = getChildAt(i);  
  15.             cWidth = childView.getMeasuredWidth();  
  16.             cHeight = childView.getMeasuredHeight();  
  17.             cParams = (MarginLayoutParams) childView.getLayoutParams();  
  18.   
  19.             int cl = 0, ct = 0, cr = 0, cb = 0;  
  20.   
  21.             switch (i)  
  22.             {  
  23.             case 0:  
  24.                 cl = cParams.leftMargin;  
  25.                 ct = cParams.topMargin;  
  26.                 break;  
  27.             case 1:  
  28.                 cl = getWidth() - cWidth - cParams.leftMargin  
  29.                         - cParams.rightMargin;  
  30.                 ct = cParams.topMargin;  
  31.   
  32.                 break;  
  33.             case 2:  
  34.                 cl = cParams.leftMargin;  
  35.                 ct = getHeight() - cHeight - cParams.bottomMargin;  
  36.                 break;  
  37.             case 3:  
  38.                 cl = getWidth() - cWidth - cParams.leftMargin  
  39.                         - cParams.rightMargin;  
  40.                 ct = getHeight() - cHeight - cParams.bottomMargin;  
  41.                 break;  
  42.   
  43.             }  
  44.             cr = cl + cWidth;  
  45.             cb = cHeight + ct;  
  46.             childView.layout(cl, ct, cr, cb);  
  47.         }  
  48.   
  49.     }  

代碼比較容易懂:遍歷所有的childView,根據childView的寬和高以及margin,然後分別將0,1,2,3位置的childView依次設置到左上、右上、左下、右下的位置。

如果是第一個View(index=0) :則childView.layout(cl, ct, cr, cb); cl爲childView的leftMargin , ct 爲topMargin , cr 爲cl+ cWidth , cb爲 ct + cHeight

如果是第二個View(index=1) :則childView.layout(cl, ct, cr, cb); 

cl爲getWidth() - cWidth - cParams.leftMargin- cParams.rightMargin;

ct 爲topMargin , cr 爲cl+ cWidth , cb爲 ct + cHeight

剩下兩個類似~

這樣就完成了,我們的ViewGroup代碼的編寫,下面我們進行測試,分別設置寬高爲固定值,wrap_content,match_parent

5、測試結果

佈局1:

  1. <com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="200dp"  
  4.     android:layout_height="200dp"  
  5.     android:background="#AA333333" >  
  6.   
  7.     <TextView  
  8.         android:layout_width="50dp"  
  9.         android:layout_height="50dp"  
  10.         android:background="#FF4444"  
  11.         android:gravity="center"  
  12.         android:text="0"  
  13.         android:textColor="#FFFFFF"  
  14.         android:textSize="22sp"  
  15.         android:textStyle="bold" />  
  16.   
  17.     <TextView  
  18.         android:layout_width="50dp"  
  19.         android:layout_height="50dp"  
  20.         android:background="#00ff00"  
  21.         android:gravity="center"  
  22.         android:text="1"  
  23.         android:textColor="#FFFFFF"  
  24.         android:textSize="22sp"  
  25.         android:textStyle="bold" />  
  26.   
  27.     <TextView  
  28.         android:layout_width="50dp"  
  29.         android:layout_height="50dp"  
  30.         android:background="#ff0000"  
  31.         android:gravity="center"  
  32.         android:text="2"  
  33.         android:textColor="#FFFFFF"  
  34.         android:textSize="22sp"  
  35.         android:textStyle="bold" />  
  36.   
  37.     <TextView  
  38.         android:layout_width="50dp"  
  39.         android:layout_height="50dp"  
  40.         android:background="#0000ff"  
  41.         android:gravity="center"  
  42.         android:text="3"  
  43.         android:textColor="#FFFFFF"  
  44.         android:textSize="22sp"  
  45.         android:textStyle="bold" />  
  46.   
  47. </com.example.zhy_custom_viewgroup.CustomImgContainer>  

ViewGroup寬和高設置爲固定值

效果圖:



佈局2:

  1. <com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="wrap_content"  
  4.     android:layout_height="wrap_content"  
  5.     android:background="#AA333333" >  
  6.   
  7.     <TextView  
  8.         android:layout_width="150dp"  
  9.         android:layout_height="150dp"  
  10.         android:background="#E5ED05"  
  11.         android:gravity="center"  
  12.         android:text="0"  
  13.         android:textColor="#FFFFFF"  
  14.         android:textSize="22sp"  
  15.         android:textStyle="bold" />  
  16.   
  17.     <TextView  
  18.         android:layout_width="50dp"  
  19.         android:layout_height="50dp"  
  20.         android:background="#00ff00"  
  21.         android:gravity="center"  
  22.         android:text="1"  
  23.         android:textColor="#FFFFFF"  
  24.         android:textSize="22sp"  
  25.         android:textStyle="bold" />  
  26.   
  27.     <TextView  
  28.         android:layout_width="50dp"  
  29.         android:layout_height="50dp"  
  30.         android:background="#ff0000"  
  31.         android:gravity="center"  
  32.         android:text="2"  
  33.         android:textColor="#FFFFFF"  
  34.         android:textSize="22sp"  
  35.         android:textStyle="bold" />  
  36.   
  37.     <TextView  
  38.         android:layout_width="50dp"  
  39.         android:layout_height="50dp"  
  40.         android:background="#0000ff"  
  41.         android:gravity="center"  
  42.         android:text="3"  
  43.         android:textColor="#FFFFFF"  
  44.         android:textSize="22sp"  
  45.         android:textStyle="bold" />  
  46.   
  47. </com.example.zhy_custom_viewgroup.CustomImgContainer>  
ViewGroup的寬和高設置爲wrap_content
效果圖:



佈局3:

  1. <com.example.zhy_custom_viewgroup.CustomImgContainer xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:background="#AA333333" >  
  6.   
  7.     <TextView  
  8.         android:layout_width="150dp"  
  9.         android:layout_height="150dp"  
  10.         android:background="#E5ED05"  
  11.         android:gravity="center"  
  12.         android:text="0"  
  13.         android:textColor="#FFFFFF"  
  14.         android:textSize="22sp"  
  15.         android:textStyle="bold" />  
  16.   
  17.     <TextView  
  18.         android:layout_width="50dp"  
  19.         android:layout_height="50dp"  
  20.         android:background="#00ff00"  
  21.         android:gravity="center"  
  22.         android:text="1"  
  23.         android:textColor="#FFFFFF"  
  24.         android:textSize="22sp"  
  25.         android:textStyle="bold" />  
  26.   
  27.     <TextView  
  28.         android:layout_width="50dp"  
  29.         android:layout_height="50dp"  
  30.         android:background="#ff0000"  
  31.         android:gravity="center"  
  32.         android:text="2"  
  33.         android:textColor="#FFFFFF"  
  34.         android:textSize="22sp"  
  35.         android:textStyle="bold" />  
  36.   
  37.     <TextView  
  38.         android:layout_width="150dp"  
  39.         android:layout_height="150dp"  
  40.         android:background="#0000ff"  
  41.         android:gravity="center"  
  42.         android:text="3"  
  43.         android:textColor="#FFFFFF"  
  44.         android:textSize="22sp"  
  45.         android:textStyle="bold" />  
  46.   
  47. </com.example.zhy_custom_viewgroup.CustomImgContainer>  

ViewGroup的寬和高設置爲match_parent





發佈了16 篇原創文章 · 獲贊 8 · 訪問量 38萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章