public class MyView extends View { public MyView(Context context) { super(context); } public MyView(Context context,AttributeSet attrs) { super(context, attrs); } /** * 测量--计算 * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMySize(100, widthMeasureSpec); int height = getMySize(100, heightMeasureSpec); // 如果宽小于高,---->取小变相等 if (width < height) { height = width; } else { width = height; } setMeasuredDimension(width, height); // 设置测量值 } private int getMySize(int defaultSize, int measureSpec) { int mySize = defaultSize; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); switch (mode) { case MeasureSpec.UNSPECIFIED: {//如果没有指定大小,就设置为默认大小;--> 固定值 mySize = defaultSize; break; } case MeasureSpec.AT_MOST: {//如果测量模式是最大取值为size wrap_content—>AT_MOST //我们将大小取最大值,你也可以取其他值 mySize = size; break; } case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改变它 match_parent—>EXACTLY 固定尺寸(如100dp)—>EXACTLY mySize = size; break; } } return mySize; } }
//
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.administrator.carsonview.MyView android:layout_width="match_parent" android:layout_height="100dp" android:background="#ff0000" /> </LinearLayout>
//效果
//
public class MyView extends View { private int defalutSize; public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); //第二个参数就是我们在styles.xml文件中的<declare-styleable>标签 //即属性集合的标签,在R文件中名称为R.styleable+name TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView); //第一个参数为属性集合里面的属性,R文件名称:R.styleable+属性集合名称+下划线+属性名称 //第二个参数为,如果没有设置这个属性,则设置的默认的值 defalutSize = a.getDimensionPixelSize(R.styleable.MyView_default_size, 100); //最后记得将TypedArray对象回收 a.recycle(); } private int getMySize(int defaultSize, int measureSpec) { int mySize = defaultSize; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); switch (mode) { case MeasureSpec.UNSPECIFIED: {//如果没有指定大小,就设置为默认大小;--> 固定值 mySize = defaultSize; break; } case MeasureSpec.AT_MOST: {//如果测量模式是最大取值为size wrap_content—>AT_MOST //我们将大小取最大值,你也可以取其他值 mySize = size; break; } case MeasureSpec.EXACTLY: {//如果是固定的大小,那就不要去改变它 match_parent—>EXACTLY 固定尺寸(如100dp)—>EXACTLY mySize = size; break; } } return mySize; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMySize(defalutSize, widthMeasureSpec); int height = getMySize(defalutSize, heightMeasureSpec); // 如果宽小于高,---->取小变相等 if (width < height) { height = width; } else { width = height; } setMeasuredDimension(width, height); } /** * 绘制 * @param canvas */ @Override protected void onDraw(Canvas canvas) { //调用父View的onDraw函数,因为View这个类帮我们实现了一些 // 基本的而绘制功能,比如绘制背景颜色、背景图片等 super.onDraw(canvas); int r = getMeasuredWidth() / 2;//也可以是getMeasuredHeight()/2,本例中我们已经将宽高设置相等了 //圆心的横坐标为当前的View的左边起始位置+半径 int centerX = getLeft() + r; //圆心的纵坐标为当前的View的顶部起始位置+半径 int centerY = getTop() + r; Paint paint = new Paint(); paint.setColor(Color.GREEN); //开始绘制 canvas.drawCircle(centerX, centerY, r, paint); } }
//
<?xml version="1.0" encoding="utf-8"?> <resources> <!--name为声明的"属性集合"名,可以随便取,但是最好是设置为跟我们的View一样的名称--> <declare-styleable name="MyView"> <!--声明我们的属性,名称为default_size,取值类型为尺寸类型(dp,px等)--> <attr name="default_size" format="dimension" /> </declare-styleable> </resources>
//
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:hh="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.administrator.carsonview.MyView android:layout_width="match_parent" android:layout_height="100dp" hh:default_size="100dp" /> </LinearLayout>
//效果
public class MyViewGroup extends ViewGroup{ public MyViewGroup(Context context) { super(context); } public MyViewGroup(Context context, AttributeSet attrs) { super(context, attrs); } /** * 测量--计算 * @param widthMeasureSpec * 宽度的模式和测量值 * @param heightMeasureSpec * 高度的模式和测量值 * * * wrap_content—>AT_MOST * match_parent—>EXACTLY */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //将所有的子View进行测量,这会触发每个子View的onMeasure函数 //注意要与measureChild区分,measureChild是对单个view进行测量 measureChildren(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec);// 宽度模型 int widthSize = MeasureSpec.getSize(widthMeasureSpec);// 宽度大小 int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); // 子view 数量 int childCount = getChildCount(); if (childCount == 0) {//如果没有子View,当前ViewGroup没有存在的意义,不用占用空间 // TODO 设置测量尺寸 setMeasuredDimension(0, 0); } else { /** 宽和高包裹 **/ //如果宽高都是包裹内容(用子view高之和,子view最宽的一个) // wrap_content—>AT_MOST if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) { //我们将高度设置为所有子View的高度相加,宽度设为子View中最大的宽度 int height = getTotleHeight(); // 子view高之和 int width = getMaxChildWidth();// 子view最宽的一个 setMeasuredDimension(width, height); /** 高包裹 **/ } else if (heightMode == MeasureSpec.AT_MOST) {//如果只有高度是包裹内容(子View高之和,宽:ViewGroup自己的测量值) //宽度设置为ViewGroup自己的测量宽度,高度设置为所有子View的高度总和 setMeasuredDimension(widthSize, getTotleHeight()); /** 宽包裹 **/ } else if (widthMode == MeasureSpec.AT_MOST) {//如果只有宽度是包裹内容(用子View最宽一个,高:ViewGroup自己的测量值) //宽度设置为子View中宽度最大的值,高度设置为ViewGroup自己的测量值 setMeasuredDimension(getMaxChildWidth(), heightSize); } } } /** * 摆放 * @param changed view有新的尺寸或者位置 * @param l left 位置 * @param t top 位置 * @param r right 位置 * @param b bottom 位置 * * child.layout(l, curHeight, l + width, curHeight + height); */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // 子view个数 int count = getChildCount(); //记录当前的高度位置 int curHeight = t;// top Log.i("cdx:","当前高度:"+curHeight); //将子View逐个摆放 for (int i = 0; i < count; i++) { View child = getChildAt(i); // 获取测量高度 int height = child.getMeasuredHeight(); // 获取测量宽度 int width = child.getMeasuredWidth(); Log.i("cdx:","测量高度:"+height); Log.i("cdx:","测量宽度:"+width); //摆放子View,参数分别是子View矩形区域的左、上、右、下边 // child.layout(l, curHeight, l + width, curHeight + height); child.layout(l, curHeight, l + width, curHeight + height); // 左 5 curHeight += height; } } /*** * 获取子View中宽度最大的值 */ private int getMaxChildWidth() { // 获取子View 数量 int childCount = getChildCount(); // 3 Log.i("cdx:","子View数量"+childCount); int maxWidth = 0; for (int i = 0; i < childCount; i++) { // 每个子view对象 View childView = getChildAt(i); Log.i("cdx:","每个子view对象:"+childView); // 获取的是view原始的大小 if (childView.getMeasuredWidth() > maxWidth) maxWidth = childView.getMeasuredWidth(); Log.i("cdx:","获取子View中宽度最大的值:"+maxWidth); } return maxWidth; } /*** * 将所有子View的高度相加 **/ private int getTotleHeight() { int childCount = getChildCount(); int height = 0; for (int i = 0; i < childCount; i++) { // 每个子View View childView = getChildAt(i); // 每个子View 的高之和 height += childView.getMeasuredHeight(); } return height; } }
//
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.example.administrator.carsonview.MyViewGroup android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#F8DF82"> <Button android:layout_width="100dp" android:layout_height="wrap_content" android:text="Button1" /> <Button android:layout_width="200dp" android:layout_height="wrap_content" android:text="Button2" /> <Button android:layout_width="120dp" android:layout_height="wrap_content" android:text="Button3" /> </com.example.administrator.carsonview.MyViewGroup> </LinearLayout>
// 效果