自定义ViewGroup代码
package com.example.customview.customview.keypad;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.example.customview.R;
import com.example.customview.customview.utils.SizeUtils;
public class KeyPadView extends ViewGroup {
public static final String TAG="KeyPadView";
//4行
public static final int DEFAULT_ROW=4;
//3列
public static final int DEFAULT_COLUMN=3;
private int mTextColor;
private float mTestSize;
private int mItemNormalBgColor;
private int mItemPressBgColor;
private int row=DEFAULT_ROW;
private int column=DEFAULT_COLUMN;
public KeyPadView(Context context) {
this(context,null);
}
public KeyPadView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public KeyPadView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//
initAttrs(context, attrs);
//
setupItem();
}
private void setupItem() {
removeAllViews();
for(int i=0;i<11;i++) {
TextView item = new TextView(getContext());
//内容
if(i==10){
//最后一个是删除
item.setTag(true);
item.setText("删除");
}else {
item.setTag(false);
item.setText(String.valueOf(i));
}
//大小
item.setTextSize(TypedValue.COMPLEX_UNIT_PX,mTestSize);//将mTestSize转成px格式
//居中
item.setGravity(Gravity.CENTER);
//字体颜色
item.setTextColor(mTextColor);
//设置背景
item.setBackground(providerItemBg());
item.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
addView(item);
}
}
//使用java代码编写item的Drawable背景
private Drawable providerItemBg(){
//按下去了的background
GradientDrawable pressDrawable = new GradientDrawable();//GradientDrawable会比ShapeDrawable属性多一些
pressDrawable.setColor(getResources().getColor(R.color.key_item_press_color));
pressDrawable.setCornerRadius(SizeUtils.dip2px(5));
StateListDrawable bg = new StateListDrawable();
bg.addState(new int[]{android.R.attr.state_pressed},pressDrawable);
//普通状态的background
GradientDrawable normalDrawable = new GradientDrawable();
normalDrawable.setColor(getResources().getColor(R.color.key_item_color));
normalDrawable.setCornerRadius(SizeUtils.dip2px(5));
bg.addState(new int[]{},normalDrawable);
return bg;
}
private void initAttrs(Context context, AttributeSet attrs) {
//attrs获取xml中标记的属性
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyPadView);
//获取属性
mTextColor = a.getColor(R.styleable.KeyPadView_numberColor, context.getResources().getColor(R.color.white));
mTestSize = a.getDimension(R.styleable.KeyPadView_numberSize, 16);
Log.d(TAG, "mTestSize: --->"+mTestSize);
mItemNormalBgColor = a.getColor(R.styleable.KeyPadView_itemNormalColor, context.getResources().getColor(R.color.key_item_color));
mItemPressBgColor = a.getColor(R.styleable.KeyPadView_itemPressColor, context.getResources().getColor(R.color.key_item_press_color));
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize=MeasureSpec.getSize(widthMeasureSpec);
int widthMode=MeasureSpec.getMode(widthMeasureSpec);
int heightSize=MeasureSpec.getSize(heightMeasureSpec);
int heightMode=MeasureSpec.getMode(heightMeasureSpec);
Log.d(TAG, "widthSize: "+widthSize);
Log.d(TAG, "widthMode: "+widthMode);
Log.d(TAG, "heightSize: "+heightSize);
Log.d(TAG, "heightMode: "+heightMode);
Log.d(TAG, "EXACTLY: "+MeasureSpec.EXACTLY);
Log.d(TAG, "AT_MOST: "+MeasureSpec.AT_MOST);
//一行三列,求出列宽,三等分
int perItemWidth=widthSize/3;
int perItemHeight=heightSize/4;
int normalWidthSpec=MeasureSpec.makeMeasureSpec(perItemWidth,MeasureSpec.EXACTLY);
int deleteWidthSpec=MeasureSpec.makeMeasureSpec(perItemWidth*2,MeasureSpec.EXACTLY);
int heightSpec=MeasureSpec.makeMeasureSpec(perItemHeight,MeasureSpec.EXACTLY);
for(int i=0;i<getChildCount();i++){
View item = getChildAt(i);
boolean isDelete = (boolean) item.getTag();
//测量孩子
Log.d(TAG, "onMeasure: "+MeasureSpec.getSize(normalWidthSpec)+"---->"+MeasureSpec.getSize(heightMeasureSpec));
item.measure(isDelete ? deleteWidthSpec:normalWidthSpec,heightSpec);
// measureChild(item,isDelete ? deleteWidthSpec:normalWidthSpec,heightSpec);
}
//测量自己
setMeasuredDimension(widthMeasureSpec,heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int left=0,top,right,bottom;
for(int i=0;i<childCount;i++){
//求出当前元素在第几行第几列
int rowIndex=i/column;
int columnIndex=i%column;
if(columnIndex==0){
left=0;
}
View item = getChildAt(i);
top=rowIndex*item.getMeasuredHeight();
right=left+item.getMeasuredWidth();
bottom=top+item.getMeasuredHeight();
item.layout(left,top,right,bottom);
left+=item.getMeasuredWidth();
}
}
}
布局代码
<?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"
xmlns:YiRan="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<com.example.customview.customview.keypad.KeyPadView
android:layout_width="match_parent"
android:layout_height="wrap_content"
YiRan:numberSize="70px"
/>
</LinearLayout>
当使用item.measure对子view进行测量的时候显示和大锯哥视频讲解的结果一样是正常的,除了最后一个代表删除的子view,其他每个子view的宽和高都取精确值布满屏幕的1/3和1/4。

但是我记得上一次大锯哥讲解FlowLayout的时候测量子view使用的measureChild方法,我就好奇的将其测量方式改为measureChild
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize=MeasureSpec.getSize(widthMeasureSpec);
int widthMode=MeasureSpec.getMode(widthMeasureSpec);
int heightSize=MeasureSpec.getSize(heightMeasureSpec);
int heightMode=MeasureSpec.getMode(heightMeasureSpec);
Log.d(TAG, "widthSize: "+widthSize);
Log.d(TAG, "widthMode: "+widthMode);
Log.d(TAG, "heightSize: "+heightSize);
Log.d(TAG, "heightMode: "+heightMode);
Log.d(TAG, "EXACTLY: "+MeasureSpec.EXACTLY);
Log.d(TAG, "AT_MOST: "+MeasureSpec.AT_MOST);
//一行三列,求出列宽,三等分
int perItemWidth=widthSize/3;
int perItemHeight=heightSize/4;
int normalWidthSpec=MeasureSpec.makeMeasureSpec(perItemWidth,MeasureSpec.EXACTLY);
int deleteWidthSpec=MeasureSpec.makeMeasureSpec(perItemWidth*2,MeasureSpec.EXACTLY);
int heightSpec=MeasureSpec.makeMeasureSpec(perItemHeight,MeasureSpec.EXACTLY);
for(int i=0;i<getChildCount();i++){
View item = getChildAt(i);
boolean isDelete = (boolean) item.getTag();
//测量孩子
Log.d(TAG, "onMeasure: "+MeasureSpec.getSize(normalWidthSpec)+"---->"+MeasureSpec.getSize(heightMeasureSpec));
//item.measure(isDelete ? deleteWidthSpec:normalWidthSpec,heightSpec);
measureChild(item,isDelete ? deleteWidthSpec:normalWidthSpec,heightSpec);
}
//测量自己
setMeasuredDimension(widthMeasureSpec,heightMeasureSpec);
}
measureChild方法的源码
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
看了measureChild方法的源码,发现最后也是调用item的measure方法,想着应该和上面的结果一样才对。可以运行后显示的布局却是这样子

明明已经在测量的时候设置子view大小的时候给他设置了宽高分别为屏幕的1/3和1/4,模式也设置为精确值,但运行后确实这个结果,不太清楚是怎么回事?请各位大佬帮忙看一下
Textview 属于 view,没有子view,你调用measureChild,没有child给Textview测量,那么TextView只会获取能刚好wrap_content的宽度和高度,所以造成你这样