显示效果如下,自定义view,重写onMeasure方法,测量wrap_content模式下控件的宽高,重写onLayout的方法,布局里面的子view,支持paddign属性.其实整个逻辑并不复杂,各位猿友可别复制粘贴啊!
/**
* Created by Administrator on 2017/7/24.
*/
public class MyFlowLayout extends ViewGroup{
//存储所有的view,行数就是该集合的元素个数
private List<List<View>> mAllViews=new ArrayList<>();
//记录每一行的行高,行数就是该集合的元素个数
private List<Integer> mLineHeight=new ArrayList<>();
public MyFlowLayout(Context context) {
this(context,null);
}
public MyFlowLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MyFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
//wrap_content时,需要自己计算宽高
int width=0;
int height=0;
//记录每一行的宽度和高度
int lineWidth=0;
int lineHeight=0;
//得到内部元素的个数
int cCount=getChildCount();
for (int i = 0; i < cCount; i++) {
View child = getChildAt(i);
//测量子view的宽和高
measureChild(child,widthMeasureSpec,heightMeasureSpec);
//得到layoutParams
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//子view占据的宽度
int childWidth=child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
//子view占据的高度
int childHeight=child.getMeasuredHeight()+lp.topMargin+lp.bottomMargin;
//换行
if (lineWidth+childWidth>sizeWidth-getPaddingLeft()-getPaddingRight())
{
//对比得到最大的宽度
width= Math.max(width,lineWidth);
//重置lineWidth
lineWidth=childWidth;
//记录行高
height+=lineHeight;
lineHeight=childHeight;
}
else
{
//叠加行宽
lineWidth+=childWidth;
//得到当前行最大的高度
lineHeight=Math.max(lineHeight,childHeight);
}
//如果是最后一个控件
if (i==cCount-1)
{
width=Math.max(width,lineWidth);
height+=lineHeight;
}
}
setMeasuredDimension(modeWidth==MeasureSpec.EXACTLY?sizeWidth:width+getPaddingLeft()+getPaddingRight(),
modeHeight==MeasureSpec.EXACTLY?sizeHeight:height+getPaddingBottom()+getPaddingTop());
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//清除数据,因为会重复调用
mAllViews.clear();
mLineHeight.clear();
//当前viewgroup的宽度
int width=getWidth();
int lineWidth=0;
int lineHeight=0;
List<View> lineViews=new ArrayList<>();
int cCount=getChildCount();
for (int i = 0; i < cCount; i++) {
View child = getChildAt(i);
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childWidth=child.getMeasuredWidth();
int childHeight=child.getMeasuredHeight();
//如果需要换行
if (childWidth+lp.leftMargin+lp.rightMargin+lineWidth>width)
{
//记录lineHeight
mLineHeight.add(lineHeight);
//记录当期的views
mAllViews.add(lineViews);
//重置我们的行宽和行高
lineWidth=0;
lineHeight=childHeight+lp.topMargin+lp.bottomMargin;
//重置我们的view集合
lineViews=new ArrayList<>();
}
lineWidth+=childWidth+lp.leftMargin+lp.rightMargin;
lineHeight=Math.max(lineHeight,childHeight+lp.topMargin+lp.bottomMargin);
lineViews.add(child);
}
//处理最后一行
mLineHeight.add(lineHeight);
mAllViews.add(lineViews);
//设置子view的位置
int left=getPaddingLeft();
int top=getPaddingTop();
//行数
int lineNum=mAllViews.size();
for (int i = 0; i < lineNum; i++) {
lineViews=mAllViews.get(i);
lineHeight = mLineHeight.get(i);
for (int j = 0; j < lineViews.size(); j++) {
View child = lineViews.get(j);
//判断child的状态
if (child.getVisibility()==GONE)
{
continue;
}
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int lc=left+lp.leftMargin;
int tc=top+lp.topMargin;
int rc=lc+child.getMeasuredWidth();
int bc=tc+child.getMeasuredHeight();
//为子view进行布局
child.layout(lc,tc,rc,bc);
left+=child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
}
left=getPaddingLeft();
top+=lineHeight;
}
}
//与当前viewgroup对应的layoutParams
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(),attrs);
}
}