自定义流式布局

解决问题:
1、确定那个子控件应该放置到哪一行
2、对measure的高度测量进行核实防止高度计算错误
3、对于每行剩余的空间平均分配给每一个子控件
4、对于同一行元素的摆放使其能够在该行中居中显示
5、修正了在添加padding后控件显示不完全的问题

注意:只有在调用了measure方法后getMeasuredXXX才能获取到值,否则为0
正确获取控件大小,可以自己手动进行测量调用子view的measure方法
测量结束是在设置了setMeasuredimention之后
onmeasure方法会被调用两次,防止计算时出现错误
只有在调用了layout方法之后才能够拿到getXXX值

package chuang.sun.cn.flowlayout;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

/**
* Created by sunchuang on 2017/10/17.
*/

public class MyFlowLayout extends ViewGroup {

private static final int DEFAULT_SPACING = 20;
private int mHorizontalSpacing = DEFAULT_SPACING;
private int mVerticalSpacing = DEFAULT_SPACING;

private int mUsedWidth;
private int totalWidth;
private Line mCurrenLine;
private int measureHeight;
private List<Line> lineList = new ArrayList<>();
public MyFlowLayout(Context context) {
    super(context);
    init();
}

public MyFlowLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

public MyFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

private void init() {
    mCurrenLine = new Line();
    lineList.add(mCurrenLine);
    mUsedWidth = 0;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    reSet();
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    int childCount = getChildCount();
    totalWidth = widthSize - getPaddingLeft()-getPaddingRight();
    for (int i = 0; i < childCount; i++) {
        View child = getChildAt(i);

        //如果view显示模式是Gone则不必测量
        if(child.getVisibility() == View.GONE){
            continue;
        }

        //获取子view的测量模式
        int childWidthMeasureSpecMode = widthMode == MeasureSpec.EXACTLY?MeasureSpec.AT_MOST:widthMode;
        int childHeightMeasureSpecMode = heightMode == MeasureSpec.EXACTLY?MeasureSpec.AT_MOST:heightMode;

        //合成子view的MeasureSpec
        int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(totalWidth,childWidthMeasureSpecMode);
        int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize,childHeightMeasureSpecMode);
        child.measure(childWidthMeasureSpec,childHeightMeasureSpec);

        //测量之后获取子控件的大小
        int childWidth = child.getMeasuredWidth();

        mUsedWidth += childWidth;

        //如果该行还可以放下view则在该行放置View
        if(mUsedWidth<=totalWidth){
            mCurrenLine.addView(child);
            mUsedWidth+=mHorizontalSpacing;

//如果加上空余的部分超出了每行的最大值那么就换行
if(mUsedWidth>totalWidth){
newLine();
}
}else{

        //如果有哪行没有数据,需要进行添加保证每行至少有一条数据
            if(mCurrenLine.getListCount() == 0){
                mCurrenLine.addView(child);
                mUsedWidth = totalWidth;
            }else{
                newLine();
                mCurrenLine.addView(child);
                mUsedWidth += childWidth + mHorizontalSpacing;
            }
        }
    }
    //根据行高计算出整个viewgroup的高度
    for(int i = 0;i<lineList.size();i++){
        measureHeight += lineList.get(i).mLineHeight+mVerticalSpacing;
    }
    measureHeight -= mVerticalSpacing;
    measureHeight+=getPaddingBottom()+getPaddingTop();
    setMeasuredDimension(widthSize,resolveSize(measureHeight,heightMeasureSpec));
}

private void newLine() {
    mCurrenLine = new Line();
    lineList.add(mCurrenLine);
    mUsedWidth = 0;
}

private void reSet(){
    mUsedWidth = 0;
    measureHeight = 0;
    lineList.clear();
    mCurrenLine = new Line();
    lineList.add(mCurrenLine);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int lineCount = lineList.size();
    l = getPaddingLeft();
    t = getPaddingBottom();
    for(int i = 0;i<lineCount;i++){
        Line line = lineList.get(i);
        line.layoutView(l,t);
        t+=line.mLineHeight+mVerticalSpacing;
    }
}

class Line{

    public int mLineWidth;
    public int mLineHeight;
    public List<View> viewList = new ArrayList<>();
    private int splitSpacing;

    public void addView(View view){
        if(!viewList.contains(view)){
            viewList.add(view);
            //计算出每行使用的宽度以及每行的高度
            mLineWidth += view.getMeasuredWidth()+mHorizontalSpacing;
            mLineHeight = mLineHeight>view.getMeasuredHeight()?mLineHeight:view.getMeasuredHeight();
        }
    }

    public int getListCount(){
        return viewList.size();
    }

    public void layoutView(int left,int top){
        int viewCount = getListCount();
        int remainSpacing = getMeasuredWidth() - mLineWidth;
        if(remainSpacing>0){//当有空余的时候平均分配
            splitSpacing = remainSpacing/getListCount();
        }
        for(int i=0;i<viewCount;i++){
            View mView = viewList.get(i);
            int mViewHeight = mView.getMeasuredHeight();
            int mViewWidth = mView.getMeasuredWidth();
            int offsetHeight = (int) ((mLineHeight-mViewHeight)/2*1.0+0.5);
            mViewWidth+=splitSpacing;
            mView.getLayoutParams().width = mViewWidth;
            //根据新计算的view大小及位置重新测量
            mView.measure(MeasureSpec.makeMeasureSpec(mViewWidth,MeasureSpec.EXACTLY),MeasureSpec.makeMeasureSpec(mViewHeight,MeasureSpec.EXACTLY));
            mView.layout(left,top+offsetHeight,left+mViewWidth,top+mViewHeight+offsetHeight);
            //同一行中高度不变,只有x坐标在改变
            left += mViewWidth +mHorizontalSpacing;
        }
    }
}

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值