首页 > 代码库 > HackThree

HackThree

创建自定义ViewGroup

?一,概要:
    使用自定义View 和ViewGroup组织应用程序布局是一个好方法,定制组件的同时允许开发者提供自定义行为和功能,以后,开发者
在需要创建复杂布局的视乎,首先应该考虑使用自定义ViewGroup是不是更适合,虽然在开始时,这样做会增加一定的工作量,但是,这是值得的
?二,基本知识点:
    要想绘制自定义ViewGroup要涉及到两个方法:
     measure(int , int):该方法从上到下遍历视图树。在递归遍历过程中,每个视图都会从下层传递尺寸和规格。当measure方法遍历结束,每个视图都保存了各自的尺寸信息。    
    layout(int, int, int, int):该方法也是从上而下遍历视图树,在遍历过程中,每个父视图通过测量过程的结果定位所有子视图的位置信息。
  思路整理:
  1. 要设置每个子View的横向偏移和竖向偏移普通的android属性中没有,所以需要自定义属性来获取值后来进行设置
  2. 通过复写ViewGroup中的onMeasure(int,int)方法测量Viewgroup和子View的大小,从而利用onLayout(int,int,int,int)来放置到具体区域    
三.效果图:
  
四.代码:
  1)自定义属性,在res/values中没有attr.xml文件就新建一个
 1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3  4     <declare-styleable name="myrectangle"> 5         <attr name="horizontal_distance" format="dimension" /> 6         <attr name="verticle_distance" format="dimension" /> 7     </declare-styleable> 8     <declare-styleable name="MyLayoutParams"> 9         <attr name="layout_verticle_distance" format="dimension" />10     </declare-styleable>11 12 </resources>

  2)设置这些属性未指定时的默认值大小,在dimens.xml中添加:

1  <dimen name="myRectangle_horizontal_distance">10dp</dimen>2  <dimen name="myRectangle_verticle_distance">10dp</dimen>

  3)自定义ViewGroup

  1 public class MyViewGoup extends ViewGroup {  2   3     private int mHorzntialDistance;  4   5     private int mVerticalDIstance;  6   7     public MyViewGoup(Context context) {  8         super(context);  9     } 10  11     public MyViewGoup(Context context, AttributeSet attrs) { 12         super(context, attrs); 13         /** 14          * 获取自定义属性值 15          */ 16         TypedArray a = context.obtainStyledAttributes(attrs, 17                 R.styleable.myrectangle); 18         try { 19  20             mHorzntialDistance = a.getDimensionPixelSize( 21                     R.styleable.myrectangle_horizontal_distance, 22                     getResources().getDimensionPixelSize( 23                             R.dimen.myRectangle_horizontal_distance)); 24             mVerticalDIstance = a.getDimensionPixelSize( 25                     R.styleable.myrectangle_verticle_distance, 26                     getResources().getDimensionPixelSize( 27                             R.dimen.myRectangle_verticle_distance)); 28         } finally { 29             a.recycle(); 30         } 31     } 32  33     public MyViewGoup(Context context, AttributeSet attrs, int defStyle) { 34         super(context, attrs, defStyle); 35     } 36  37     @Override 38     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 39         super.onMeasure(widthMeasureSpec, heightMeasureSpec); 40         int width = getPaddingLeft(); 41         int height = getPaddingTop(); 42         /** 43          * 测量子view的大小 44          */ 45         int childrenCount = getChildCount(); 46         int verticleDistance; 47         for (int i = 0; i < childrenCount; i++) { 48             View child = getChildAt(i); 49             measureChild(child, widthMeasureSpec, heightMeasureSpec); 50             /** 51              * 子View设置了自定义属性值就lp中verticleDistance就会获取到值 52              */ 53             LayoutParams lp = (LayoutParams) child.getLayoutParams(); 54             width = getPaddingLeft() + mHorzntialDistance * i; 55             lp.x = width; 56             lp.y = height; 57             verticleDistance = mVerticalDIstance; 58  59             if (lp.verticleDistance > 0) { 60                 verticleDistance += lp.verticleDistance; 61             } 62             width += child.getMeasuredWidth(); 63             height += verticleDistance; 64         } 65         /** 66          * 给ViewGroup设置大小. /** 因为设计者可以通过调用setMeasuredDimension决定视图的最终大小 67          * ,例如调用setMeasuredDimension 68          * (100,100)将视图的mMeasuredWidth和mMeasuredHeight设置为100 69          * ,100,那么父视图提供的大小以及程序员在xml中设置的layout_width和layout_height将完全不起作用 70          * ,当然良好的设计一般会根据子视图的measureSpec来设置mMeasuredWidth和mMeasuredHeight的大小 71          */ 72         width += getPaddingRight(); 73         height += getChildAt(getChildCount() - 1).getMeasuredHeight() 74                 + getPaddingBottom(); 75  76         if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) { 77             System.out.println("是AT_MOST,AT_MOST,AT_MOSTAT_MOSTAT_MOSTAT_MOST"); 78         } 79  80         /** 81          *     resolveSize的源码 82          *   public static int resolveSize(int size, int measureSpec) { 83              int result = size; 84              int specMode = MeasureSpec.getMode(measureSpec); 85              int specSize =  MeasureSpec.getSize(measureSpec); 86              switch (specMode) { 87              case MeasureSpec.UNSPECIFIED: 88                  result = size; 89                  break; 90              case MeasureSpec.AT_MOST: 91                  result = Math.min(size, specSize); 92                  break; 93              case MeasureSpec.EXACTLY: 94                  result = specSize; 95                  break; 96              } 97              return result; 98          } 99              所以不管怎么谁知其实都是屏幕的大小,这widthMeasureSpec的mode类型是EXACTLY100          */101         setMeasuredDimension(resolveSize(width, widthMeasureSpec),102                 resolveSize(height, heightMeasureSpec));103     }104 105     @Override106     protected void onLayout(boolean changed, int l, int t, int r, int b) {107 108         /**109          * 给子View设置位置(其实感觉是指定一块区域)110          */111 112         int childrenCount = getChildCount();113         for (int i = 0; i < childrenCount; i++) {114             View child = getChildAt(i);115             LayoutParams lp = (LayoutParams) child.getLayoutParams();116             child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y117                     + child.getMeasuredHeight());118 119         }120 121     }122 123     @Override124     protected boolean checkLayoutParams(android.view.ViewGroup.LayoutParams p) {125         return p instanceof LayoutParams;126     }127 128     @Override129     protected LayoutParams generateDefaultLayoutParams() {130         return new LayoutParams(LayoutParams.WRAP_CONTENT,131                 LayoutParams.WRAP_CONTENT);132     }133 134     @Override135     public LayoutParams generateLayoutParams(AttributeSet attrs) {136         return new LayoutParams(getContext(), attrs);137     }138 139     @Override140     protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {141         return new LayoutParams(p.width, p.height);142     }143 144     private class LayoutParams extends ViewGroup.LayoutParams {145 146         int x;147         int y;148         public int verticleDistance;149 150         public LayoutParams(Context context, AttributeSet attrs) {151             super(context, attrs);152 153             TypedArray a = context.obtainStyledAttributes(attrs,154                     R.styleable.MyLayoutParams);155             try {156                 /**157                  * 获取子View设置的竖直偏移量158                  */159                 verticleDistance = a160                         .getDimensionPixelSize(161                                 R.styleable.MyLayoutParams_layout_verticle_distance,162                                 -1);163             } finally {164                 a.recycle();165             }166 167         }168 169         public LayoutParams(int x, int y) {170             super(x, y);171         }172 173         public LayoutParams(android.view.ViewGroup.LayoutParams arg0) {174             super(arg0);175         }176 177     }178 179 }

  4)activity_main.xml

  

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2     xmlns:tools="http://schemas.android.com/tools" 3     xmlns:myrectangle="http://schemas.android.com/apk/res/com.example.myviewgroup" 4     android:layout_width="match_parent" 5     android:layout_height="match_parent" 6     tools:context=".MainActivity" > 7  8     <com.example.myviewgroup.view.MyViewGoup 9         android:layout_width="match_parent"10         android:layout_height="match_parent"11         android:background="#FF00FF"12         myrectangle:horizontal_distance="70dp"13         myrectangle:verticle_distance="50dp" >14 15         <View16             android:layout_width="100dp"17             android:layout_height="150dp"18             android:background="#FF0000" />19 20         <View21             android:layout_width="100dp"22             android:layout_height="150dp"23             android:background="#00FF00" />24 25         <View26             android:layout_width="100dp"27             android:layout_height="150dp"28             android:background="#0000FF" />29     </com.example.myviewgroup.view.MyViewGoup>30 31 </LinearLayout>

 

  
 

HackThree