查看我的所有开源项目【开源实验室

欢迎增加我的QQ群:【201055521】,本博客client下载【请点击

摘要

新项目用到了一种全新布局————Android标签流式布局的功能,正好一直说给大家讲自己定义控件的实现,今天就为大家讲一种android流式布局的实现。

本文原创,转载请注明地址:http://blog.kymjs.com/

正文

在日常的app使用中,我们会在android 的app中看见热门标签等自己主动换行的流式布局,今天。我们就来看看怎样自己定义一个相似热门标签那样的流式布局吧(源代码下载在以下最后给出)



这个控件并非我实现的,代码是从网上搜流式布局找到的。我仅仅是为大家解说一下实现过程以及原理。

先看代码

  1. public class FlowLayout extends ViewGroup {
  2. private float mVerticalSpacing; //每个item纵向间距
  3. private float mHorizontalSpacing; //每个item横向间距
  4. public FlowLayout(Context context) {
  5. super(context);
  6. }
  7. public FlowLayout(Context context, AttributeSet attrs) {
  8. super(context, attrs);
  9. }
  10. public void setHorizontalSpacing(float pixelSize) {
  11. mHorizontalSpacing = pixelSize;
  12. }
  13. public void setVerticalSpacing(float pixelSize) {
  14. mVerticalSpacing = pixelSize;
  15. }
  16. @Override
  17. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  18. int selfWidth = resolveSize(0, widthMeasureSpec);
  19. int paddingLeft = getPaddingLeft();
  20. int paddingTop = getPaddingTop();
  21. int paddingRight = getPaddingRight();
  22. int paddingBottom = getPaddingBottom();
  23. int childLeft = paddingLeft;
  24. int childTop = paddingTop;
  25. int lineHeight = 0;
  26. //通过计算每个子控件的高度,得到自己的高度
  27. for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
  28. View childView = getChildAt(i);
  29. LayoutParams childLayoutParams = childView.getLayoutParams();
  30. childView.measure(
  31. getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
  32. childLayoutParams.width),
  33. getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
  34. childLayoutParams.height));
  35. int childWidth = childView.getMeasuredWidth();
  36. int childHeight = childView.getMeasuredHeight();
  37. lineHeight = Math.max(childHeight, lineHeight);
  38. if (childLeft + childWidth + paddingRight > selfWidth) {
  39. childLeft = paddingLeft;
  40. childTop += mVerticalSpacing + lineHeight;
  41. lineHeight = childHeight;
  42. } else {
  43. childLeft += childWidth + mHorizontalSpacing;
  44. }
  45. }
  46. int wantedHeight = childTop + lineHeight + paddingBottom;
  47. setMeasuredDimension(selfWidth, resolveSize(wantedHeight, heightMeasureSpec));
  48. }
  49. @Override
  50. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  51. int myWidth = r - l;
  52. int paddingLeft = getPaddingLeft();
  53. int paddingTop = getPaddingTop();
  54. int paddingRight = getPaddingRight();
  55. int childLeft = paddingLeft;
  56. int childTop = paddingTop;
  57. int lineHeight = 0;
  58. //依据子控件的宽高,计算子控件应该出现的位置。
  59. for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
  60. View childView = getChildAt(i);
  61. if (childView.getVisibility() == View.GONE) {
  62. continue;
  63. }
  64. int childWidth = childView.getMeasuredWidth();
  65. int childHeight = childView.getMeasuredHeight();
  66. lineHeight = Math.max(childHeight, lineHeight);
  67. if (childLeft + childWidth + paddingRight > myWidth) {
  68. childLeft = paddingLeft;
  69. childTop += mVerticalSpacing + lineHeight;
  70. lineHeight = childHeight;
  71. }
  72. childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
  73. childLeft += childWidth + mHorizontalSpacing;
  74. }
  75. }
  76. }

从控件创建过程说起

  1. 当这个流式布局在被载入如内存并显示在屏幕上这一过程中。首先会调用view.measure(w,h)这种方法,表示測量view的宽度与高度。当中參数w与h分别表示这个控件的父控件的宽高。
  2. 在view.measure()方法的调用过程中又会调用view本身的一个回调方法,onMeasure(),这个是view自身的一个回调方法。用于让开发人员在自己定义View的时候又一次计算自身的大小。通常会在这种方法中循环遍历,计算出这个控件的所有子孙控件的宽高。
  3. 在View的宽高计算完毕以后,考虑将这个控件显示到屏幕的指定位置上,此时view的onLayout()方法会被调用。

    一般同一时候会在这种方法中计算出所有子孙控件在这个控件中的位置。

    可能基本流程有些枯燥,接下来结合代码看看。

流布局的实现

看到onMeasure()方法中的这段:

  1. //通过计算每个子控件的高度,得到自己的高度
  2. for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
  3. View childView = getChildAt(i);
  4. LayoutParams childLayoutParams = childView.getLayoutParams();
  5. childView.measure(
  6. getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
  7. childLayoutParams.width),
  8. getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
  9. childLayoutParams.height));
  10. int childWidth = childView.getMeasuredWidth();
  11. int childHeight = childView.getMeasuredHeight();
  12. lineHeight = Math.max(childHeight, lineHeight);
  13. if (childLeft + childWidth + paddingRight > selfWidth) {
  14. childLeft = paddingLeft;
  15. childTop += mVerticalSpacing + lineHeight;
  16. lineHeight = childHeight;
  17. } else {
  18. childLeft += childWidth + mHorizontalSpacing;
  19. }
  20. }

首先通过循环。遍历这个控件的所有子控件。同一时候调用子控件的measure()方法。这时measure方法的两个參数是控件能给这个子控件的最大宽高(我们都知道的,子控件再大。显示的大小也不能比父控件还大)。这里getChildMeasureSpec()方法的作用是用来计算一个合适子视图的尺寸大小(宽度或者高度),结合我们从子视图的LayoutParams所给出的MeasureSpec信息来获取最合适的结果。比方,假设这个View知道自己的大小尺寸(由于它本身的MeasureSpec的model为Exactly,)而且子视图的大小恰好跟父窗体一样大,父窗体必须用给定的大小去layout子视图

參数含义:spec 父窗体传递给子视图的大小和模式

padding 父窗体的边距,也就是xml中的android:padding

childDimension 子视图想要绘制的准确大小,但终于不一定绘制此值

当得到了每个子控件的大小以后。再要计算自己的宽高就简单了。

int wantedHeight = childTop + lineHeight + paddingBottom;

同理,在onLayout中的这一句

  1. for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
  2. View childView = getChildAt(i);
  3. if (childView.getVisibility() == View.GONE) {
  4. continue;
  5. }
  6. int childWidth = childView.getMeasuredWidth();
  7. int childHeight = childView.getMeasuredHeight();
  8. lineHeight = Math.max(childHeight, lineHeight);
  9. if (childLeft + childWidth + paddingRight > myWidth) {
  10. childLeft = paddingLeft;
  11. childTop += mVerticalSpacing + lineHeight;
  12. lineHeight = childHeight;
  13. }
  14. childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
  15. childLeft += childWidth + mHorizontalSpacing;
  16. }

首先通过循环遍历,控制每个item子控件的显示位置。假设当前行还能放得下一个item。就放到当前行,假设放不下就放到下一行的最左边。

终于,遍历完毕,也就相当于把自己的位置显示完毕了。

效果截图

Android流式布局实现的更多相关文章

  1. android -------- 流式布局,支持单选、多选等

    最近开发中有流式标签这个功能,网上学了下,来分享一下 Android 流式布局,支持单选.多选等,适合用于产品标签等. 效果图: 用法: dependencies { compile 'com.hym ...

  2. android流式布局、待办事项应用、贝塞尔曲线、MVP+Rxjava+Retrofit、艺术图片应用等源码

    Android精选源码 android模仿淘宝首页效果源码 一款艺术图片应用,采用T-MVVM打造 Android MVP + RxJava + Retrofit项目 android流式布局实现热门标 ...

  3. 含有过滤功能的android流式布局

    FilterFlowLayout 含有过滤功能的流式布局, 參考FlowLayout 能够去除宽度不在范围(比例或真实值)内的子view 能够设置最大行数 能够加入组件间水平间距 能够加入行间距 系统 ...

  4. Android流式布局控件

    1,自定义flowlayout代码 package com.hyang.administrator.studentproject.widget; import android.content.Cont ...

  5. Android 自动换行流式布局的RadioGroup

    效果图 用法 使用FlowRadioGroup代替RadioGroup 代码 import android.content.Context; import android.util.Attribute ...

  6. Android 自定义View修炼-Android中常见的热门标签的流式布局的实现

    一.概述:在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧(源码下载在下面最后给出哈) 类似的 ...

  7. Android控件进阶-自定义流式布局和热门标签控件

    技术:Android+java   概述 在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧,类 ...

  8. 【Android - 自定义View】之自定义可滚动的流式布局

    首先来介绍一下这个自定义View: (1)这个自定义View的名称叫做 FlowLayout ,继承自ViewGroup类: (2)在这个自定义View中,用户可以放入所有继承自View类的视图,这个 ...

  9. Android自己定义之流式布局

    流式布局,优点就是父类布局能够自己主动的推断子孩子是不是须要换行,什么时候须要换行,能够做到网页版的标签的效果. 今天就是简单的做了自己定义的流式布局. 详细效果: 原理: 事实上非常easy,Mea ...

随机推荐

  1. PostgreSql入门命令

    1 命令行登录数据库 有两种方式,一是直接在系统shell下执行psql命令:而是先进入psql环境,然后再连接数据库.下面分别给出实例: (1)直接登录 执行命令:psql -h 192.168.1 ...

  2. windows和linux下获取当前程序路径以及cpu数

    #ifdef WIN32 #include <Windows.h> #else #include <stdio.h> #include <unistd.h> #en ...

  3. linux下主要是VirtualBox及GuestAdditions的安装

    Linux版本的VirtualBox下载:http://www.virtualbox.org/wiki/Linux_Downloads.请下载对应的版本. RedHat.RHEL:rpm -i vh ...

  4. 泛泰A870S官方4.4.2系统S0218210 内核版本号信息

    从EGL中获取的信息: OpenGL ES Shader Compiler Version: 17.01.12.SPL Build Date: 02/24/14 Mon Local Branch: R ...

  5. asp.net 网站所有请求跳转到同一个页面

    应用场景:网站维护和未开发完成时,一般需要把所有请求都跳转的一个相关说明的页面,这样用户不至于困惑这个网站是不存在还是怎么了. Solution1:使用一个名称为 app_offline.htm(名字 ...

  6. oracle form 触发器执行顺序及键定义[Z]

    1当打开FORM时: (1)PRE-FORM (2)PRE-BLOCK(BLOCK级) (3)WHEN-NEW-FORM-INSTANCE (4)WHEN-NEW-BLOCK-INSTANCE (5) ...

  7. ios 中的构造方法(二)

    在之前有简单介绍了构造方法的结构,以及构造方法的作用,那么我们现在来讨论一下: 对象的创建分为两步:+ alloc 分配内存空间和 -init 进行初始化 那么在继承自 NSObject 的类当中,我 ...

  8. C++学习笔录1

    1.在实际开发中,引用类型变量值用于函数的参数中.它不会另外开辟空间(提高了程序效率),他相当于变量的别名,代表的就是当前这个变量的地址空间.(引用的底层用的是指针.因此从底层的角度讲,其实它的效率是 ...

  9. EC读书笔记系列之13:条款25 考虑写出一个不抛异常的swap函数

    记住: ★当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定其不抛出异常 ★若你提供一个member swap,也该提供一个non-member swap来调用前者.对于cla ...

  10. c++ 从一个BYTE[] *filePtr 追加二进制文件

    在顶部#include <fstream> 然后,在c盘新建一个txt文件,把后缀名更改为.dat,并且命名mp3Decode.dat //以二进制模式和在文件尾追加的方式打开文件 std ...