import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* 作者: willkong on 2017/11/15.
* 作用:流式布局
*/
public class FlowLayout extends ViewGroup{
public FlowLayout(Context context) {
this(context,null);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//能够设置当前布局的宽度和高度
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取设置的宽高的模式和具体的值
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//如果用户使用的至多模式,那么使用如下的两个变量计算真实的宽高值
int width = 0;
int height= 0;
//每一行的宽度和高度
int lineWidth = 0;
int lineHeight = 0;
//获取子视图
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
//只有调用了如下的方法,方可计算子视图的测量的宽高
measureChild(childView,widthMeasureSpec,heightMeasureSpec);
//获取子视图的宽高
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
//要想保证可以获取子视图的边距参数对象,必须重写generateLayoutParams()
MarginLayoutParams mp = (MarginLayoutParams) childView.getLayoutParams();
if (lineWidth + childWidth + mp.leftMargin+mp.rightMargin<widthSize){//不换行
lineWidth += childWidth + mp.leftMargin+mp.rightMargin;
lineHeight = Math.max(lineHeight,childHeight+mp.topMargin+mp.bottomMargin);
}else {//换行
width = Math.max(width,lineWidth);
height+=lineHeight;
//重置更新一行的宽高
lineWidth = childWidth + mp.leftMargin+mp.rightMargin;
lineHeight = childHeight+mp.topMargin+mp.bottomMargin;
}
//最后一个元素--如果最后一个不换行,宽高没有计算,不准确,所以需要下面的代码执行计算
//不管有没有换行,最后一行都要加上,不然,最后一行就没有显示了。
if (i==childCount-1){
width = Math.max(width,lineWidth);
height+=lineHeight;
}
}
//设置当前流式布局的宽高
setMeasuredDimension(widthMode==MeasureSpec.EXACTLY?widthSize:width,heightMode==MeasureSpec.EXACTLY?heightSize:height);
}
//布局:给每一个子视图指定显示的位置,childView.layout()
private List<List<View>>allViews = new ArrayList<>();//每一行的子视图的集合构成的集合
private List<Integer>allHeights = new ArrayList<>();//每一行的高度构成的集合。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//一、给两个集合添加元素
//每一行的宽度和高度
int lineWidth = 0;
int lineHeight = 0;
//提供一个集合,保存一行childView
List<View>lineList = new ArrayList<>();
//获取布局的宽度
int width = this.getMeasuredWidth();
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
//获取视图的测量宽高、边距
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
MarginLayoutParams mp = (MarginLayoutParams) childView.getLayoutParams();
if (lineWidth+childView.getMeasuredWidth()+mp.leftMargin+mp.rightMargin<=width){//不换行
lineList.add(childView);
lineWidth+=childWidth+mp.leftMargin+mp.rightMargin;
lineHeight = Math.max(lineHeight,childHeight+mp.topMargin+mp.bottomMargin);
}else {//换行
allViews.add(lineList);
allHeights.add(lineHeight);
lineWidth = childWidth+mp.leftMargin+mp.rightMargin;
lineHeight = childHeight+mp.topMargin+mp.bottomMargin;
lineList = new ArrayList<>();
lineList.add(childView);
}
if (i == childCount-1){//如果是最后一个元素
allViews.add(lineList);
allHeights.add(lineHeight);
}
}
//二、给每一个子视图指定显示的位置
int x=0;
int y=0;
for (int i = 0; i < allViews.size(); i++) {//每遍历一次,对应一行元素
List<View>lineViews = allViews.get(i);//取出当前行构成的集合
for (int j = 0; j < lineViews.size(); j++) {
View childView = lineViews.get(j);
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
MarginLayoutParams mp = (MarginLayoutParams) childView.getLayoutParams();
int left = x + mp.leftMargin;
int top = y + mp.topMargin;
int right = left + childView.getMeasuredWidth();
int bottom = top + childView.getMeasuredHeight();
childView.layout(left,top,right,bottom);
x+= childWidth + mp.leftMargin + mp.rightMargin;
}
y += allHeights.get(i);
x = 0;
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
MarginLayoutParams mp = new MarginLayoutParams(getContext(), attrs);
return mp;
}
}
<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.willkong.p2pclient.fragment.ProductListFragment">
<com.willkong.p2pclient.ui.FlowLayout
android:id="@+id/flow_hot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_blue_light">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="你是我心内的一首歌"
android:layout_margin="10dp"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="涛声依旧"
android:layout_margin="10dp"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="夜夜"
android:layout_margin="10dp"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="月落乌啼霜满天"
android:layout_margin="10dp"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="哇塞"
android:layout_margin="10dp"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="word天哪"
android:layout_margin="10dp"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="你咋不上天呢"
android:layout_margin="10dp"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="你想怎样"
android:layout_margin="10dp"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="夜曲"
android:layout_margin="10dp"
android:textSize="20sp" />
</com.willkong.p2pclient.ui.FlowLayout>
</LinearLayout>
需要注意的是,要想保证可以获取子视图的边距参数对象,必须重写generateLayoutParams()方法。
动态加载数据:
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.loopj.android.http.RequestParams;
import com.willkong.p2pclient.R;
import com.willkong.p2pclient.common.BaseFragment;
import com.willkong.p2pclient.ui.FlowLayout;
import com.willkong.p2pclient.util.DrawUtils;
import com.willkong.p2pclient.util.UIUtils;
import java.util.Random;
import butterknife.Bind;
public class ProductHotFragment extends BaseFragment {
@Bind(R.id.flow_hot)
FlowLayout flowHot;
private String[] datas = new String[]{"新手福利计划", "财神道90天计划", "硅谷计划", "30天理财计划", "180天理财计划", "月月升", "中情局投资商业经营", "大学老师购买车辆", "屌丝下海经商计划", "美人鱼影视拍摄投资", "Android培训老师自己周转", "养猪场扩大经营",
"旅游公司扩大规模", "摩托罗拉洗钱计划", "铁路局回款计划", "屌丝迎娶白富美计划"
};
@Override
protected RequestParams getParams() {
return null;
}
@Override
protected String getUrl() {
return null;
}
@Override
protected void initData(String content) {
for (int i = 0; i < datas.length; i++) {
final TextView tv = new TextView(getContext());
tv.setText(datas[i]);
ViewGroup.MarginLayoutParams mp = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mp.leftMargin = UIUtils.dp2px(10);
mp.topMargin = UIUtils.dp2px(10);
mp.rightMargin = UIUtils.dp2px(10);
mp.bottomMargin = UIUtils.dp2px(10);
tv.setLayoutParams(mp);//设置边距
int padding = UIUtils.dp2px(5);
tv.setPadding(padding,padding,padding,padding);//设置内边距
tv.setTextSize(UIUtils.dp2px(5));
Random random = new Random();
int red = random.nextInt(211);
int green = random.nextInt(211);
int blue = random.nextInt(211);
//设置背景
// tv.setBackground(DrawUtils.getDrawable(Color.rgb(red,green,blue),UIUtils.dp2px(5)));
//设置具有选择器的背景
tv.setBackground(DrawUtils.getSelector(DrawUtils.getDrawable(Color.rgb(red,green,blue),UIUtils.dp2px(5)),DrawUtils.getDrawable(Color.WHITE,UIUtils.dp2px(5))));
//设置textView是可点击的
// tv.setClickable(true);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
UIUtils.toast(tv.getText().toString().trim(),false);
}
});
flowHot.addView(tv);
}
}
@Override
protected void initTitle() {
}
@Override
public int getLayoutId() {
return R.layout.fragment_product_hot;
}
}
DrawUtils.class
import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
public class DrawUtils {
@SuppressLint("WrongConstant")
public static Drawable getDrawable(int rgb, int radius){
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setColor(rgb);
gradientDrawable.setGradientType(GradientDrawable.RECTANGLE);//矩形
gradientDrawable.setCornerRadius(radius);//四周圆角半径
gradientDrawable.setStroke(UIUtils.dp2px(1),rgb);//边框厚度与颜色
return gradientDrawable;
}
public static StateListDrawable getSelector(Drawable normalDrawable,Drawable pressDrawable){
StateListDrawable stateListDrawable = new StateListDrawable();
//给当前的颜色选择器添加选中图片选中指向状态,未选中图片指向状态
stateListDrawable.addState(new int[]{android.R.attr.state_enabled,android.R.attr.state_pressed},pressDrawable);
stateListDrawable.addState(new int[]{android.R.attr.state_enabled},normalDrawable);
//设置默认状态
stateListDrawable.addState(new int[]{},normalDrawable);
return stateListDrawable;
}
}