当前位置: 移动技术网 > 移动技术>移动开发>Android > Android自定义ViewGroup之实现FlowLayout流式布局

Android自定义ViewGroup之实现FlowLayout流式布局

2019年07月24日  | 移动技术网移动技术  | 我要评论

整理总结自鸿洋的博客:
 一、flowlayout介绍
 所谓flowlayout,就是控件根据viewgroup的宽,自动的往右添加,如果当前行剩余空间不足,则自动添加到下一行。有点像所有的控件都往左飘的感觉,第一行满了,往第二行飘~所以也叫流式布局。android并没有提供流式布局,但是某些场合中,流式布局还是非常适合使用的,比如关键字标签,搜索热词列表等,比如下图: 

github上已有现成的flowlayout,本文是从无到有去制作。

二、制作分析 

1、对于flowlayout,需要指定的layoutparams,我们目前只需要能够识别margin即可,即使用marginlayoutparams.
2、onmeasure中计算所有childview的宽和高,然后根据childview的宽和高,计算自己的宽和高。(当然,如果不是wrap_content,直接使用父viewgroup传入的计算值即可)
3、onlayout中对所有的childview进行布局。 

三、代码 
1、mainactivity.java

 public class mainactivity extends activity {

 @override
 protected void oncreate(bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.activity_main);
//  setcontentview(r.layout.activity_main2);
//  setcontentview(r.layout.activity_main3);
 }
} 

 2、customviewgroup.java

public class customviewgroup extends viewgroup {

 private final string tag = getclass().getsimplename();

 public customviewgroup(context context) {
  super(context);
 }

 public customviewgroup(context context, attributeset attrs) {
  super(context, attrs);
 }

 public customviewgroup(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
 }

 /**
  * 一、重写generatelayoutparams,确定该viewgroup的layoutparams
  * 返回marginlayoutparams的实例,这样就为我们的viewgroup指定了其layoutparams为marginlayoutparams
  */
 @override
 public layoutparams generatelayoutparams(attributeset attrs) {
  return new marginlayoutparams(getcontext(), attrs);
 }

 /**
  * 二、计算所有childview的宽度和高度 然后根据childview的计算结果,设置自己的宽和高
  */
 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
  super.onmeasure(widthmeasurespec, heightmeasurespec);
  //1、获得此viewgroup上级容器为其推荐的宽和高,以及计算模式
  int widthmode = measurespec.getmode(widthmeasurespec);
  int heightmode = measurespec.getmode(heightmeasurespec);
  int sizewidth = measurespec.getsize(widthmeasurespec);
  int sizeheight = measurespec.getsize(heightmeasurespec);

  // 2、如果viewgroup布局是wrap_content时,根据childview的尺寸,计算容器的宽和高
  int width = 0;//viewgroup的宽度
  int height = 0;//viewgroup的高度
  int linewidth = 0;//childview所占据的当前行总宽度
  int lineheight = 0;//childview所占据的各行总高度
  int ccount = getchildcount();////childview的数量
  for(int i=0; i<ccount; i++){//遍历每个childview
   view childview = getchildat(i);
   measurechild(childview, widthmeasurespec, heightmeasurespec);// 测量当前child的宽和高
   marginlayoutparams mlp = (marginlayoutparams) childview.getlayoutparams();
   int cwidth = childview.getmeasuredwidth() + mlp.leftmargin + mlp.rightmargin;
   int cheight = childview.getmeasuredheight() + mlp.topmargin + mlp.bottommargin;
   if(linewidth + cwidth > sizewidth){//如果加入当前childview后超出最大宽度,width取最大高度,累加lineheight,然后开启新行
    width = math.max(linewidth, cwidth);
    height += lineheight;
    linewidth = cwidth;
   }else{//如果加入当前childview后小于最大宽度,则累加linewidthheight lineheight取最大高度
    linewidth += cwidth;
    height = math.max(lineheight, cheight);
   }
   if(i == ccount-1){// 如果是最后一个childview,则将当前记录的最大宽度和当前linewidth做比较
    width = math.max(linewidth, cwidth);
    height += lineheight;
   }
  }

  //3、如果是wrap_content设置为我们计算的值;否则直接设置为父容器计算的值
  setmeasureddimension(
    (widthmode == measurespec.exactly) ? sizewidth : width,
    (heightmode == measurespec.exactly) ? sizeheight : height
  );
 }

 /**
  * 三、重写onlayout,对其所有childview进行定位(设置childview的绘制区域)
  */
 private list<list<view>> allchildviews = new arraylist<list<view>>();//存储所有的childview,按行记录
 private list<integer> maxlineheight = new arraylist<integer>();//存储每行的最大高度值
 @override
 protected void onlayout(boolean changed, int l, int t, int r, int b) {
  allchildviews.clear();
  maxlineheight.clear();

  int width = getwidth();//每行的最大宽度
  int linewidth = 0;//每行的即时宽度
  int lineheight = 0;//每行的即时高度

  list<view> linechildviews = new arraylist<view>();//存储每行所有的childview

  int ccount = getchildcount();
  for(int i=0; i<ccount; i++){//遍历所有childview
   view childview = getchildat(i);
   marginlayoutparams mlp = (marginlayoutparams) childview.getlayoutparams();
   int cwidth = childview.getmeasuredwidth();
   int cheight = childview.getmeasuredheight();
   if(linewidth + cwidth + mlp.leftmargin + mlp.rightmargin > width){//如果需要换行
    maxlineheight.add(lineheight);// 存储这一行最大高度
    allchildviews.add(linechildviews);// 将当前行的childview保存,然后开启新的arraylist保存下一行的childview
    linechildviews = new arraylist<view>();
    linewidth = 0;// 重置行宽
   }else{//如果不需要换行
    linewidth += cwidth + mlp.leftmargin + mlp.rightmargin;//即时宽度累加
    lineheight = math.max(lineheight,cheight + mlp.topmargin + mlp.bottommargin );//即时高度取最大值
    linechildviews.add(childview);//把当前childview存入这一行的集合
   }
  }
  // 记录最后一行
  maxlineheight.add(lineheight);
  allchildviews.add(linechildviews);

  int left = 0;//左坐标
  int top = 0;//上坐标
  int linenums = allchildviews.size();// 得到总行数
  for (int i = 0; i < linenums; i++) {
   linechildviews = allchildviews.get(i);// 取得每一行的所有的views
   lineheight = maxlineheight.get(i);// 取得当前行的最大高度

   log.e(tag, "第" + i + "行 :" + linechildviews.size() + " , " + linechildviews);
   log.e(tag, "第" + i + "行, :" + lineheight);

   // 遍历当前行所有的view 
   for (int j = 0; j < linechildviews.size(); j++) {
    view childview = linechildviews.get(j);//取得childview
    if (childview.getvisibility() == view.gone) {
     continue;
    }
    marginlayoutparams mlp = (marginlayoutparams) childview.getlayoutparams();
    //计算childview的left,top,right,bottom
    int lc = left + mlp.leftmargin;
    int tc = top + mlp.topmargin;
    int rc = lc + childview.getmeasuredwidth();
    int bc = tc + childview.getmeasuredheight();

    log.e(tag, childview + " , l = " + lc + " , t = " + t + " , r =" + rc + " , b = " + bc);

    childview.layout(lc, tc, rc, bc);//设置这个childview的位置
    left += childview.getmeasuredwidth() + mlp.rightmargin + mlp.leftmargin;//左坐标累加
   }
   left = 0;//开始新的一行,左坐标重置
   top += lineheight;//开始新的一行,上坐标累加
  }
 }
}

 3、activity_main.xml

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:background="#e1e6f6"
 android:orientation="vertical">

 <com.cctvjiatao.customviewgroupflowlayout.view.customviewgroup
  android:layout_width="fill_parent"
  android:layout_height="wrap_content">

  <textview
   style="@style/text_flag_01"
   android:text="welcome" />

  <textview
   style="@style/text_flag_01"
   android:text="it工程师" />

  <textview
   style="@style/text_flag_01"
   android:text="学习ing" />

  <textview
   style="@style/text_flag_01"
   android:text="恋爱ing" />

  <textview
   style="@style/text_flag_01"
   android:text="挣钱ing" />

  <textview
   style="@style/text_flag_01"
   android:text="努力ing" />

  <textview
   style="@style/text_flag_01"
   android:text="i thick i can" />
 </com.cctvjiatao.customviewgroupflowlayout.view.customviewgroup>

</linearlayout>

 4、activity_main2.xml

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:background="#e1e6f6"
 android:orientation="vertical">

 <com.cctvjiatao.customviewgroupflowlayout.view.customviewgroup
  android:layout_width="fill_parent"
  android:layout_height="wrap_content">

  <textview
   style="@style/text_flag_01"
   android:text="welcome" />

  <textview
   style="@style/text_flag_01"
   android:text="it工程师" />

  <textview
   style="@style/text_flag_01"
   android:text="学习ing" />

  <textview
   style="@style/text_flag_01"
   android:text="恋爱ing" />

  <textview
   style="@style/text_flag_01"
   android:text="挣钱ing" />

  <textview
   style="@style/text_flag_01"
   android:text="努力ing" />

  <textview
   style="@style/text_flag_01"
   android:text="i thick i can" />
 </com.cctvjiatao.customviewgroupflowlayout.view.customviewgroup>


 <com.cctvjiatao.customviewgroupflowlayout.view.customviewgroup
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_margintop="20dp">

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_02"
   android:text="welcome"
   android:textcolor="#888888" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_02"
   android:text="it工程师"
   android:textcolor="#888888" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_02"
   android:text="学习ing"
   android:textcolor="#888888" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_02"
   android:text="恋爱ing"
   android:textcolor="#888888" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_02"
   android:text="挣钱ing"
   android:textcolor="#888888" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_02"
   android:text="努力ing"
   android:textcolor="#888888" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_02"
   android:text="i thick i can"
   android:textcolor="#888888" />
 </com.cctvjiatao.customviewgroupflowlayout.view.customviewgroup>

 <com.cctvjiatao.customviewgroupflowlayout.view.customviewgroup
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_margintop="20dp">

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_03"
   android:text="welcome"
   android:textcolor="#43bbe7" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_03"
   android:text="it工程师"
   android:textcolor="#43bbe7" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_03"
   android:text="学习ing"
   android:textcolor="#43bbe7" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_03"
   android:text="恋爱ing"
   android:textcolor="#43bbe7" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_03"
   android:text="挣钱ing"
   android:textcolor="#43bbe7" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_03"
   android:text="努力ing"
   android:textcolor="#43bbe7" />

  <textview
   style="@style/text_flag_01"
   android:background="@drawable/flag_03"
   android:text="i thick i can"
   android:textcolor="#43bbe7" />
 </com.cctvjiatao.customviewgroupflowlayout.view.customviewgroup>

</linearlayout> 

 5、activity_main3.xml

<com.cctvjiatao.customviewgroupflowlayout.view.customviewgroup xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="200dp"
 android:layout_height="wrap_content"
 android:background="#ffffff">

 <textview
  style="@style/text_flag_01"
  android:background="@drawable/flag_04"
  android:text="welcome"
  android:textcolor="#323232" />

 <textview
  style="@style/text_flag_01"
  android:background="@drawable/flag_04"
  android:text="it工程师"
  android:textcolor="#323232" />

 <textview
  style="@style/text_flag_01"
  android:background="@drawable/flag_04"
  android:text="学习ing"
  android:textcolor="#323232" />

 <textview
  style="@style/text_flag_01"
  android:background="@drawable/flag_04"
  android:text="恋爱ing"
  android:textcolor="#323232" />

 <textview
  style="@style/text_flag_01"
  android:background="@drawable/flag_04"
  android:text="挣钱ing"
  android:textcolor="#323232" />

 <textview
  style="@style/text_flag_01"
  android:background="@drawable/flag_04"
  android:text="努力ing"
  android:textcolor="#323232" />

 <textview
  style="@style/text_flag_01"
  android:background="@drawable/flag_04"
  android:text="i thick i can"
  android:textcolor="#323232" />

</com.cctvjiatao.customviewgroupflowlayout.view.customviewgroup>

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网