概述
大家都知道,java 图形编程有一个布局叫FlowLayout,即流式布局。但Android没有,所以今天教大家写一个Android版的流式布局。
代码
核心代码(FlowLayout.java)如下:
package com.example.flowlayout;MainActivity.java的代码如下:
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class FlowLayout extends ViewGroup {
//记录每个View的位置
private List<ChildPos> mChildPos = new ArrayList<ChildPos>();
private class ChildPos {
int left, top, right, bottom;
public ChildPos(int left, int top, int right, int bottom) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
}
public FlowLayout(Context context) {
this(context, null);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 最终调用这个构造方法
* @param context 上下文
* @param attrs xml属性集合
* @param defStyle Theme中定义的style
*/
public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 测量宽度和高度
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//获取流式布局的宽度和模式
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
//获取流式布局的高度和模式
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//使用wrap_content的流式布局的最终宽度和高度
int width = 0, height = 0;
//记录每一行的宽度和高度
int lineWidth = 0, lineHeight = 0;
//得到内部元素的个数
int count = getChildCount();
mChildPos.clear();
for (int i = 0; i < count; i++) {
//获取对应索引的view
View child = getChildAt(i);
//测量子view的宽和高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//子view占据的宽度
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
//子view占据的高度
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
//换行
if (lineWidth + childWidth > widthSize - getPaddingLeft() - getPaddingRight()) {
//取最大的行宽为流式布局宽度
width = Math.max(width, lineWidth);
//叠加行高得到流式布局高度
height += lineHeight;
//重置行宽度为第一个View的宽度
lineWidth = childWidth;
//重置行高度为第一个View的高度
lineHeight = childHeight;
//记录位置
mChildPos.add(new ChildPos(
getPaddingLeft() + lp.leftMargin,
getPaddingTop() + height + lp.topMargin,
getPaddingLeft() + childWidth - lp.rightMargin,
getPaddingTop() + height + childHeight - lp.bottomMargin));
} else { //不换行
//记录位置
mChildPos.add(new ChildPos(
getPaddingLeft() + lineWidth + lp.leftMargin,
getPaddingTop() + height + lp.topMargin,
getPaddingLeft() + lineWidth + childWidth - lp.rightMargin,
getPaddingTop() + height + childHeight - lp.bottomMargin));
//叠加子View宽度得到新行宽度
lineWidth += childWidth;
//取当前行子View最大高度作为行高度
lineHeight = Math.max(lineHeight, childHeight);
}
//最后一个控件
if (i == count - 1)
{
width = Math.max(lineWidth, width);
height += lineHeight;
}
}
setMeasuredDimension(
widthMode == MeasureSpec.EXACTLY ? widthSize : width + getPaddingLeft() + getPaddingRight(),
heightMode == MeasureSpec.EXACTLY ? heightSize : height + getPaddingTop() + getPaddingBottom());
}
/**
* 让ViewGroup能够支持margin属性
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
/**
* 设置每个View的位置
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
ChildPos pos = mChildPos.get(i);
//设置View的左边、上边、右边底边位置
child.layout(pos.left, pos.top, pos.right, pos.bottom);
}
}
}
package com.example.flowlayout;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity {
//标签名称
private String mTvNames[] = {"动漫", "钉宫理惠", "灼眼的夏娜", "绯弹的亚里亚", "零之使魔", "夕阳染红的街道"};
//流式布局
private FlowLayout mFlowLayout;
//用于动态添加TextView到流式布局的按钮
private Button mBtn;
//接收新添加TextView的名称
private EditText mEdit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFlowLayout = (FlowLayout) findViewById(R.id.flow_layout);
init();
mEdit = (EditText) findViewById(R.id.edit);
mBtn = (Button) findViewById(R.id.btn);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String s = mEdit.getText().toString();
addTextView(s);
}
});
}
public void init() {
//遍历标签名称数组
for (String s : mTvNames) {
addTextView(s);
}
}
public void addTextView(String tvName) {
//加载TextView并设置名称,并设置名称
TextView tv = (TextView) LayoutInflater.from(this).inflate(R.layout.tv, mFlowLayout, false);
tv.setText(tvName);
//把TextView加入流式布局
mFlowLayout.addView(tv);
}
}
布局文件(activity_main.xml)如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.flowlayout.FlowLayout
android:id="@+id/flow_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="10dp" >
</com.example.flowlayout.FlowLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="@+id/edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="请输入新标签名称" />
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="添加" />
</LinearLayout>
</LinearLayout>
每个标签的布局(tv.xml)如下:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/bg"
android:text="hello"
android:textColor="#FFF"
android:textSize="20sp" >
</TextView>
标签背景效果(bg.xml)如下:
<?xml version="1.0" encoding="utf-8"?>效果展示:
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#666666" />
<corners android:radius="15dp" />
<padding
android:bottom="5dp"
android:left="10dp"
android:right="10dp"
android:top="5dp" />
</shape>