自定义ViewGroup——实战,实现FlowLayout

175 阅读3分钟

 

一、FlowLayout介绍

所谓FlowLayout,就是控件根据ViewGroup的宽,自动的往右添加,如果当前行剩余空间不足,则自动添加到下一行。有点像所有的控件都往左飘的感觉,第一行满了,往第二行飘~所以也叫流式布局。Android并没有提供流式布局,但是某些场合中,流式布局还是非常适合使用的,比如关键字标签,搜索热词列表等,比如下图:\

\

 

二、制作分析

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

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

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

\

			//如果lineWidth + chilWidth > viewGroup最大宽度, childView就会移到下一行
			if(lineWidth + childWidth >  widthSpec){//换行
				height += lineheight;
				width = Math.max(lineWidth, childWidth);
				lineWidth = childWidth;//当了下一行
			}else {//同一行的话, 宽度增加
				lineWidth = lineWidth + childWidth;
				//只判断一行最大高度,当换行时, 才增加高度
				lineheight = Math.max(childHeight, lineheight);
			}


\

\

\

\

\

\

\

match_parent  android:layout_width="fill_parent"

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#E1E6F6"
    android:orientation="vertical" >

    <com.example.viewgroupdemo.MyViewGroup
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

        <TextView
            style="@style/text_flag_01"
            android:text="Welcome" />

        <TextView
            style="@style/text_flag_01"
            android:text="IT工程师" />

        <TextView
            style="@style/text_flag_01"
            android:text="学习ing" />

        <TextView
            style="@style/text_flag_01"
            android:text="恋爱ing" />

        <TextView
            style="@style/text_flag_01"
            android:text="挣钱ing" />

        <TextView
            style="@style/text_flag_01"
            android:text="努力ing" />

        <TextView
            style="@style/text_flag_01"
            android:text="I thick i can" />
    </com.example.viewgroupdemo.MyViewGroup>

    <com.example.viewgroupdemo.MyViewGroup
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp" >

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_02"
            android:text="Welcome"
            android:textColor="#888888" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_02"
            android:text="IT工程师"
            android:textColor="#888888" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_02"
            android:text="学习ing"
            android:textColor="#888888" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_02"
            android:text="恋爱ing"
            android:textColor="#888888" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_02"
            android:text="挣钱ing"
            android:textColor="#888888" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_02"
            android:text="努力ing"
            android:textColor="#888888" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_02"
            android:text="I thick i can"
            android:textColor="#888888" />
    </com.example.viewgroupdemo.MyViewGroup>

    <com.example.viewgroupdemo.MyViewGroup
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp" >

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_03"
            android:text="Welcome"
            android:textColor="#43BBE7" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_03"
            android:text="IT工程师"
            android:textColor="#43BBE7" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_03"
            android:text="学习ing"
            android:textColor="#43BBE7" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_03"
            android:text="恋爱ing"
            android:textColor="#43BBE7" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_03"
            android:text="挣钱ing"
            android:textColor="#43BBE7" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_03"
            android:text="努力ing"
            android:textColor="#43BBE7" />

        <TextView
            style="@style/text_flag_01"
            android:background="@drawable/flag_03"
            android:text="I thick i can"
            android:textColor="#43BBE7" />
    </com.example.viewgroupdemo.MyViewGroup>

</LinearLayout>


\

定值:android:layout_width="200dp"

<com.example.viewgroupdemo.MyViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="200dp"
    android:layout_height="wrap_content"
    android:background="#FFFFFF" >

    <TextView
        style="@style/text_flag_01"
        android:background="@drawable/flag_04"
        android:text="Welcome"
        android:textColor="#323232" />

    <TextView
        style="@style/text_flag_01"
        android:background="@drawable/flag_04"
        android:text="IT工程师"
        android:textColor="#323232" />

    <TextView
        style="@style/text_flag_01"
        android:background="@drawable/flag_04"
        android:text="学习ing"
        android:textColor="#323232" />

    <TextView
        style="@style/text_flag_01"
        android:background="@drawable/flag_04"
        android:text="恋爱ing"
        android:textColor="#323232" />

    <TextView
        style="@style/text_flag_01"
        android:background="@drawable/flag_04"
        android:text="挣钱ing"
        android:textColor="#323232" />

    <TextView
        style="@style/text_flag_01"
        android:background="@drawable/flag_04"
        android:text="努力ing"
        android:textColor="#323232" />

    <TextView
        style="@style/text_flag_01"
        android:background="@drawable/flag_04"
        android:text="I thick i can"
        android:textColor="#323232" />

</com.example.viewgroupdemo.MyViewGroup>


\

\

package com.example.viewgroupdemo;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.LinearLayout;

public class MyViewGroup extends ViewGroup {
	public MyViewGroup(Context context) {

		this(context, null);
	}
	public MyViewGroup(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		Log.i("tag", "MyViewGroup");
	}
	/***
	 * 第一步:重写generateLayoutParams, 确定ViewGroup的LayoutParams
	 * 返回MarginLayoutParams的实例, 这就是为我们的ViewGroup指定
	 * 其LayoutParams为MarginLayoutParams
	 */
	@Override
	public LayoutParams generateLayoutParams(AttributeSet attrs) {
		Log.i("tag", "generateLayoutParams");
		return  new MarginLayoutParams(getContext(), attrs);
	}
	/**
	 * 第二步:
	 *    计算所有的childView的width和height, 
	 *    然后根据childview的width,height, margin(MarginLayoutParams获取margin)
	 *    设置viewgroup的高度和宽度
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		Log.i("tag", "onMeasure");
		//获取父容器的模式和高度,宽度
		int widthSpec = MeasureSpec.getSize(widthMeasureSpec);
		int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		int heightSpec = MeasureSpec.getSize(heightMeasureSpec);
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		//如果是warp_content情况下, 记录高度和宽度
		int width = 0;
		int height = 0;
		//记录当前行的高度和宽度
		int lineWidth =0;
		int lineheight = 0;
		//Layoutparams 
		MarginLayoutParams mlp = null;
		//获取childview的数目
		int childCount = getChildCount();
		for (int i = 0; i < childCount; i++) {
			View childview = getChildAt(i);
			//测量childview的高度和宽度
			measureChild(childview, widthMeasureSpec, heightMeasureSpec);

			//获取LayoutParams
			mlp = (MarginLayoutParams) childview.getLayoutParams();

			//获取childview实际所占空间的高度和宽度
			int childWidth = childview.getMeasuredWidth()+mlp.leftMargin+mlp.rightMargin;
			int childHeight = childview.getMeasuredHeight()+mlp.topMargin+mlp.bottomMargin;

			//如果lineWidth + chilWidth > viewGroup最大宽度, childView就会移到下一行
			if(lineWidth + childWidth >  widthSpec){//换行
				height += lineheight;
				width = Math.max(lineWidth, childWidth);
				lineWidth = childWidth;//当了下一行
			}else {//同一行的话, 宽度增加
				lineWidth = lineWidth + childWidth;
				//只判断一行最大高度,当换行时, 才增加高度
				lineheight = Math.max(childHeight, lineheight);
			}

			// 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较  
			if (i == childCount - 1)  {  
				width = Math.max(width, lineWidth);  
				height += lineheight;  
			}  

		
		}   
		//根据模式来设置ViewGroup的高度和宽度
		setMeasuredDimension(
				widthMode==MeasureSpec.EXACTLY ? widthSpec : width, 
						heightMode==MeasureSpec.EXACTLY ? heightSpec : height);
	}

	/**
	 * 设置childView的位置
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		Log.i("tag", "onLayout");
		//如果是warp_content情况下, 记录高度和宽度
		int height = 0;
		//记录当前行的高度和宽度
		int lineWidth =0;
		int lineheight = 0;
		//Layoutparams 
		MarginLayoutParams mlp = null;
		//获取childview的数目
		int childCount = getChildCount();
		for (int i = 0; i < childCount; i++) {
			View childview = getChildAt(i); 
			//childview的区域
			int cl = 0, ct = 0, cr = 0, cb = 0;
			mlp = (MarginLayoutParams) childview.getLayoutParams();
			//获取childview实际所占空间的高度和宽度
			int childWidth = childview.getMeasuredWidth()+mlp.leftMargin+mlp.rightMargin;
			int childHeight = childview.getMeasuredHeight()+mlp.topMargin+mlp.bottomMargin;

			//如果lineWidth + chilWidth > viewGroup最大宽度, childView就会移到下一行
			if(lineWidth + childWidth >  getWidth()){//换行
				height += lineheight;
				lineWidth = childWidth;//当了下一行
				cl = mlp.leftMargin;
				ct = height + mlp.rightMargin;

			}else {//同一行的话, 宽度增加
				cl = lineWidth+mlp.leftMargin;
				ct = height + mlp.topMargin;

				lineWidth = lineWidth + childWidth;
				//只判断一行最大高度,当换行时, 才增加高度
				lineheight = Math.max(childHeight, lineheight);
			}
			cr = cl + childview.getMeasuredWidth();
			cb = ct + childview.getMeasuredHeight();
			childview.layout(cl, ct, cr, cb);
		}
	}


}


\

\

\