自定義ViewGroup,流式佈局FlowLayout

本文參考了【張鴻洋的博客】: Android 手把手教您自定義ViewGroup(一)Android 自定義ViewGroup 實戰篇 -> 實現FlowLayout

在很多應用裏面,我們能看到類似於這樣的流式
佈局
用現有的控件我們實現這種動態適應的佈局很麻煩,對於如何比較簡單的去實現這種佈局,就需要我們去自定義ViewGroup了

實現步驟

一、新建一個FlowLayout類,繼承ViewGroup,並重寫generateLayoutParams方法

重寫generateLayoutParams是爲了設置佈局的LayoutParams,在這裏,我們用系統的MarginLayoutParams就可以了

 //設置佈局的LayoutParams,用系統的MarginLayoutParams就行
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

二、重寫onMeasure方法

重寫onMeasure方法是爲了根據子控件去計算佈局的寬高,並進行設置

 //根據childView的高寬,計算佈局的寬高,並進行設置
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //獲取佈局的父容器爲它設置的測量模式和大小
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        //最後計算的佈局寬高
        int width = 0, height = 0;
        //每一行的寬高
        int lineWidth = 0, lineHeight = 0;
        //循環childView
        for (int i = 0, count = getChildCount(); i < count; i++) {
            View view = getChildAt(i);
            //計算當前childView的高和寬
            measureChild(view, widthMeasureSpec, heightMeasureSpec);
            //得到childView的LayoutParams
            MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
            //得到childView所佔的寬度和高度
            int childWidth = view.getMeasuredWidth() + params.leftMargin + params.rightMargin;
            if (childWidth > widthSize) {
                params.width = widthSize - params.leftMargin - params.rightMargin;
                view.setLayoutParams(params);
                measureChild(view, widthMeasureSpec, heightMeasureSpec);
            }
            params = (MarginLayoutParams) view.getLayoutParams();
            childWidth = view.getMeasuredWidth() + params.leftMargin + params.rightMargin;
            int childHeight = view.getMeasuredHeight() + params.topMargin + params.bottomMargin;

            //如果加入當前childView的寬度會超出父容器計算的寬度話,則需要開啓新的一行,累加height
            //否則累加當前行的寬高
            if (lineWidth + childWidth > widthSize) {
                //取寬度最大值
                width = Math.max(lineWidth, childWidth);
                //累加height
                height += lineHeight;
                //記錄下一行的寬高
                lineWidth = childWidth;
                lineHeight = childHeight;
            } else {
                lineWidth += childWidth;
                lineHeight = Math.max(lineHeight, childHeight);
            }

            //如果是最後一個,則將當前行的寬度與width做比較,累加height
            if (i == count - 1) {
                width = Math.max(lineWidth, width);
                height += lineHeight;
            }
        }

        //設置佈局的寬高
        setMeasuredDimension((widthMode == MeasureSpec.EXACTLY ? widthSize : width),
                (heightMode == MeasureSpec.EXACTLY ? heightSize : height));
    }

三、重寫onLayout方法

重寫onLayout方法是爲了對佈局所有的子控件進行重新排版,對他們的位置進行設置

//對所有的childView位置和大小進行設置
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int widht = getWidth();
        int height = getHeight();

        //記錄的高度
        int recordHeight = 0;
        //每行的寬高
        int lineWidth = 0, lineHeight = 0;
        //每個childView的座標
        int left = 0, top = 0, right = 0, bottom = 0;
        //循環childView
        for (int i = 0, count = getChildCount(); i < count; i++) {
            View view = getChildAt(i);
            //得到childView的LayoutParams
            MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
            //得到childView寬高
            int childWidth = view.getMeasuredWidth();
            int childHeight = view.getMeasuredHeight();
            if (childWidth + params.leftMargin + params.rightMargin + lineWidth > widht) {
                recordHeight = recordHeight + lineHeight;
                left = params.leftMargin;
                right = left + childWidth;
                top = recordHeight + params.topMargin;
                bottom = top + childHeight;
                lineWidth = childWidth + params.leftMargin + params.rightMargin;
                lineHeight = childHeight + params.topMargin + params.bottomMargin;
            } else {
                left = lineWidth + params.leftMargin;
                right = left + childWidth;
                top = recordHeight + params.topMargin;
                bottom = top + childHeight;
                lineWidth = lineWidth + childWidth + params.leftMargin + params.rightMargin;
                lineHeight = Math.max(lineHeight, childHeight + params.topMargin + params.bottomMargin);
            }
            view.layout(left, top, right, bottom);
        }
    }

四、代碼中使用

至此,我們的流式佈局已經寫完,現在去用他進行佈局
佈局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.krubo.flowlayout.MainActivity">

    <com.krubo.flowlayout.FlowLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="遍歷所有的childView"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="用於設置allViews的值"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="根據"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="childView"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="所有的"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="遍歷"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="到此完成了所有的childView的繪製區域的確定,到此,我們的FlowLayout的代碼也結束了~~靜下心來看一看是不是也不難"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="靜下心"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="用於設置allViews的值"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="根據"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="childView"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="所有的"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="#dddddd"
            android:text="遍歷"/>
    </com.krubo.flowlayout.FlowLayout>
</LinearLayout>

運行後的效果圖如下
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章