效果图:

先梳理下整个流程:
- 继承ViewGroup ,重写onMeasure,onLayout 方法;
- 在onMeasure方法里通过加入其中的子view个数来计算父级的宽高;
- 在onLayout 方法里对子view进行排版(横向排列),即超过父级宽度就另换一行
注意:当父级宽高发生变化时会重新执行onMeasure,onLayout 方法,即会被调用2遍。
详细代码如下:
FlowLayout:
import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* 自定义流式布局
*/
public class FlowLayout extends ViewGroup{
/** view距离父布局的边界信息*/
private MarginLayoutParams marginLayoutParams;
/** child宽度*/
private int childwidth;
/** child高度*/
int childheight;
/** 父级实际的宽度*/
int factwidth;
/** 父级实际的高度*/
int factheight;
/** 每行宽度*/
int linewidth;
/** 每行高度*/
int lineheight;
/** 用于记录上一次的行高*/
int lastchildheight=0;
public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs,0);
}
public FlowLayout(Context context) {
super(context,null);
}
/**
* 根据子view来确定宽高
* */
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
factheight=0;
factwidth=0;
int widthsize=MeasureSpec.getSize(widthMeasureSpec);
int widthmode=MeasureSpec.getMode(widthMeasureSpec);
int heightsize=MeasureSpec.getSize(heightMeasureSpec);
int heightmode=MeasureSpec.getMode(heightMeasureSpec);
int childcount=getChildCount();
for(int i=0;i<childcount;i++){
View child=getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
marginLayoutParams=(MarginLayoutParams) child.getLayoutParams();
childwidth=child.getMeasuredWidth()+marginLayoutParams.leftMargin+marginLayoutParams.rightMargin;
childheight=child.getMeasuredHeight()+marginLayoutParams.topMargin+marginLayoutParams.bottomMargin;
if(linewidth+childwidth>widthsize){
factwidth=Math.max(widthsize,linewidth);
factheight+=lastchildheight;
linewidth=0;
lastchildheight=0;
lineheight=0;
}
linewidth+=childwidth;
lineheight=Math.max(lastchildheight,childheight);
lastchildheight=lineheight;
if(i==childcount-1){
factwidth=Math.max(widthsize,linewidth);
factheight+=lastchildheight;
lastchildheight=0;
lineheight=0;
linewidth=0;
}
}
setMeasuredDimension(widthmode==MeasureSpec.EXACTLY?widthsize:factwidth,
heightmode==MeasureSpec.EXACTLY?heightsize:factheight);
}
/** 每行view的集合*/
private List<List<View>> AllChildView=new ArrayList<List<View>>();
/** 每行高度的集合*/
private List<Integer> LineHeight=new ArrayList<Integer>();
/**
* 排版 (横向排列)
* */
@SuppressLint("DrawAllocation")
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
factwidth=getMeasuredWidth();
AllChildView.clear();
LineHeight.clear();
List<View> linelist=new ArrayList<View>();
int childcount=getChildCount();
for(int i=0;i<childcount;i++){
View view=getChildAt(i);
marginLayoutParams=(MarginLayoutParams) view.getLayoutParams();
int childwidth=view.getMeasuredWidth()+marginLayoutParams.leftMargin+marginLayoutParams.rightMargin;
int childheight=view.getMeasuredHeight()+marginLayoutParams.topMargin+marginLayoutParams.bottomMargin;
if(linewidth+childwidth>=factwidth){
LineHeight.add(lastchildheight);
AllChildView.add(linelist);
linewidth=0;
lastchildheight=0;
linelist=new ArrayList<View>();
}
lineheight=Math.max(childheight,lastchildheight);
lastchildheight=lineheight;
linewidth+=childwidth;
linelist.add(view);
if(i==childcount-1){
LineHeight.add(lastchildheight);
AllChildView.add(linelist);
lastchildheight=0;
linewidth=0;
}
}
int left=0;
int top=0;
for(int w=0;w<AllChildView.size();w++){
linelist=AllChildView.get(w);
lineheight=LineHeight.get(w);
for(int m=0;m<linelist.size();m++){
View childview=linelist.get(m);
if(childview.getVisibility()==View.GONE){
continue;
}
marginLayoutParams=(MarginLayoutParams) childview.getLayoutParams();
int cleft=left+marginLayoutParams.leftMargin;
int ctop=top+marginLayoutParams.topMargin+(lineheight/2-childview.getHeight()/2);
int cright=cleft+childview.getMeasuredWidth();
int cbottom=ctop+childview.getMeasuredHeight();
childview.layout(cleft, ctop, cright, cbottom);
left+=childview.getMeasuredWidth()+marginLayoutParams.leftMargin+marginLayoutParams.rightMargin;
}
left=0;
top+=lineheight;
}
}
}
FlowLayoutActivity:
import java.util.Random
import android.app.Activity
import android.graphics.Color
import android.os.Bundle
import android.view.Gravity
import android.view.ViewGroup.LayoutParams
import android.view.ViewGroup.MarginLayoutParams
import android.widget.TextView
public class FlowLayoutActivity extends Activity {
private String mNames[] = {
"welcome","android","TextView",
"apple","jamy","kobe bryant",
"jordan","layout","viewgroup",
"margin","padding","text","地方了开始讲道理","第三方","的飞洒",
"name","type","search","logcat","逗你玩",
"罚款圣诞节疯狂绝对是垃圾分类看见冻死了快解放了跨世纪的离开房间了少打飞放得开收垃圾费考虑到就是浪费",
"发神经"
}
private Random random
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_flow_layout)
random=new Random()
init()
}
private void init() {
FlowLayout flowLayout=(FlowLayout) findViewById(R.id.flowlayout)
MarginLayoutParams lp = new MarginLayoutParams(
LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT)
lp.leftMargin = 5
lp.rightMargin = 5
lp.topMargin = 5
lp.bottomMargin = 5
int a=0,i=0
while(a<40){
++a
i=random.nextInt(mNames.length)
TextView view = new TextView(this)
view.setText(mNames[i])
view.setTextColor(Color.WHITE)
view.setGravity(Gravity.CENTER)
view.setBackground(getResources().getDrawable(R.drawable.textview_bg))
if(i%3==0){
view.setTextSize(20)
}
view.setTextColor(Color.rgb(random.nextInt(255), random.nextInt(255), random.nextInt(255)))
flowLayout.addView(view,lp)
}
}
}
R.layout.activity_flow_layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.mytoolutils.FlowLayoutActivity" >
<com.example.mytoolutils.FlowLayout
android:id="@+id/flowlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_blue_light" >
</com.example.mytoolutils.FlowLayout>
</LinearLayout>