创建自定义ViewGroup
?一,概要:
使用自定义View 和ViewGroup组织应用程序布局是一个好方法,定制组件的同时允许开发者提供自定义行为和功能,以后,开发者
在需要创建复杂布局的视乎,首先应该考虑使用自定义ViewGroup是不是更适合,虽然在开始时,这样做会增加一定的工作量,但是,这是值得的
?二,基本知识点:
要想绘制自定义ViewGroup要涉及到两个方法:
measure(int , int):该方法从上到下遍历视图树。在递归遍历过程中,每个视图都会从下层传递尺寸和规格。当measure方法遍历结束,每个视图都保存了各自的尺寸信息。
layout(int, int, int, int):该方法也是从上而下遍历视图树,在遍历过程中,每个父视图通过测量过程的结果定位所有子视图的位置信息。
思路整理:
- 要设置每个子View的横向偏移和竖向偏移普通的android属性中没有,所以需要自定义属性来获取值后来进行设置
- 通过复写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>
时间: 2024-11-08 21:59:23