zoukankan      html  css  js  c++  java
  • 实现FlowLayout 自动排序标签

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38352503 ,本文出自【张鸿洋的博客】

    1、概述

    上一篇已经基本给大家介绍了如何自定义ViewGroup,如果你还不了解,请查看:Android 手把手教您自定ViewGroup ,本篇将使用上篇介绍的方法,给大家带来一个实例:实现FlowLayout,何为FlowLayout,如果对Java的Swing比较熟悉的话一定不会陌生,就是控件根据ViewGroup的宽,自动的往右添加,如果当前行剩余空间不足,则自动添加到下一行。有点所有的控件都往左飘的感觉,第一行满了,往第二行飘~所以也叫流式布局。Android并没有提供流式布局,但是某些场合中,流式布局还是非常适合使用的,比如关键字标签,搜索热词列表等,比如下图:

    这些都特别适合使用FlowLayout,本篇博客会带领大家自己实现FlowLayout,然后使用我们自己定义的FlowLayout实现上面的标签效果。对了,github已经有了这样FlowLayout,但是我觉得丝毫不影响我们对其的学习,学会使用一个控件和学会写一个控件,我相信大家都明白,授人以鱼不如授人以渔。

    2、简单的分析

    1、对于FlowLayout,需要指定的LayoutParams,我们目前只需要能够识别margin即可,即使用MarginLayoutParams.

    2、onMeasure中计算所有childView的宽和高,然后根据childView的宽和高,计算自己的宽和高。(当然,如果不是wrap_content,直接使用父ViewGroup传入的计算值即可)

    3、onLayout中对所有的childView进行布局。

    3、generateLayoutParams

    因为我们只需要支持margin,所以直接使用系统的MarginLayoutParams

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. @Override  
    2. public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)  
    3. {  
    4.     return new MarginLayoutParams(getContext(), attrs);  
    5. }  

    4、onMeasure

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. /** 
    2.      * 负责设置子控件的测量模式和大小 根据所有子控件设置自己的宽和高 
    3.      */  
    4.     @Override  
    5.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
    6.     {  
    7.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
    8.         // 获得它的父容器为它设置的测量模式和大小  
    9.         int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);  
    10.         int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);  
    11.         int modeWidth = MeasureSpec.getMode(widthMeasureSpec);  
    12.         int modeHeight = MeasureSpec.getMode(heightMeasureSpec);  
    13.   
    14.         Log.e(TAG, sizeWidth + "," + sizeHeight);  
    15.   
    16.         // 如果是warp_content情况下,记录宽和高  
    17.         int width = 0;  
    18.         int height = 0;  
    19.         /** 
    20.          * 记录每一行的宽度,width不断取最大宽度 
    21.          */  
    22.         int lineWidth = 0;  
    23.         /** 
    24.          * 每一行的高度,累加至height 
    25.          */  
    26.         int lineHeight = 0;  
    27.   
    28.         int cCount = getChildCount();  
    29.   
    30.         // 遍历每个子元素  
    31.         for (int i = 0; i < cCount; i++)  
    32.         {  
    33.             View child = getChildAt(i);  
    34.             // 测量每一个child的宽和高  
    35.             measureChild(child, widthMeasureSpec, heightMeasureSpec);  
    36.             // 得到child的lp  
    37.             MarginLayoutParams lp = (MarginLayoutParams) child  
    38.                     .getLayoutParams();  
    39.             // 当前子空间实际占据的宽度  
    40.             int childWidth = child.getMeasuredWidth() + lp.leftMargin  
    41.                     + lp.rightMargin;  
    42.             // 当前子空间实际占据的高度  
    43.             int childHeight = child.getMeasuredHeight() + lp.topMargin  
    44.                     + lp.bottomMargin;  
    45.             /** 
    46.              * 如果加入当前child,则超出最大宽度,则的到目前最大宽度给width,类加height 然后开启新行 
    47.              */  
    48.             if (lineWidth + childWidth > sizeWidth)  
    49.             {  
    50.                 width = Math.max(lineWidth, childWidth);// 取最大的  
    51.                 lineWidth = childWidth; // 重新开启新行,开始记录  
    52.                 // 叠加当前高度,  
    53.                 height += lineHeight;  
    54.                 // 开启记录下一行的高度  
    55.                 lineHeight = childHeight;  
    56.             } else  
    57.             // 否则累加值lineWidth,lineHeight取最大高度  
    58.             {  
    59.                 lineWidth += childWidth;  
    60.                 lineHeight = Math.max(lineHeight, childHeight);  
    61.             }  
    62.             // 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较  
    63.             if (i == cCount - 1)  
    64.             {  
    65.                 width = Math.max(width, lineWidth);  
    66.                 height += lineHeight;  
    67.             }  
    68.   
    69.         }  
    70.         setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth  
    71.                 : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight  
    72.                 : height);  
    73.   
    74.     }  


    首先得到其父容器传入的测量模式和宽高的计算值,然后遍历所有的childView,使用measureChild方法对所有的childView进行测量。然后根据所有childView的测量得出的宽和高得到该ViewGroup如果设置为wrap_content时的宽和高。最后根据模式,如果是MeasureSpec.EXACTLY则直接使用父ViewGroup传入的宽和高,否则设置为自己计算的宽和高。

    5、onLayout

    onLayout中完成对所有childView的位置以及大小的指定

    [java] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. /** 
    2.      * 存储所有的View,按行记录 
    3.      */  
    4.     private List<List<View>> mAllViews = new ArrayList<List<View>>();  
    5.     /** 
    6.      * 记录每一行的最大高度 
    7.      */  
    8.     private List<Integer> mLineHeight = new ArrayList<Integer>();  
    9.     @Override  
    10.     protected void onLayout(boolean changed, int l, int t, int r, int b)  
    11.     {  
    12.         mAllViews.clear();  
    13.         mLineHeight.clear();  
    14.   
    15.         int width = getWidth();  
    16.   
    17.         int lineWidth = 0;  
    18.         int lineHeight = 0;  
    19.         // 存储每一行所有的childView  
    20.         List<View> lineViews = new ArrayList<View>();  
    21.         int cCount = getChildCount();  
    22.         // 遍历所有的孩子  
    23.         for (int i = 0; i < cCount; i++)  
    24.         {  
    25.             View child = getChildAt(i);  
    26.             MarginLayoutParams lp = (MarginLayoutParams) child  
    27.                     .getLayoutParams();  
    28.             int childWidth = child.getMeasuredWidth();  
    29.             int childHeight = child.getMeasuredHeight();  
    30.   
    31.             // 如果已经需要换行  
    32.             if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width)  
    33.             {  
    34.                 // 记录这一行所有的View以及最大高度  
    35.                 mLineHeight.add(lineHeight);  
    36.                 // 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView  
    37.                 mAllViews.add(lineViews);  
    38.                 lineWidth = 0;// 重置行宽  
    39.                 lineViews = new ArrayList<View>();  
    40.             }  
    41.             /** 
    42.              * 如果不需要换行,则累加 
    43.              */  
    44.             lineWidth += childWidth + lp.leftMargin + lp.rightMargin;  
    45.             lineHeight = Math.max(lineHeight, childHeight + lp.topMargin  
    46.                     + lp.bottomMargin);  
    47.             lineViews.add(child);  
    48.         }  
    49.         // 记录最后一行  
    50.         mLineHeight.add(lineHeight);  
    51.         mAllViews.add(lineViews);  
    52.   
    53.         int left = 0;  
    54.         int top = 0;  
    55.         // 得到总行数  
    56.         int lineNums = mAllViews.size();  
    57.         for (int i = 0; i < lineNums; i++)  
    58.         {  
    59.             // 每一行的所有的views  
    60.             lineViews = mAllViews.get(i);  
    61.             // 当前行的最大高度  
    62.             lineHeight = mLineHeight.get(i);  
    63.   
    64.             Log.e(TAG, "第" + i + "行 :" + lineViews.size() + " , " + lineViews);  
    65.             Log.e(TAG, "第" + i + "行, :" + lineHeight);  
    66.   
    67.             // 遍历当前行所有的View  
    68.             for (int j = 0; j < lineViews.size(); j++)  
    69.             {  
    70.                 View child = lineViews.get(j);  
    71.                 if (child.getVisibility() == View.GONE)  
    72.                 {  
    73.                     continue;  
    74.                 }  
    75.                 MarginLayoutParams lp = (MarginLayoutParams) child  
    76.                         .getLayoutParams();  
    77.   
    78.                 //计算childView的left,top,right,bottom  
    79.                 int lc = left + lp.leftMargin;  
    80.                 int tc = top + lp.topMargin;  
    81.                 int rc =lc + child.getMeasuredWidth();  
    82.                 int bc = tc + child.getMeasuredHeight();  
    83.   
    84.                 Log.e(TAG, child + " , l = " + lc + " , t = " + t + " , r ="  
    85.                         + rc + " , b = " + bc);  
    86.   
    87.                 child.layout(lc, tc, rc, bc);  
    88.                   
    89.                 left += child.getMeasuredWidth() + lp.rightMargin  
    90.                         + lp.leftMargin;  
    91.             }  
    92.             left = 0;  
    93.             top += lineHeight;  
    94.         }  
    95.   
    96.     }  


    allViews的每个Item为每行所有View的List集合。

    mLineHeight记录的为每行的最大高度。

    23-48行,遍历所有的childView,用于设置allViews的值,以及mLineHeight的值。

    57行,根据allViews的长度,遍历所有的行数

    67-91行,遍历每一行的中所有的childView,对childView的left , top , right , bottom 进行计算,和定位。

    92-93行,重置left和top,准备计算下一行的childView的位置。

    好了,到此完成了所有的childView的绘制区域的确定,到此,我们的FlowLayout的代码也结束了~~静下心来看一看是不是也不难~

    6、测试

    我准备使用TextView作为我们的标签,所以为其简单写了一点样式:

    res/values/styles.xml中:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <style name="text_flag_01">  
    2.        <item name="android:layout_width">wrap_content</item>  
    3.        <item name="android:layout_height">wrap_content</item>  
    4.        <item name="android:layout_margin">4dp</item>  
    5.        <item name="android:background">@drawable/flag_01</item>  
    6.        <item name="android:textColor">#ffffff</item>  
    7.    </style>  


    flag_01.xml

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <shape xmlns:android="http://schemas.android.com/apk/res/android" >  
    3.   
    4.     <solid android:color="#7690A5" >  
    5.     </solid>  
    6.   
    7.     <corners android:radius="5dp"/>  
    8.     <padding  
    9.         android:bottom="2dp"  
    10.         android:left="10dp"  
    11.         android:right="10dp"  
    12.         android:top="2dp" />  
    13.   
    14. </shape>  


    布局文件:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:tools="http://schemas.android.com/tools"  
    3.     android:layout_width="fill_parent"  
    4.     android:layout_height="fill_parent"  
    5.     android:background="#E1E6F6"  
    6.     android:orientation="vertical" >  
    7.   
    8.     <com.zhy.zhy_flowlayout02.FlowLayout  
    9.         android:layout_width="fill_parent"  
    10.         android:layout_height="wrap_content" >  
    11.   
    12.         <TextView  
    13.             style="@style/text_flag_01"  
    14.             android:text="Welcome" />  
    15.   
    16.         <TextView  
    17.             style="@style/text_flag_01"  
    18.             android:text="IT工程师" />  
    19.   
    20.         <TextView  
    21.             style="@style/text_flag_01"  
    22.             android:text="学习ing" />  
    23.   
    24.         <TextView  
    25.             style="@style/text_flag_01"  
    26.             android:text="恋爱ing" />  
    27.   
    28.         <TextView  
    29.             style="@style/text_flag_01"  
    30.             android:text="挣钱ing" />  
    31.   
    32.         <TextView  
    33.             style="@style/text_flag_01"  
    34.             android:text="努力ing" />  
    35.   
    36.         <TextView  
    37.             style="@style/text_flag_01"  
    38.             android:text="I thick i can" />  
    39.     </com.zhy.zhy_flowlayout02.FlowLayout>  
    40.       
    41.     </LinearLayout>  


    效果图:

    是不是还不错,下面继续简单自定义几个背景:

    res/drawble/flog_02.xml

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <shape xmlns:android="http://schemas.android.com/apk/res/android" >  
    3.   
    4.     <solid android:color="#FFFFFF" >  
    5.     </solid>  
    6.   
    7.     <corners android:radius="40dp"/>  
    8.     <stroke android:color="#C9C9C9" android:width="2dp"/>  
    9.       
    10.     <padding  
    11.         android:bottom="2dp"  
    12.         android:left="10dp"  
    13.         android:right="10dp"  
    14.         android:top="2dp" />  
    15.   
    16. </shape>  


    flag_03.xml

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <shape xmlns:android="http://schemas.android.com/apk/res/android" >  
    3.   
    4.     <solid android:color="#FFFFFF" >  
    5.     </solid>  
    6.   
    7.     <corners android:radius="40dp"/>  
    8.       
    9.     <padding  
    10.         android:bottom="2dp"  
    11.         android:left="10dp"  
    12.         android:right="10dp"  
    13.         android:top="2dp" />  
    14.   
    15. </shape>  


    布局文件:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:tools="http://schemas.android.com/tools"  
    3.     android:layout_width="fill_parent"  
    4.     android:layout_height="fill_parent"  
    5.     android:background="#E1E6F6"  
    6.     android:orientation="vertical" >  
    7.   
    8.     <com.zhy.zhy_flowlayout02.FlowLayout  
    9.         android:layout_width="fill_parent"  
    10.         android:layout_height="wrap_content" >  
    11.   
    12.         <TextView  
    13.             style="@style/text_flag_01"  
    14.             android:text="Welcome" />  
    15.   
    16.         <TextView  
    17.             style="@style/text_flag_01"  
    18.             android:text="IT工程师" />  
    19.   
    20.         <TextView  
    21.             style="@style/text_flag_01"  
    22.             android:text="学习ing" />  
    23.   
    24.         <TextView  
    25.             style="@style/text_flag_01"  
    26.             android:text="恋爱ing" />  
    27.   
    28.         <TextView  
    29.             style="@style/text_flag_01"  
    30.             android:text="挣钱ing" />  
    31.   
    32.         <TextView  
    33.             style="@style/text_flag_01"  
    34.             android:text="努力ing" />  
    35.   
    36.         <TextView  
    37.             style="@style/text_flag_01"  
    38.             android:text="I thick i can" />  
    39.     </com.zhy.zhy_flowlayout02.FlowLayout>  
    40.       
    41.   
    42.     <com.zhy.zhy_flowlayout02.FlowLayout  
    43.         android:layout_width="fill_parent"  
    44.         android:layout_height="wrap_content"  
    45.         android:layout_marginTop="20dp" >  
    46.   
    47.         <TextView  
    48.             style="@style/text_flag_01"  
    49.             android:background="@drawable/flag_02"  
    50.             android:text="Welcome"  
    51.             android:textColor="#888888" />  
    52.   
    53.         <TextView  
    54.             style="@style/text_flag_01"  
    55.             android:background="@drawable/flag_02"  
    56.             android:text="IT工程师"  
    57.             android:textColor="#888888" />  
    58.   
    59.         <TextView  
    60.             style="@style/text_flag_01"  
    61.             android:background="@drawable/flag_02"  
    62.             android:text="学习ing"  
    63.             android:textColor="#888888" />  
    64.   
    65.         <TextView  
    66.             style="@style/text_flag_01"  
    67.             android:background="@drawable/flag_02"  
    68.             android:text="恋爱ing"  
    69.             android:textColor="#888888" />  
    70.   
    71.         <TextView  
    72.             style="@style/text_flag_01"  
    73.             android:background="@drawable/flag_02"  
    74.             android:text="挣钱ing"  
    75.             android:textColor="#888888" />  
    76.   
    77.         <TextView  
    78.             style="@style/text_flag_01"  
    79.             android:background="@drawable/flag_02"  
    80.             android:text="努力ing"  
    81.             android:textColor="#888888" />  
    82.   
    83.         <TextView  
    84.             style="@style/text_flag_01"  
    85.             android:background="@drawable/flag_02"  
    86.             android:text="I thick i can"  
    87.             android:textColor="#888888" />  
    88.     </com.zhy.zhy_flowlayout02.FlowLayout>  
    89.   
    90.     <com.zhy.zhy_flowlayout02.FlowLayout  
    91.         android:layout_width="fill_parent"  
    92.         android:layout_height="wrap_content"  
    93.         android:layout_marginTop="20dp" >  
    94.   
    95.         <TextView  
    96.             style="@style/text_flag_01"  
    97.             android:background="@drawable/flag_03"  
    98.             android:text="Welcome"  
    99.             android:textColor="#43BBE7" />  
    100.   
    101.         <TextView  
    102.             style="@style/text_flag_01"  
    103.             android:background="@drawable/flag_03"  
    104.             android:text="IT工程师"  
    105.             android:textColor="#43BBE7" />  
    106.   
    107.         <TextView  
    108.             style="@style/text_flag_01"  
    109.             android:background="@drawable/flag_03"  
    110.             android:text="学习ing"  
    111.             android:textColor="#43BBE7" />  
    112.   
    113.         <TextView  
    114.             style="@style/text_flag_01"  
    115.             android:background="@drawable/flag_03"  
    116.             android:text="恋爱ing"  
    117.             android:textColor="#43BBE7" />  
    118.   
    119.         <TextView  
    120.             style="@style/text_flag_01"  
    121.             android:background="@drawable/flag_03"  
    122.             android:text="挣钱ing"  
    123.             android:textColor="#43BBE7" />  
    124.   
    125.         <TextView  
    126.             style="@style/text_flag_01"  
    127.             android:background="@drawable/flag_03"  
    128.             android:text="努力ing"  
    129.             android:textColor="#43BBE7" />  
    130.   
    131.         <TextView  
    132.             style="@style/text_flag_01"  
    133.             android:background="@drawable/flag_03"  
    134.             android:text="I thick i can"  
    135.             android:textColor="#43BBE7" />  
    136.     </com.zhy.zhy_flowlayout02.FlowLayout>  
    137.   
    138. </LinearLayout>  


    效果图:

    暂不赞~~上面都是match_parent~~下面固定下宽度,实现文章最开始的移动开发热门标签:

    flag_04.xml

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <shape xmlns:android="http://schemas.android.com/apk/res/android" >  
    3.   
    4.     <solid android:color="#E7E7E7" >  
    5.     </solid>  
    6.     <corners  
    7.         android:radius="30dp"  
    8.          />  
    9.   
    10.     <padding  
    11.         android:bottom="2dp"  
    12.         android:left="10dp"  
    13.         android:right="10dp"  
    14.         android:top="2dp" />  
    15. </shape>  


    布局文件:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. <com.zhy.zhy_flowlayout02.FlowLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:tools="http://schemas.android.com/tools"  
    3.     android:layout_width="200dp"  
    4.     android:layout_height="wrap_content"  
    5.     android:background="#FFFFFF" >  
    6.   
    7.     <TextView  
    8.         style="@style/text_flag_01"  
    9.         android:background="@drawable/flag_04"  
    10.         android:text="Welcome"  
    11.         android:textColor="#323232" />  
    12.   
    13.     <TextView  
    14.         style="@style/text_flag_01"  
    15.         android:background="@drawable/flag_04"  
    16.         android:text="IT工程师"  
    17.         android:textColor="#323232" />  
    18.   
    19.     <TextView  
    20.         style="@style/text_flag_01"  
    21.         android:background="@drawable/flag_04"  
    22.         android:text="学习ing"  
    23.         android:textColor="#323232" />  
    24.   
    25.     <TextView  
    26.         style="@style/text_flag_01"  
    27.         android:background="@drawable/flag_04"  
    28.         android:text="恋爱ing"  
    29.         android:textColor="#323232" />  
    30.   
    31.     <TextView  
    32.         style="@style/text_flag_01"  
    33.         android:background="@drawable/flag_04"  
    34.         android:text="挣钱ing"  
    35.         android:textColor="#323232" />  
    36.   
    37.     <TextView  
    38.         style="@style/text_flag_01"  
    39.         android:background="@drawable/flag_04"  
    40.         android:text="努力ing"  
    41.         android:textColor="#323232" />  
    42.   
    43.     <TextView  
    44.         style="@style/text_flag_01"  
    45.         android:background="@drawable/flag_04"  
    46.         android:text="I thick i can"  
    47.         android:textColor="#323232" />  
    48.   
    49. </com.zhy.zhy_flowlayout02.FlowLayout>  


    效果图:

    是不是完全相同~~o了~

    如果你觉得本篇博客对你有用,那么就留个言或者顶一个~~

  • 相关阅读:
    父子进程 signal 出现 Interrupted system call 问题
    一个测试文章
    《淘宝客户端 for Android》项目实战 html webkit android css3
    Django 中的 ForeignKey ContentType GenericForeignKey 对应的数据库结构
    coreseek 出现段错误和Unigram dictionary load Error 新情况(Gentoo)
    一个 PAM dbus 例子
    漫画统计学 T分数
    解决 paramiko 安装问题 Unable to find vcvarsall.bat
    20141202
    js
  • 原文地址:https://www.cnblogs.com/taoboy/p/5585903.html
Copyright © 2011-2022 走看看