Android流式布局实现

查看我的全部开源项目【开源实验室

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

摘要

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

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

正文

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

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

先看代码

public class FlowLayout extends ViewGroup {
    private float mVerticalSpacing; //每个item纵向间距
    private float mHorizontalSpacing; //每个item横向间距

    public FlowLayout(Context context) {
        super(context);
    }
    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public void setHorizontalSpacing(float pixelSize) {
        mHorizontalSpacing = pixelSize;
    }
    public void setVerticalSpacing(float pixelSize) {
        mVerticalSpacing = pixelSize;
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int selfWidth = resolveSize(0, widthMeasureSpec);

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        int childLeft = paddingLeft;
        int childTop = paddingTop;
        int lineHeight = 0;

        //通过计算每一个子控件的高度,得到自己的高度
        for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
            View childView = getChildAt(i);
            LayoutParams childLayoutParams = childView.getLayoutParams();
            childView.measure(
                    getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
                            childLayoutParams.width),
                    getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
                            childLayoutParams.height));
            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            lineHeight = Math.max(childHeight, lineHeight);

            if (childLeft + childWidth + paddingRight > selfWidth) {
                childLeft = paddingLeft;
                childTop += mVerticalSpacing + lineHeight;
                lineHeight = childHeight;
            } else {
                childLeft += childWidth + mHorizontalSpacing;
            }
        }

        int wantedHeight = childTop + lineHeight + paddingBottom;
        setMeasuredDimension(selfWidth, resolveSize(wantedHeight, heightMeasureSpec));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int myWidth = r - l;

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int paddingRight = getPaddingRight();

        int childLeft = paddingLeft;
        int childTop = paddingTop;

        int lineHeight = 0;

        //根据子控件的宽高,计算子控件应该出现的位置。
        for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
            View childView = getChildAt(i);

            if (childView.getVisibility() == View.GONE) {
                continue;
            }

            int childWidth = childView.getMeasuredWidth();
            int childHeight = childView.getMeasuredHeight();

            lineHeight = Math.max(childHeight, lineHeight);

            if (childLeft + childWidth + paddingRight > myWidth) {
                childLeft = paddingLeft;
                childTop += mVerticalSpacing + lineHeight;
                lineHeight = childHeight;
            }
            childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
            childLeft += childWidth + mHorizontalSpacing;
        }
    }
}

从控件创建过程说起

  1. 当这个流式布局在被加载如内存并显示在屏幕上这一过程中,首先会调用view.measure(w,h)这个方法,表示测量view的宽度与高度,其中参数w与h分别表示这个控件的父控件的宽高。
  2. 在view.measure()方法的调用过程中又会调用view本身的一个回调方法,onMeasure(),这个是view自身的一个回调方法,用于让开发者在自定义View的时候重新计算自身的大小。一般会在这个方法中循环遍历,计算出这个控件的全部子孙控件的宽高。
  3. 在View的宽高计算完成以后,考虑将这个控件显示到屏幕的指定位置上,此时view的onLayout()方法会被调用。 一般同时会在这个方法中计算出全部子孙控件在这个控件中的位置。

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

流布局的实现

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

//通过计算每一个子控件的高度,得到自己的高度
for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
        View childView = getChildAt(i);
        LayoutParams childLayoutParams = childView.getLayoutParams();
        childView.measure(
                getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
                        childLayoutParams.width),
                getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
                        childLayoutParams.height));
        int childWidth = childView.getMeasuredWidth();
        int childHeight = childView.getMeasuredHeight();

        lineHeight = Math.max(childHeight, lineHeight);

        if (childLeft + childWidth + paddingRight > selfWidth) {
            childLeft = paddingLeft;
            childTop += mVerticalSpacing + lineHeight;
            lineHeight = childHeight;
        } else {
            childLeft += childWidth + mHorizontalSpacing;
        }
    }

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

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

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

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

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

int wantedHeight = childTop + lineHeight + paddingBottom;

同理,在onLayout中的这一句

for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
        View childView = getChildAt(i);

        if (childView.getVisibility() == View.GONE) {
            continue;
        }

        int childWidth = childView.getMeasuredWidth();
        int childHeight = childView.getMeasuredHeight();

        lineHeight = Math.max(childHeight, lineHeight);

        if (childLeft + childWidth + paddingRight > myWidth) {
            childLeft = paddingLeft;
            childTop += mVerticalSpacing + lineHeight;
            lineHeight = childHeight;
        }
        childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
        childLeft += childWidth + mHorizontalSpacing;
    } 

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

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

效果截图

时间: 2024-10-27 19:34:55

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

android流式布局热门标签的实现

在日常的app使用中,我们会在android 的app中看见热门标签等自动换行的流式布局,今天就为大家分享一种android流式布局的实现. 先看最终效果 自定义流式布局的实现 package com.sunny.flowlayout.view; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.util.AttributeSet; import an

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

最近开发中有流式标签这个功能,网上学了下,来分享一下 Android 流式布局,支持单选.多选等,适合用于产品标签等. 效果图: 用法: dependencies { compile 'com.hyman:flowlayout-lib:1.1.2' } 布局: <!--max_select:-1为不限制选择数量,>=1的数字为控制选择tag的数量--> <com.zhy.view.flowlayout.TagFlowLayout android:id="@+id/id_f

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

FilterFlowLayout 含有过滤功能的流式布局, 参考FlowLayout 可以去除宽度不在范围(比例或真实值)内的子view 可以设置最大行数 可以添加组件间水平间距 可以添加行间距 系统要求 Android 4.0以上 快速使用 <me.codeboy.android.lib.FilterFlowLayout xmlns:cb="http://schemas.android.com/apk/res-auto" android:id="@+id/filter

android流式布局:FlexboxLayout用法探析(一)

FlexboxLayout是google官方开源的一个可以简单快速创建具有弹性功能的流式布局,它的目的是使用我们常见的布局模式,帮我们很好的实现UI区域的比例划分,比如三列布局,可以非常简单的实现.它支持非常多的属性设置,用起来很简单. GitHub:https://github.com/google/flexbox-layout 首先引入该库: dependencies { compile 'com.google.android:flexbox:0.2.2' } 然后是在布局文件中声明使用该控

Android之自定义流式布局FlowLayout

流式布局常常用于“热门标签”中,大概功能就是将所有的子View一行一行的排列,如果一行中剩下的空间不足以盛放下一个子View,则换到另一行继续排列.这样做的好处是不需要在主线程中自己麻烦定义控件的位置,只需要把生成的控件放到容器中,容器自己会自动排列.首先来看一下运行结果: Android中的自定义容器控件(继承自ViewGroup的控件)都有两个必须实现的方法:onMeasure()和onLayout() (1)onMeasure:测量子View的宽和高,设置自己的宽和高,根据自View的布局

Android中常见的热门标签的流式布局的实现

一.概述:在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧(源码下载在下面最后给出) 类似的自定义布局.下面我们就来详细介绍流式布局的应用特点以及用的的技术点: 1.流式布局的特点以及应用场景    特点:当上面一行的空间不够容纳新的TextView时候,    才开辟下一行的空间 原理图: 场景:主要用于关键词搜索或者热门标签等场景2.自定义ViewGroup,重点重写下面两个方法 1.o

Android 自动换行流式布局的RadioGroup

用法 使用FlowRadioGroup代替RadioGroup import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.RadioGroup; /** * 流式布局的RadioGroup */ public class FlowRadioGroup extends RadioGroup { public FlowRadioGr

Android自定义ViewGroup实现流式布局

实现宽度不足自动换行的流式布局: FlowLayout.java package com.jackie.flowlayout; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * Created by Jackie on 8/28/15. */ public class FlowLayout

Android自定义之流式布局

流式布局,好处就是父类布局可以自动的判断子孩子是不是需要换行,什么时候需要换行,可以做到网页版的标签的效果.今天就是简单的做了自定义的流式布局. 具体效果: 原理: 其实很简单,Measure  Layout.只需要这两个步骤就可以搞定了.完全的手动去Measure  Layout. 我们看一下代码. 解释就在代码里面做注释了,因为使用为知笔记写的博客,格式不符合代码格式.大家可以看具体的源码.最后又源码下载地址. 1.Measure  测量 @Override protected void o