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了~

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

  • 相关阅读:
    SQLite剖析之异步IO模式、共享缓存模式和解锁通知
    SQLite剖析之动态内存分配
    SQLite剖析之锁和并发控制
    SQLite剖析之临时文件、内存数据库
    SQLite剖析之数据类型
    关于Docker目录挂载的总结(一)
    docker常用命令
    玩转docker(一)
    go hello
    术语“go”不被识别为cmdlet,函数,脚本文件或可操作程序的名称
  • 原文地址:https://www.cnblogs.com/taoboy/p/5585903.html
Copyright © 2011-2022 走看看