先上源码
package com.example.view2;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* 项目名称 View2
* 创建人
* 创建时间 10/2/21 4:37 PM
**/
class MyFlowLayout extends ViewGroup {
final String TAG = "MyFlowLayout";
// 每行中的view on 10/2/21 4:48 PM
private List<View> rowList = new ArrayList<>();
// 所有的view on 10/2/21 4:49 PM
private List<List<View>> lists = new ArrayList<>();
private List<Integer> heights = new ArrayList<>();
private List<Integer> widths = new ArrayList<>();
// 当前行已添加View的宽度和 on 10/2/21 4:50 PM
int currentRowSize = 0;
public MyFlowLayout(Context context) {
super(context);
}
public MyFlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
lists.clear();
rowList.clear();
currentRowSize = 0;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int currentMaxHeight = 0;
int childCount = getChildCount();
for(int i=0;i<childCount;i++){
View view = getChildAt(i);
int childWidthSpec = getChildMeasureSpec(widthMeasureSpec,0,view.getLayoutParams().width);
int childHeightSpec = getChildMeasureSpec(heightMeasureSpec,0,view.getLayoutParams().height);
view.measure(childWidthSpec,childHeightSpec);
currentRowSize = currentRowSize + view.getMeasuredWidth();
currentMaxHeight = Math.max(currentMaxHeight,view.getMeasuredHeight());
if(currentRowSize > widthSize){
widths.add(currentRowSize - view.getMeasuredWidth());
currentRowSize = view.getMeasuredWidth();
lists.add(rowList);
heights.add(currentMaxHeight);
rowList = new ArrayList<>();
}
rowList.add(view);
view.measure(childWidthSpec,childWidthSpec);
}
if(rowList.size() > 0){
lists.add(rowList);
widths.add(currentRowSize);
heights.add(currentMaxHeight);
}
int width = 0;
int height = 0;
switch (widthMode){
case MeasureSpec.EXACTLY:
width = widthSize;
break;
case MeasureSpec.AT_MOST:
for(int i=0;i<widths.size();i++){
width = Math.max(width,widths.get(i));
}
break;
case MeasureSpec.UNSPECIFIED:
width = widthSize;
break;
}
switch (heightMode){
case MeasureSpec.EXACTLY:
height = heightSize;
break;
case MeasureSpec.AT_MOST:
for(int i=0;i<heights.size();i++){
height = height + heights.get(i);
}
break;
case MeasureSpec.UNSPECIFIED:
height = heightSize;
break;
}
setMeasuredDimension(width,height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int leftOffset = 0;
int topOffset = 0;
for(int i=0;i<lists.size();i++){
rowList = lists.get(i);
leftOffset = 0;
for(int j=0;j<rowList.size();j++){
View view = rowList.get(j);
view.layout(
leftOffset,
topOffset,
leftOffset + view.getMeasuredWidth(),
topOffset + view.getMeasuredHeight());
leftOffset = leftOffset + view.getMeasuredWidth();
}
topOffset = topOffset + heights.get(i);
}
}
}
复制代码
思路说明
测量:
- 遍历子View,父容器的MeasureSpec和子View的布局参数,测量子View。
- 在测量过程中,记录每一个View的宽和高。
- 遍历过程中累加遍历的view的宽度值,如果超过了父容器的宽度,换行。
- 换行时,将当前行的View中的高度的最大值保存。
布局:
- 遍历保存的每一行的View,计算left、top、right和bottom,为其定位。
问题 在自定义ViewGroup中,如果ViewGroup的宽度为wrap_content,ViewGroup最终的尺寸如何确定。