继承ViewGroup实现一个简单的自动换行流布局
可实现同比等宽、自适应宽度两种样式
源码:https://github.com/Beckboy/FlowLayoutTextViewDome.git
前段时间在工作中有遇到瀑布流布局,给自己造成一定困扰。
通过这段时间的实用和总结,自己也相应的封装了一套,会在接下来的一段时间不断完善,方便今后能直接使用。
步骤
- 继承ViewGroup;
- 复写onMeasure(),计算每个子view的宽和高,根据全部子view的宽和高,获取整个布局的宽和高;
- 复写onLayout(),计算每个子view的位置并设置布局;
- 提供获取行数的方法getLines();
- 提供获取当前行高度的方法getCloseHeight();
实现
- 继承ViewGroup
public class FlowLayout extends ViewGroup
- 初始化数据源
//存储当前ViewGroup的所有view,在Activity中直接用addView(View view)添加;
private List<List<View>> mAllViews = new ArrayList<List<View>>();
//把每一行数据的高度存储到List
private List<Integer> mHeightList = new ArrayList<Integer>();
- 复写onLayout
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mAllViews.clear();
mHeightList.clear();
int width = getWidth();
int overWidth = 0; //每一行view所占据的总宽度margin,padding
int overHeight = 0; //每一个view所占据的总高度
List<View> lineViews = new ArrayList<View>();
int viewCount = getChildCount(); //所有View的总数量
for (int i = 0;i < viewCount ; i++){
View child = getChildAt(i); //每一个子View
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childViewWidth = child.getMeasuredWidth();
int childViewHeight = child.getMeasuredHeight();
//当前View超过一行时,换行处理
if (childViewWidth + overWidth + lp.leftMargin + lp.rightMargin > width - getPaddingLeft() - getPaddingRight()){ //换行判断
mHeightList.add(overHeight);
mAllViews.add(lineViews);
//重置行宽和行高
overWidth = 0;
overHeight = childViewHeight + lp.topMargin + lp.bottomMargin;
//重置我们的View集合
lineViews = new ArrayList<View>();
}
overWidth += childViewWidth + lp.leftMargin + lp.rightMargin;
overHeight = Math.max(overHeight,childViewHeight + lp.topMargin +lp.bottomMargin);
lineViews.add(child);
}
//处理最后一行
mHeightList.add(overHeight);
mAllViews.add(lineViews);
//设置每一个子View的位置
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
//当前行数
int linesNum = mAllViews.size();
for (int i = 0; i < linesNum; i++){
//当前行的所有view
lineViews = mAllViews.get(i);
overHeight = mHeightList.get(i);
for (int j = 0;j < lineViews.size();j++){
View child = lineViews.get(j);
//判断当前View的状态
if (child.getVisibility() == View.GONE){
continue;
}
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int lc = childLeft + lp.leftMargin;
int tc = childTop + lp.topMargin;
int rc = lc + child.getMeasuredWidth();
int bc = tc + child.getMeasuredHeight();
child.layout(lc,tc,rc,bc); //为子View进行布局
childLeft +=child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
}
childLeft = getPaddingLeft();
childTop += overHeight;
}
}
-复写onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//wrap_content模式下的宽度和高度
int width = 0;
int height = 0;
//记录每一行的宽度和高度
int lineWidth = 0;
int lineHeight = 0;
//获取内容的View元素个数
int cCount = getChildCount();
for (int i = 0; i < cCount; i++){
View child = getChildAt(i);
//测量子View的宽度和高度
measureChild(child,widthMeasureSpec,heightMeasureSpec);
//得到LayoutParams
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 > sizeWidth - getPaddingLeft() - getPaddingRight()){
//对比得到的最大宽度
width = Math.max(width,lineWidth);
//重置
lineWidth = childWidth;
//记录行高
height += lineHeight;
lineHeight = childHeight;
}else {
lineWidth += childWidth;
//获取当前行最大的高度
lineHeight = Math.max(lineHeight,childHeight);
}
if (i == cCount - 1){ //如果是最后一个控件
width = Math.max(lineWidth,width);
height +=lineHeight;
}
}
setMeasuredDimension(
modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(),
modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom());
}
- 获取指定行数的高度
public int getCloseHeight(int lines){
int hh = 0;
if (lines > mHeightList.size()){
lines = mHeightList.size();
}
for (int i = 0; i < lines;i++){
hh += mHeightList.get(i);
}
return hh;
}
- 获取总行数
public int getLines(){
return mAllViews.size();
}
使用
- 初始化数据源
// 数据源
private String[] collegData = new String[]{"清华大学","北京大学","复旦大学","浙江大学","南开大学","同济大学"
,"苏州大学","吉林大学","哈佛大学","斯坦福大学","麻省理工大学","斯坦福大学","加州理工学院"};
- 初始化子布局
//宽高
private int width = ViewGroup.LayoutParams.WRAP_CONTENT;
private int height = ViewGroup.LayoutParams.WRAP_CONTENT;
- 在代码中动态添加子view
/**
* 初始化瀑布流列表
*/
private void initData() {
for (String colleg : collegData){
loadFlowCircleList(colleg);
}
}
/**
* 加载流标签
*/
private void loadFlowCircleList(String colleg) {
CheckBox cbTag = (CheckBox) getLayoutInflater().inflate(R.layout.item_topic_tag, mFlow, false);
cbTag.setBackgroundDrawable(getResources().getDrawable(R.drawable.selector_bg_check_circle));
ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(width,height);
lp.leftMargin = 20; //左间距
lp.rightMargin = 20; //右间距
lp.topMargin = 40; //上间距
cbTag.setLayoutParams(lp);
cbTag.setText(colleg);
mFlow.addView(cbTag);
}
- 设置子view的点击事件
public void onclick(View view) {
mFlow.removeAllViews();
switch (view.getId()){
case R.id.btn_left: //加载自适应流布局
width = ViewGroup.LayoutParams.WRAP_CONTENT;
break;
case R.id.btn_right: //加载等宽流布局
int column = 3; //列数
int max_width = getWindowManager().getDefaultDisplay().getWidth(); //获取屏幕宽度
width = (max_width - 40 * column)/column;
break;
}
initData();
}
- 布局文件
FlowLayout布局文件
<com.example.hunter_j.flowlayoutdemo.view.FlowLayout
android:id="@+id/flowlayouot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"/>
子view布局文件
<CheckBox
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="54px"
android:paddingTop="14px"
android:paddingBottom="14px"
android:paddingLeft="20px"
android:paddingRight="20px"
android:gravity="center"
android:textSize="13sp"
android:button="@null"
android:textColor="@drawable/selector_color_txt">
</CheckBox>
xml设置子view字体、背景变色自动
<!--drawable资源文件:selector_bg_check_circle-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="false" >
<shape android:shape="rectangle">
<solid android:color="#f3f6f7"/>
<corners android:radius="50px"/>
</shape>
</item>
<item android:state_checked="true">
<shape android:shape="rectangle">
<solid android:color="#ffc84a"/>
<corners android:radius="50px"/>
</shape>
</item>
</selector>
<!--drawable资源文件:selector_color_txt-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="false" android:state_enabled="true" android:state_pressed="false"
android:color="@drawable/color_cdac8d" />
<item android:state_enabled="false" android:color="@drawable/color_cdac8d" />
<item android:state_pressed="true" android:color="@drawable/color_f9" />
<item android:state_focused="true" android:color="@drawable/color_f9" />
</selector>
<!--color资源文件-->
<resources>
<!-- 字体颜色的选择器 -->
<drawable name="color_f9">#F9F9F9</drawable>
<drawable name="color_cdac8d">#CDAC8D</drawable>
</resources>
源码:
源码:https://github.com/Beckboy/FlowLayoutTextViewDome.git
新手上路,欢迎同学们吐槽评论,如果你觉得对你有帮助
那么就留个言或者点下喜欢吧(^-^)
网友评论