一、FlowLayout介绍
所谓FlowLayout,就是控件根据ViewGroup的宽,自动的往右添加,如果当前行剩余空间不足,则自动添加到下一行。有点像所有的控件都往左飘的感觉,第一行满了,往第二行飘~所以也叫流式布局。Android并没有提供流式布局,但是某些场合中,流式布局还是非常适合使用的,比如关键字标签,搜索热词列表等,比如下图:\
\
二、制作分析
1、对于FlowLayout,需要指定的LayoutParams,我们目前只需要能够识别margin即可,即使用MarginLayoutParams.
2、onMeasure中计算所有childView的宽和高,然后根据childView的宽和高,计算自己的宽和高。(当然,如果不是wrap_content,直接使用父ViewGroup传入的计算值即可)
3、onLayout中对所有的childView进行布局。
\
//如果lineWidth + chilWidth > viewGroup最大宽度, childView就会移到下一行
if(lineWidth + childWidth > widthSpec){//换行
height += lineheight;
width = Math.max(lineWidth, childWidth);
lineWidth = childWidth;//当了下一行
}else {//同一行的话, 宽度增加
lineWidth = lineWidth + childWidth;
//只判断一行最大高度,当换行时, 才增加高度
lineheight = Math.max(childHeight, lineheight);
}
\
\
\
\
\
\
\
match_parent android:layout_width="fill_parent"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#E1E6F6"
android:orientation="vertical" >
<com.example.viewgroupdemo.MyViewGroup
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<TextView
style="@style/text_flag_01"
android:text="Welcome" />
<TextView
style="@style/text_flag_01"
android:text="IT工程师" />
<TextView
style="@style/text_flag_01"
android:text="学习ing" />
<TextView
style="@style/text_flag_01"
android:text="恋爱ing" />
<TextView
style="@style/text_flag_01"
android:text="挣钱ing" />
<TextView
style="@style/text_flag_01"
android:text="努力ing" />
<TextView
style="@style/text_flag_01"
android:text="I thick i can" />
</com.example.viewgroupdemo.MyViewGroup>
<com.example.viewgroupdemo.MyViewGroup
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" >
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="Welcome"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="IT工程师"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="学习ing"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="恋爱ing"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="挣钱ing"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="努力ing"
android:textColor="#888888" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_02"
android:text="I thick i can"
android:textColor="#888888" />
</com.example.viewgroupdemo.MyViewGroup>
<com.example.viewgroupdemo.MyViewGroup
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp" >
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="Welcome"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="IT工程师"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="学习ing"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="恋爱ing"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="挣钱ing"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="努力ing"
android:textColor="#43BBE7" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_03"
android:text="I thick i can"
android:textColor="#43BBE7" />
</com.example.viewgroupdemo.MyViewGroup>
</LinearLayout>
\
定值:android:layout_width="200dp"
<com.example.viewgroupdemo.MyViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:background="#FFFFFF" >
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_04"
android:text="Welcome"
android:textColor="#323232" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_04"
android:text="IT工程师"
android:textColor="#323232" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_04"
android:text="学习ing"
android:textColor="#323232" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_04"
android:text="恋爱ing"
android:textColor="#323232" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_04"
android:text="挣钱ing"
android:textColor="#323232" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_04"
android:text="努力ing"
android:textColor="#323232" />
<TextView
style="@style/text_flag_01"
android:background="@drawable/flag_04"
android:text="I thick i can"
android:textColor="#323232" />
</com.example.viewgroupdemo.MyViewGroup>
\
\
package com.example.viewgroupdemo;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.LinearLayout;
public class MyViewGroup extends ViewGroup {
public MyViewGroup(Context context) {
this(context, null);
}
public MyViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Log.i("tag", "MyViewGroup");
}
/***
* 第一步:重写generateLayoutParams, 确定ViewGroup的LayoutParams
* 返回MarginLayoutParams的实例, 这就是为我们的ViewGroup指定
* 其LayoutParams为MarginLayoutParams
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
Log.i("tag", "generateLayoutParams");
return new MarginLayoutParams(getContext(), attrs);
}
/**
* 第二步:
* 计算所有的childView的width和height,
* 然后根据childview的width,height, margin(MarginLayoutParams获取margin)
* 设置viewgroup的高度和宽度
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.i("tag", "onMeasure");
//获取父容器的模式和高度,宽度
int widthSpec = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSpec = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//如果是warp_content情况下, 记录高度和宽度
int width = 0;
int height = 0;
//记录当前行的高度和宽度
int lineWidth =0;
int lineheight = 0;
//Layoutparams
MarginLayoutParams mlp = null;
//获取childview的数目
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childview = getChildAt(i);
//测量childview的高度和宽度
measureChild(childview, widthMeasureSpec, heightMeasureSpec);
//获取LayoutParams
mlp = (MarginLayoutParams) childview.getLayoutParams();
//获取childview实际所占空间的高度和宽度
int childWidth = childview.getMeasuredWidth()+mlp.leftMargin+mlp.rightMargin;
int childHeight = childview.getMeasuredHeight()+mlp.topMargin+mlp.bottomMargin;
//如果lineWidth + chilWidth > viewGroup最大宽度, childView就会移到下一行
if(lineWidth + childWidth > widthSpec){//换行
height += lineheight;
width = Math.max(lineWidth, childWidth);
lineWidth = childWidth;//当了下一行
}else {//同一行的话, 宽度增加
lineWidth = lineWidth + childWidth;
//只判断一行最大高度,当换行时, 才增加高度
lineheight = Math.max(childHeight, lineheight);
}
// 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较
if (i == childCount - 1) {
width = Math.max(width, lineWidth);
height += lineheight;
}
}
//根据模式来设置ViewGroup的高度和宽度
setMeasuredDimension(
widthMode==MeasureSpec.EXACTLY ? widthSpec : width,
heightMode==MeasureSpec.EXACTLY ? heightSpec : height);
}
/**
* 设置childView的位置
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.i("tag", "onLayout");
//如果是warp_content情况下, 记录高度和宽度
int height = 0;
//记录当前行的高度和宽度
int lineWidth =0;
int lineheight = 0;
//Layoutparams
MarginLayoutParams mlp = null;
//获取childview的数目
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childview = getChildAt(i);
//childview的区域
int cl = 0, ct = 0, cr = 0, cb = 0;
mlp = (MarginLayoutParams) childview.getLayoutParams();
//获取childview实际所占空间的高度和宽度
int childWidth = childview.getMeasuredWidth()+mlp.leftMargin+mlp.rightMargin;
int childHeight = childview.getMeasuredHeight()+mlp.topMargin+mlp.bottomMargin;
//如果lineWidth + chilWidth > viewGroup最大宽度, childView就会移到下一行
if(lineWidth + childWidth > getWidth()){//换行
height += lineheight;
lineWidth = childWidth;//当了下一行
cl = mlp.leftMargin;
ct = height + mlp.rightMargin;
}else {//同一行的话, 宽度增加
cl = lineWidth+mlp.leftMargin;
ct = height + mlp.topMargin;
lineWidth = lineWidth + childWidth;
//只判断一行最大高度,当换行时, 才增加高度
lineheight = Math.max(childHeight, lineheight);
}
cr = cl + childview.getMeasuredWidth();
cb = ct + childview.getMeasuredHeight();
childview.layout(cl, ct, cr, cb);
}
}
}
\
\
\