MyView继承View:
/**
* Created by zhaochengfang on 2020/12/8
*/
public class MyView extends View {
private int defaultSize;
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//第二个参数就是我们在styles.xml文件中的<declare-styleable>标签
//即属性集合的标签,在R文件中名称为R.styleable + name
TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.MyView);
//第一个参数为属性集合里边的属性,R文件名称:R.styleable + 属性集合名称 + 下划线 + 属性名称
//第二个参数为,如果没有设置这个属性,则设置的默认的值
defaultSize = array.getDimensionPixelSize(R.styleable.MyView_default_size,200);
//将TypedArray对象回收
array.recycle();
}
private int getMySize(int defaultSize,int measureSpec){
int mySize = defaultSize;
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
switch (mode){
//父容器没有对当前View有任何限制,当前View可以任意取尺寸,如果没有指定大小,就设置为默认大小
case MeasureSpec.UNSPECIFIED:{
mySize = defaultSize;
break;
}
//wrap_content,当前尺寸是当前View能取的最大尺寸,如果测量模式是最大取值为size,我们将大小取最大值,你也可以取其他值
case MeasureSpec.AT_MOST:
//match_parent||固定尺寸(如100dp),当前的尺寸就是当前View应该取的尺寸,如果是固定的大小,那就不要去改变它
case MeasureSpec.EXACTLY: {
mySize = size;
break;
}
default:
break;
}
return mySize;
}
@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);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int r = getMeasuredWidth() / 2;
int centerX = r;
int centerY = r;
Paint paint = new Paint();
paint.setColor(Color.GREEN);
//开始绘制
canvas.drawCircle(centerX,centerY,r,paint);
}
}
MyViewGroup继承ViewGroup:
/**
* Created by zhaochengfang on 2020/12/8
*/
public class MyViewGroup extends ViewGroup {
public MyViewGroup(Context context) {
super(context);
}
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
@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);
int childCount = getChildCount();
if (childCount == 0){//如果没有子View,当前ViewGroup没有存在的意义,不用占用空间
setMeasuredDimension(0,0);
}else {
//如果宽高都是包裹内容
if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
//我们将高度设置为所有子View的高度相加,宽度设置为子View中的最大的宽度
int height = getTotalHeight();
int width = getMaxChildWidth();
setMeasuredDimension(width,height);
}else if (heightMode == MeasureSpec.AT_MOST){//如果只有高度是包裹内容
//宽度设置为ViewGroup自己的测量宽度高度设置为所有子View的高度总和
setMeasuredDimension(widthSize,getTotalHeight());
}else if (widthMode == MeasureSpec.AT_MOST){//如果只有宽度是包裹内容
//宽度设置为子View中宽度最大的值,高度设置为ViewGroup自己的测量值
setMeasuredDimension(getMaxChildWidth(),heightSize);
}
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
//记录当前的高度位置
int curHeight = 0;
//将子View逐个摆放
for (int i = 0; i < count; i ++){
View child = getChildAt(i);
int height = child.getMeasuredHeight();
int width = child.getMeasuredWidth();
//摆放子View,参数分别是子View矩形区域的左、上、右、下边
child.layout(0,curHeight,0 + width,curHeight + height);
curHeight += height;
}
}
/**
* 获取子View中宽度最大的值
* */
private int getMaxChildWidth(){
int childCount = getChildCount();
int maxWidth = 0;
for (int i = 0;i < childCount; i ++){
View childView = getChildAt(i);
if (childView.getMeasuredWidth() > maxWidth){
maxWidth = childView.getMeasuredWidth();
}
}
return maxWidth;
}
/**
* 将所有子View的高度相加
* */
private int getTotalHeight(){
int childCount = getChildCount();
int height = 0;
for (int i = 0; i < childCount; i ++){
View childView = getChildAt(i);
height += childView.getMeasuredHeight();
}
return height;
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:zcf="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
>
<com.android.view.MyView
android:layout_width="match_parent"
android:layout_height="100dp"
zcf:default_size="200dp"
/>
<com.android.view.MyView
android:layout_width="match_parent"
android:layout_height="100dp"
zcf:default_size="200dp"
/>
<com.android.view.MyViewGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ff9900"
>
<Button
android:layout_width="100dp"
android:layout_height="30dp"/>
<Button
android:layout_width="100dp"
android:layout_height="200dp"/>
<Button
android:layout_width="100dp"
android:layout_height="50dp"/>
<Button
android:layout_width="100dp"
android:layout_height="100dp"/>
</com.android.view.MyViewGroup>
</LinearLayout>
效果: