总结流式布局经验,比较简单,就是先获取当前容器的宽高,遍历所有的子view,根据子view的宽度和子view间的距离来计算是否超出当前容器的宽度,如果超出宽度的话记录当前行的子view,和当前行的高度,如果不需要换行,就宽度累加,获取最大高度。获取到总高度和每行的子view后,开始设置每个子view的位置,为每个view设置布局属性
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class CustomFlowLayout extends ViewGroup {
public CustomFlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomFlowLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CustomFlowLayout(Context context) {
this(context,null);
}
@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);
//重写了generateLayoutParams,获取到的是子控件布局的属性,可以获取属性中margin的值
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//子View占据的宽度
int childWidth = child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
//子view占据的高度
int childHeight = child.getMeasuredHeight()+lp.topMargin+lp.bottomMargin;
//大于sizewidth换行
if(lineWidth+childWidth>sizeWidth){
//对比得到�?大的宽度
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(lineWidth, width);
height +=lineHeight;
}
}
//wrap_content
// if(modeWidth==MeasureSpec.AT_MOST){
// setMeasuredDimension(width, height);
// }else{
// setMeasuredDimension(sizeWidth, sizeHeight);
// }
//以上注释部分可以用下面代码替换
//如果模式是EXACTLY(宽高为精确值) 用传入的控件宽高,否则使用自己测量的其控件宽高
setMeasuredDimension(
modeWidth==MeasureSpec.EXACTLY?sizeWidth:width,
modeHeight==MeasureSpec.EXACTLY?sizeHeight:height);
}
/**
* 存储所有的view
*/
private List<List<View>> mAllViews = new ArrayList<List<View>>();
/**
* 每一行的高度
*/
private List<Integer> mLineHeight = new ArrayList<Integer>();
/**
* 设置子view的宽高布局
*/
@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<View>();
int cCount = getChildCount();//子view的个数
for(int i=0;i<cCount-1;i++){
View child = getChildAt(i);
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//获取子view的宽度
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
if(childWidth+lineWidth+lp.leftMargin+lp.rightMargin>width){
//记录lineHeight
mLineHeight.add(lineHeight);
//记录当前的views
mAllViews.add(lineViews);
//重置下一行宽度
lineWidth=0;
lineHeight = childHeight+lp.topMargin+lp.bottomMargin;
//重置views集合
lineViews = new ArrayList<View>();
}else{
lineWidth+=childWidth+lp.leftMargin+lp.rightMargin;
lineHeight = Math.max(lineHeight, childHeight+lp.topMargin+lp.bottomMargin);
lineViews.add(child);
}
}//for end
//处理最后一个
mLineHeight.add(lineHeight);
mAllViews.add(lineViews);
//设置子view的位置
int left = 0;
int top = 0;
//行数
int lineNum = mAllViews.size();
for(int i=0;i<lineNum;i++){
//当前行的所有的view
lineViews = mAllViews.get(i);
//当前控件
lineHeight = mLineHeight.get(i);
for(int j=0;j<lineViews.size();j++){
View child = lineViews.get(j);
if(child.getVisibility()==View.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设为0;高度增加lineheight
left=0;
top += lineHeight;
}
}
/**
* 与当前viewgroup对应的布局
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(),attrs);
}
}
测试Activity:
import com.example.testcustomviewgroup.R;
import android.os.Bundle;
import android.app.Activity;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.Button;
public class MainActivity extends Activity {
CustomFlowLayout vg_test;
String[] text = {"pulltofresh","visiable","hello world","linearlayout","goodidea","test","abcd","free","Android"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vg_test = (CustomFlowLayout) findViewById(R.id.vg_test);
initData();
}
private void initData() {
for(int i=0;i<text.length;i++){
Button btnButton = new Button(this);
MarginLayoutParams lp = new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT);
btnButton.setText(text[i]);
vg_test.addView(btnButton,lp);
}
}
}
结果显示: