天天看點

Android 自定義流布局。使用開源庫SimpleFlowLayout前言實作思路實作代碼

前言

實際項目中需要實作一個 熱門搜尋 的欄目,類似下圖:

由于 子項(子view) 中的文字是可變的,一行能顯示的 子項 的個數也無法确定。需要支援自動換行和計算位置。

Android 自定義流布局。使用開源庫SimpleFlowLayout前言實作思路實作代碼

�# 使用開源類庫SimpleFlowLayout

我自己寫了個 自定義view ,繼承自viewGroup, 來實作它,托管到github開源平台。

名稱:SimpleFlowLayout
位址:https://github.com/vir56k/SimpleFlowLayout
特點:可以不斷添加多個子view,計算位置,自動換行。 類似html中的div标簽
适用: 熱門标簽           

複制

實作思路

要實作 自定義的viewgroup,需要:

  1. 繼承自 ViewGroup
  2. 實作 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

      這個方法用于測量 自己(自定義view)本身需要的寬度和高度

  3. 實作 protected void onLayout(boolean changed, int l, int t, int r, int b)

      這個方法用于指定如何擺放 子view 的位置。

實作代碼

package zhangyf.vir56k.flowframelayout;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

/**
 * name: android 簡單的流布局自定義view
 * 作者:張雲飛vir
 * 特點:可以不斷添加多個子view,計算位置,自動換行。
 * 适用: 熱門标簽
 * Created by zhangyunfei on 15/12/4.
 */
public class SimpleFlowLayout extends ViewGroup {
    public SimpleFlowLayout(Context context) {
        super(context);
    }

    public SimpleFlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SimpleFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMax = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMax = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);


        int widthNeed = 0;
        int heightNeed = 0;
        int x = 0;
        int y = 0;
        int currentLineHeight = 0;
        View child;
        for (int i = 0; i < getChildCount(); i++) {
            child = getChildAt(i);
            if (child.getVisibility() == View.GONE) {
                continue;
            }

            child.measure(widthMeasureSpec, heightMeasureSpec);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();//獲得子view的 外邊距
            //測算子view寬度,本行這句代碼有問題,不能計算子view的自動換行 int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            //使用viewGroup的measureChildWithMargins測算寬度,在這個方法裡處理了 LayoutParams的match_parent等方式的處理
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);

            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;

            if (x + childWidth > widthMax) {//換行處理,本行高度和x軸都清零,y軸下移(加上上次的行高)
                y += currentLineHeight;
                currentLineHeight = 0;
                x = 0;
            }
            x += childWidth;
            currentLineHeight = Math.max(currentLineHeight, childHeight);

            widthNeed = Math.max(widthNeed, x);//加入了這個 子view後,留下最大寬度
            heightNeed = Math.max(heightNeed, y + currentLineHeight);//對比上次的,留下最大的高度
        }
        setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthMax : widthNeed,
                heightMode == MeasureSpec.EXACTLY ? heightMax : heightNeed);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int widthMax = getWidth();
        int x, y;
        x = 0;
        y = 0;
        View child;
        int left = 0;
        int top = 0;
        int currentLineHeight = 0;
        for (int i = 0; i < getChildCount(); i++) {
            child = getChildAt(i);
            if (child.getVisibility() == View.GONE) {
                continue;
            }
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            if (x + childWidth > widthMax) {//換行處理
                y += currentLineHeight;
                x = 0;
                currentLineHeight = 0;
            }
            left = x + lp.leftMargin;
            top = y + lp.topMargin;
            //定位子view的位置
            child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());

            x += childWidth;
            currentLineHeight = Math.max(currentLineHeight, childHeight);
        }
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

}           

複制