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类型是EXACTLY
100          */
101         setMeasuredDimension(resolveSize(width, widthMeasureSpec),
102                 resolveSize(height, heightMeasureSpec));
103     }
104 
105     @Override
106     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.y
117                     + child.getMeasuredHeight());
118 
119         }
120 
121     }
122 
123     @Override
124     protected boolean checkLayoutParams(android.view.ViewGroup.LayoutParams p) {
125         return p instanceof LayoutParams;
126     }
127 
128     @Override
129     protected LayoutParams generateDefaultLayoutParams() {
130         return new LayoutParams(LayoutParams.WRAP_CONTENT,
131                 LayoutParams.WRAP_CONTENT);
132     }
133 
134     @Override
135     public LayoutParams generateLayoutParams(AttributeSet attrs) {
136         return new LayoutParams(getContext(), attrs);
137     }
138 
139     @Override
140     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 = a
160                         .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         <View
16             android:layout_width="100dp"
17             android:layout_height="150dp"
18             android:background="#FF0000" />
19 
20         <View
21             android:layout_width="100dp"
22             android:layout_height="150dp"
23             android:background="#00FF00" />
24 
25         <View
26             android:layout_width="100dp"
27             android:layout_height="150dp"
28             android:background="#0000FF" />
29     </com.example.myviewgroup.view.MyViewGoup>
30 
31 </LinearLayout>

 

  
 
posted @ 2014-10-28 15:25  perfect亮  阅读(182)  评论(0编辑  收藏  举报