欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-03-02 19:40:58
整理总结自鸿洋的博客:  一、flowlayout介绍  所谓flowlayout,就是控件根据viewgroup的宽,自动的往右添加,如果当前行剩...

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

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

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

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>

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