一个简单的标签流布局

作者: Null_Point | 来源:发表于2018-01-25 12:31 被阅读0次

继承ViewGroup实现一个简单的自动换行流布局
可实现同比等宽、自适应宽度两种样式
源码:https://github.com/Beckboy/FlowLayoutTextViewDome.git

前段时间在工作中有遇到瀑布流布局,给自己造成一定困扰。
通过这段时间的实用和总结,自己也相应的封装了一套,会在接下来的一段时间不断完善,方便今后能直接使用。

标签流.gif

步骤

  1. 继承ViewGroup;
  2. 复写onMeasure(),计算每个子view的宽和高,根据全部子view的宽和高,获取整个布局的宽和高;
  3. 复写onLayout(),计算每个子view的位置并设置布局;
  4. 提供获取行数的方法getLines();
  5. 提供获取当前行高度的方法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

新手上路,欢迎同学们吐槽评论,如果你觉得对你有帮助
那么就留个言或者点下喜欢吧(^-^)

相关文章

  • 2018-09-19 Day23 CSS布局

    一、标准流 标准流:浏览器对标签默认的布局方式就是标准流。标准流布局原则: 块级标签:a、块级标签一个占一行(不管...

  • 2018-09-19-day3总结

    一、标准流 标准流:浏览器对标签默认的布局方式就是标准流标准流布局原则:块级: a,块级标签一个占一行(不管标签的...

  • 2018-09-19-day23总结

    1.浮动属性 标准流:浏览器对标签默认的布局方式就是标准流块级: 2.标准流布局 标准流布局原则:块级标签一个占一...

  • day3 CSS布局

    1、标准流 1.什么是标准流 标签在没有添加布局相关的样式的时候,在浏览器中默认的布局方式块级标签在标准流中是一个...

  • CSS布局

    一.标准流和display 1.标准流:浏览器对标签默认的布局方式就是标准流2.标准流布局原则块级:a.块级标签一...

  • Day_3-css布局

    一、标准流 1.什么是标准流标签在没有添加布局相关的样式时,在浏览器中默认的布局方式块级标签:一个占一行(不管宽度...

  • Day03-CSS布局

    1.什么是标准流标签在没有添加布局相关的样式的时候,在浏览器中默认的布局方式块级标签在标准流中是一个占一行(不管宽...

  • day23总结

    1.什么是标准流标签在没有添加布局相关的样式的时候,在浏览器中默认的布局方式  块级标签在标准流中是一个占一行(不...

  • 2019-01-23day3学习总结

    1.什么是标准流标签在没有添加布局相关的样式的时候,在浏览器中默认的布局方式块级标签在标准流中是一个占一行(不管宽...

  • 2018-09-19 day3 css布局

    1.标准流和display属性 1.1标准流 1.标准流:浏览器对标签默认的布局方式就是标准流2.标准流布局原则:...

网友评论

    本文标题:一个简单的标签流布局

    本文链接:https://www.haomeiwen.com/subject/fqpaoxtx.html