Android 自定義流布局。使用開源庫SimpleFlowLayout

前言

實際項目中需要實現一個 熱門搜索 的欄目,類似下圖:
由于 子項(子view) 中的文字是可變的,一行能顯示的 子項 的個數也無法確定。需要支持自動換行和計算位置。



?# 使用開源類庫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);
    }

}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容