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

1、概述

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

【Android】 自定义ViewGroup 实战篇 -> 实现FlowLayout_第1张图片

这些都特别适合使用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
  1. @Override
  2. publicViewGroup.LayoutParamsgenerateLayoutParams(AttributeSetattrs)
  3. {
  4. returnnewMarginLayoutParams(getContext(),attrs);
  5. }

4、onMeasure

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

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

5、onLayout

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

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

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
  1. <stylename="text_flag_01">
  2. <itemname="android:layout_width">wrap_content</item>
  3. <itemname="android:layout_height">wrap_content</item>
  4. <itemname="android:layout_margin">4dp</item>
  5. <itemname="android:background">@drawable/flag_01</item>
  6. <itemname="android:textColor">#ffffff</item>
  7. </style>

flag_01.xml

[html] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <shapexmlns:android="http://schemas.android.com/apk/res/android">
  3. <solidandroid:color="#7690A5">
  4. </solid>
  5. <cornersandroid:radius="5dp"/>
  6. <padding
  7. android:bottom="2dp"
  8. android:left="10dp"
  9. android:right="10dp"
  10. android:top="2dp"/>
  11. </shape>

布局文件:

[html] view plain copy
  1. <LinearLayoutxmlns: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. <com.zhy.zhy_flowlayout02.FlowLayout
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content">
  10. <TextView
  11. style="@style/text_flag_01"
  12. android:text="Welcome"/>
  13. <TextView
  14. style="@style/text_flag_01"
  15. android:text="IT工程师"/>
  16. <TextView
  17. style="@style/text_flag_01"
  18. android:text="学习ing"/>
  19. <TextView
  20. style="@style/text_flag_01"
  21. android:text="恋爱ing"/>
  22. <TextView
  23. style="@style/text_flag_01"
  24. android:text="挣钱ing"/>
  25. <TextView
  26. style="@style/text_flag_01"
  27. android:text="努力ing"/>
  28. <TextView
  29. style="@style/text_flag_01"
  30. android:text="Ithickican"/>
  31. </com.zhy.zhy_flowlayout02.FlowLayout>
  32. </LinearLayout>

效果图:

【Android】 自定义ViewGroup 实战篇 -> 实现FlowLayout_第2张图片

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

res/drawble/flog_02.xml

[html] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <shapexmlns:android="http://schemas.android.com/apk/res/android">
  3. <solidandroid:color="#FFFFFF">
  4. </solid>
  5. <cornersandroid:radius="40dp"/>
  6. <strokeandroid:color="#C9C9C9"android:width="2dp"/>
  7. <padding
  8. android:bottom="2dp"
  9. android:left="10dp"
  10. android:right="10dp"
  11. android:top="2dp"/>
  12. </shape>

flag_03.xml

[html] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <shapexmlns:android="http://schemas.android.com/apk/res/android">
  3. <solidandroid:color="#FFFFFF">
  4. </solid>
  5. <cornersandroid:radius="40dp"/>
  6. <padding
  7. android:bottom="2dp"
  8. android:left="10dp"
  9. android:right="10dp"
  10. android:top="2dp"/>
  11. </shape>

布局文件:

[html] view plain copy
  1. <LinearLayoutxmlns: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. <com.zhy.zhy_flowlayout02.FlowLayout
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content">
  10. <TextView
  11. style="@style/text_flag_01"
  12. android:text="Welcome"/>
  13. <TextView
  14. style="@style/text_flag_01"
  15. android:text="IT工程师"/>
  16. <TextView
  17. style="@style/text_flag_01"
  18. android:text="学习ing"/>
  19. <TextView
  20. style="@style/text_flag_01"
  21. android:text="恋爱ing"/>
  22. <TextView
  23. style="@style/text_flag_01"
  24. android:text="挣钱ing"/>
  25. <TextView
  26. style="@style/text_flag_01"
  27. android:text="努力ing"/>
  28. <TextView
  29. style="@style/text_flag_01"
  30. android:text="Ithickican"/>
  31. </com.zhy.zhy_flowlayout02.FlowLayout>
  32. <com.zhy.zhy_flowlayout02.FlowLayout
  33. android:layout_width="fill_parent"
  34. android:layout_height="wrap_content"
  35. android:layout_marginTop="20dp">
  36. <TextView
  37. style="@style/text_flag_01"
  38. android:background="@drawable/flag_02"
  39. android:text="Welcome"
  40. android:textColor="#888888"/>
  41. <TextView
  42. style="@style/text_flag_01"
  43. android:background="@drawable/flag_02"
  44. android:text="IT工程师"
  45. android:textColor="#888888"/>
  46. <TextView
  47. style="@style/text_flag_01"
  48. android:background="@drawable/flag_02"
  49. android:text="学习ing"
  50. android:textColor="#888888"/>
  51. <TextView
  52. style="@style/text_flag_01"
  53. android:background="@drawable/flag_02"
  54. android:text="恋爱ing"
  55. android:textColor="#888888"/>
  56. <TextView
  57. style="@style/text_flag_01"
  58. android:background="@drawable/flag_02"
  59. android:text="挣钱ing"
  60. android:textColor="#888888"/>
  61. <TextView
  62. style="@style/text_flag_01"
  63. android:background="@drawable/flag_02"
  64. android:text="努力ing"
  65. android:textColor="#888888"/>
  66. <TextView
  67. style="@style/text_flag_01"
  68. android:background="@drawable/flag_02"
  69. android:text="Ithickican"
  70. android:textColor="#888888"/>
  71. </com.zhy.zhy_flowlayout02.FlowLayout>
  72. <com.zhy.zhy_flowlayout02.FlowLayout
  73. android:layout_width="fill_parent"
  74. android:layout_height="wrap_content"
  75. android:layout_marginTop="20dp">
  76. <TextView
  77. style="@style/text_flag_01"
  78. android:background="@drawable/flag_03"
  79. android:text="Welcome"
  80. android:textColor="#43BBE7"/>
  81. <TextView
  82. style="@style/text_flag_01"
  83. android:background="@drawable/flag_03"
  84. android:text="IT工程师"
  85. android:textColor="#43BBE7"/>
  86. <TextView
  87. style="@style/text_flag_01"
  88. android:background="@drawable/flag_03"
  89. android:text="学习ing"
  90. android:textColor="#43BBE7"/>
  91. <TextView
  92. style="@style/text_flag_01"
  93. android:background="@drawable/flag_03"
  94. android:text="恋爱ing"
  95. android:textColor="#43BBE7"/>
  96. <TextView
  97. style="@style/text_flag_01"
  98. android:background="@drawable/flag_03"
  99. android:text="挣钱ing"
  100. android:textColor="#43BBE7"/>
  101. <TextView
  102. style="@style/text_flag_01"
  103. android:background="@drawable/flag_03"
  104. android:text="努力ing"
  105. android:textColor="#43BBE7"/>
  106. <TextView
  107. style="@style/text_flag_01"
  108. android:background="@drawable/flag_03"
  109. android:text="Ithickican"
  110. android:textColor="#43BBE7"/>
  111. </com.zhy.zhy_flowlayout02.FlowLayout>
  112. </LinearLayout>

效果图:

【Android】 自定义ViewGroup 实战篇 -> 实现FlowLayout_第3张图片

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

flag_04.xml

[html] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <shapexmlns:android="http://schemas.android.com/apk/res/android">
  3. <solidandroid:color="#E7E7E7">
  4. </solid>
  5. <corners
  6. android:radius="30dp"
  7. />
  8. <padding
  9. android:bottom="2dp"
  10. android:left="10dp"
  11. android:right="10dp"
  12. android:top="2dp"/>
  13. </shape>

布局文件:

[html] view plain copy
  1. <com.zhy.zhy_flowlayout02.FlowLayoutxmlns: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. <TextView
  7. style="@style/text_flag_01"
  8. android:background="@drawable/flag_04"
  9. android:text="Welcome"
  10. android:textColor="#323232"/>
  11. <TextView
  12. style="@style/text_flag_01"
  13. android:background="@drawable/flag_04"
  14. android:text="IT工程师"
  15. android:textColor="#323232"/>
  16. <TextView
  17. style="@style/text_flag_01"
  18. android:background="@drawable/flag_04"
  19. android:text="学习ing"
  20. android:textColor="#323232"/>
  21. <TextView
  22. style="@style/text_flag_01"
  23. android:background="@drawable/flag_04"
  24. android:text="恋爱ing"
  25. android:textColor="#323232"/>
  26. <TextView
  27. style="@style/text_flag_01"
  28. android:background="@drawable/flag_04"
  29. android:text="挣钱ing"
  30. android:textColor="#323232"/>
  31. <TextView
  32. style="@style/text_flag_01"
  33. android:background="@drawable/flag_04"
  34. android:text="努力ing"
  35. android:textColor="#323232"/>
  36. <TextView
  37. style="@style/text_flag_01"
  38. android:background="@drawable/flag_04"
  39. android:text="Ithickican"
  40. android:textColor="#323232"/>
  41. </com.zhy.zhy_flowlayout02.FlowLayout>

效果图:

【Android】 自定义ViewGroup 实战篇 -> 实现FlowLayout_第4张图片

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


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


源码点击下载


更多相关文章

  1. [Android] 获取系统顶部状态栏(Status Bar)与底部导航栏(Navigat
  2. Android通知栏的高度获取
  3. Android 遍历界面控件
  4. Android获取系统顶部状态栏(Status Bar)与底部导航栏(Navigation
  5. android 获取屏幕的宽度和高度
  6. Android 遍历文件夹下的所有文件
  7. Android Studio——Android获取屏幕宽度的4种方法
  8. android 遍历所有控件

随机推荐

  1. Android之Binder和AIDL原理
  2. cocos2dx 3.x系列之Mac环境编译Android(
  3. Android中微信支付的调用方法
  4. Android之音频开发基础入门
  5. Android开发-从硬件到APP(一) : 单独编内核
  6. Android布局方式(FrameLayout)学习
  7. 【usb存储挂载】android6.0固定usb存储挂
  8. # Android的按键消息分发机制
  9. [置顶] Android中调用系统相机、系统相册
  10. Android透明状态栏与沉浸模式全解