1.在FlowLayout的构造里面获取自定义属性,让其支持横向间距和纵向间距
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
horizontal_space = typedArray.getDimension(R.styleable.FlowLayout_width_space, 0);
vertical_space = typedArray.getDimension(R.styleable.FlowLayout_height_space, 0);
typedArray.recycle();
}
2.在OnMeasure()里面进行测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mLines.clear();
mCurrentLine = null;
//获取总的宽度
int width = MeasureSpec.getSize(widthMeasureSpec);
//计算最大的宽度
mMaxWidth = width - getPaddingRight() - getPaddingLeft();
//测量孩子
int childCount = this.getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
//测量完成之后将孩子添加 到管理行进行管理
if (mCurrentLine == null) {
//第一次添加孩子的时候
mCurrentLine = new Line(mMaxWidth,horizontal_space);
//添加孩子
mCurrentLine.addView(childView);
//.添加行
mLines.add(mCurrentLine);
}else{
//先判断行里是否可以继续添加孩子
if(mCurrentLine.canAddView(childView)){
mCurrentLine.addView(childView);
}else{
//换到下一行
mCurrentLine=new Line(mMaxWidth ,horizontal_space);
mCurrentLine.addView(childView);
mLines.add(mCurrentLine);
}
}
}
//完成了测量孩子,那么就来测量自己
//计算自己只需要测量自己的高度,宽度肯定回被填充
int height = getPaddingTop() + getPaddingBottom();
for(int i=0;i//所有的行高
height+=mLines.get(i).height;
}
//所有竖直方向的间距
height+=(mLines.size()-1)*vertical_space;
//测量
setMeasuredDimension(width,height);
}
3.在OnLayout()里面遍历获取所有的行,让行去指定孩子的位置,指定行的高度。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//这里只负责具体 高度位置,具体 的 宽度和子孩子的高度让具体 的行去管理
l=getPaddingLeft();
t=getPaddingTop();
for(int j=0 ;j//获取 行
Line line = mLines.get(j);
line.onLayout(t,l);
//更新高度
t+=line.height;
if(j!=mLines.size()-1){
//不是最后一条 添加间距
t+=vertical_space;
}
}
}
4.在内部类里面对孩子进行管理,包括添加子view,子view的位置摆放
/**
* 内部类,行管理器,管理每一行的孩子
*/
public static class Line {
private float horizontalSpace;
private int maxWidth;
//定义一个行的集合来装子View
List views = new ArrayList<>();
private int usedWidth;
private int height;
public Line(int maxWidth, float horizontalSpace) {
this.maxWidth = maxWidth;
this.horizontalSpace = horizontalSpace;
}
public void addView(View view) {
int childWidth = view.getMeasuredWidth();
int childHeight = view.getMeasuredHeight();
//更新行里面 的宽度与高度
if (views.size() == 0) {
//集合里面没有孩子的时候
if (childWidth > maxWidth) {
usedWidth = maxWidth;
height = childHeight;
} else {
usedWidth = childWidth;
height = childHeight;
}
} else {
usedWidth += childWidth + horizontalSpace;
height = childHeight > height ? childHeight : height;
}
//添加孩子到集合
views.add(view);
}
public boolean canAddView(View view) {
//集合里面添加孩子
if (views.size() == 0) {
return true;
}
//最后一个孩子的宽度大于剩余的宽度返回false
if (view.getMeasuredWidth() > (maxWidth - usedWidth - horizontalSpace)) {
return false;
}
return true;
}
/**
* 指定孩子的显示位子
*/
public void onLayout(int t, int l) {
//平分剩余的空间
int avg = (maxWidth - usedWidth) / views.size();
//循环指定孩子的位子
for (View view : views) {
//获取宽高
int measuredWidth = view.getMeasuredWidth();
int measuredHeight = view.getMeasuredHeight();
//重新测量
view.measure(MeasureSpec.makeMeasureSpec(measuredWidth + avg, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY));
//重新获取宽高
measuredWidth = view.getMeasuredWidth();
//指定左上右下
int top = t;
int left = l;
int right = measuredWidth + left;
int bottom = measuredHeight + top;
view.layout(left, top, right, bottom);
//跟新数据
l += measuredWidth + horizontalSpace;
}
}
}
package com.zyh.jsondemo;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* @创建者 Administrator
* @创建时间 2016/10/16 10:32
* @描述 ${TODO}
*/
public class FlowLayout extends ViewGroup {
/**
* 储存行的集合
*/
private List mLines = new ArrayList<>();
// 当前行的指针
private Line mCurrentLine;
//水平方向的间距
private float horizontal_space;
//垂直方向的间距
private float vertical_space;
private int mMaxWidth;
public FlowLayout(Context context) {
this(context, null);
}
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FlowLayout);
horizontal_space = typedArray.getDimension(R.styleable.FlowLayout_width_space, 0);
vertical_space = typedArray.getDimension(R.styleable.FlowLayout_height_space, 0);
typedArray.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mLines.clear();
mCurrentLine = null;
//获取总的宽度
int width = MeasureSpec.getSize(widthMeasureSpec);
//计算最大的宽度
mMaxWidth = width - getPaddingRight() - getPaddingLeft();
//测量孩子
int childCount = this.getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
//测量完成之后将孩子添加 到管理行进行管理
if (mCurrentLine == null) {
//第一次添加孩子的时候
mCurrentLine = new Line(mMaxWidth, horizontal_space);
//添加孩子
mCurrentLine.addView(childView);
//.添加行
mLines.add(mCurrentLine);
} else {
//先判断行里是否可以继续添加孩子
if (mCurrentLine.canAddView(childView)) {
mCurrentLine.addView(childView);
} else {
//换到下一行
mCurrentLine = new Line(mMaxWidth, horizontal_space);
mCurrentLine.addView(childView);
mLines.add(mCurrentLine);
}
}
}
//完成了测量孩子,那么就来测量自己
//计算自己只需要测量自己的高度,宽度肯定回被填充
int height = getPaddingTop() + getPaddingBottom();
for (int i = 0; i < mLines.size(); i++) {
//所有的行高
height += mLines.get(i).height;
}
//所有竖直方向的间距
height += (mLines.size() - 1) * vertical_space;
//测量
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//这里只负责具体 高度位置,具体 的 宽度和子孩子的高度让具体 的行去管理
l = getPaddingLeft();
t = getPaddingTop();
for (int j = 0; j < mLines.size(); j++) {
//获取 行
Line line = mLines.get(j);
line.onLayout(t, l);
//更新高度
t += line.height;
if (j != mLines.size() - 1) {
//不是最后一条 添加间距
t += vertical_space;
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
/**
* 内部类,行管理器,管理每一行的孩子
*/
public static class Line {
private float horizontalSpace;
private int maxWidth;
//定义一个行的集合来装子View
List views = new ArrayList<>();
private int usedWidth;
private int height;
public Line(int maxWidth, float horizontalSpace) {
this.maxWidth = maxWidth;
this.horizontalSpace = horizontalSpace;
}
public void addView(View view) {
int childWidth = view.getMeasuredWidth();
int childHeight = view.getMeasuredHeight();
//更新行里面 的宽度与高度
if (views.size() == 0) {
//集合里面没有孩子的时候
if (childWidth > maxWidth) {
usedWidth = maxWidth;
height = childHeight;
} else {
usedWidth = childWidth;
height = childHeight;
}
} else {
usedWidth += childWidth + horizontalSpace;
height = childHeight > height ? childHeight : height;
}
//添加孩子到集合
views.add(view);
}
public boolean canAddView(View view) {
//集合里面添加孩子
if (views.size() == 0) {
return true;
}
//最后一个孩子的宽度大于剩余的宽度返回false
if (view.getMeasuredWidth() > (maxWidth - usedWidth - horizontalSpace)) {
return false;
}
return true;
}
/**
* 指定孩子的显示位子
*/
public void onLayout(int t, int l) {
//平分剩余的空间
int avg = (maxWidth - usedWidth) / views.size();
//循环指定孩子的位子
for (View view : views) {
//获取宽高
int measuredWidth = view.getMeasuredWidth();
int measuredHeight = view.getMeasuredHeight();
//重新测量
view.measure(MeasureSpec.makeMeasureSpec(measuredWidth + avg, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY));
//重新获取宽高
measuredWidth = view.getMeasuredWidth();
//指定左上右下
int top = t;
int left = l;
int right = measuredWidth + left;
int bottom = measuredHeight + top;
view.layout(left, top, right, bottom);
//跟新数据
l += measuredWidth + horizontalSpace;
}
}
}
}
2.自定义属性
<resources>
<declare-styleable name="FlowLayout">
<attr name="width_space" format="dimension">attr>
<attr name="height_space" format="dimension">attr>
declare-styleable>
resources>
3.xml文件
<com.zyh.jsondemo.FlowLayout
android:id="@+id/fl"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:width_space="5dp"
app:height_space="5dp"
/>
4.MainActivity代码
public class MainActivity2 extends Activity{
private FlowLayout mFl;
/**
* 显示的文字
*/
private String[] mDatas = new String[]{"QQ",
"视频",
"放开那三国",
"电子书",
"酒店",
"单机",
"小说",
"斗地主",
"优酷",
"网游",
"WIFI万能钥匙",
"播放器",
"捕鱼达人2",
"机票",
"游戏",
"熊出没之熊大快跑",
"美图秀秀",
"浏览器",
"单机游戏",
"我的世界",
"电影电视",
"QQ空间",
"旅游",
"免费游戏",
"2048",
"刀塔传奇",
"壁纸",
"节奏大师",
"锁屏",
"装机必备",
"天天动听",
"备份",
"网盘",
"海淘网",
"大众点评",
"爱奇艺视频",
"腾讯手机管家",
"百度地图",
"猎豹清理大师",
"谷歌地图",
"hao123上网导航",
"京东",
"有你",
"万年历-农历黄历",
"支付宝钱包"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
initView();
initData();
}
private void initView() {
mFl = (FlowLayout) findViewById(R.id.fl);
}
private void initData() {
Random random = new Random();
for(String name:mDatas){
final TextView view = new TextView(this);
view.setText(name);
view.setTextColor(Color.WHITE);
view.setPadding(8, 8, 8, 8);
view.setGravity(Gravity.CENTER);
view.setTextSize(20);
// 设置点击事件
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity2.this, view.getText().toString(), Toast.LENGTH_SHORT).show();
}
});
// 设置彩色背景
GradientDrawable normalDrawable = new GradientDrawable();
normalDrawable.setShape(GradientDrawable.RECTANGLE);
int a = 255;
int r = 50 + random.nextInt(150);
int g = 50 + random.nextInt(150);
int b = 50 + random.nextInt(150);
normalDrawable.setColor(Color.argb(a, r, g, b));
// 设置按下的灰色背景
GradientDrawable pressedDrawable = new GradientDrawable();
pressedDrawable.setShape(GradientDrawable.RECTANGLE);
pressedDrawable.setColor(Color.GRAY);
// 背景选择器
StateListDrawable stateDrawable = new StateListDrawable();
stateDrawable.addState(new int[]{android.R.attr.state_pressed}, pressedDrawable);
stateDrawable.addState(new int[]{}, normalDrawable);
// 设置背景选择器到TextView上
view.setBackground(stateDrawable);
mFl.addView(view);
}
}
}