自定义View,流式布局实践

 

一、效果图:

 

二、FlowLayout实现

package com.example.widgetsview;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

public class FlowLayout extends ViewGroup {

    private int mHorizontalSpacing = dp2px(16); //每个item横向间距

    private int mVerticalSpacing = dp2px(8); //每个item纵向间距

    private List<List<View>> allLines = new ArrayList<>(); // 记录所有的行,一行一行的存储,用于layout
    private List<Integer> lineHeights = new ArrayList<>(); // 记录每一行的行高,用于layout


    public FlowLayout(Context context) {
        super(context);
    }

    // 反射
    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    // 主题
    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

    private void clearMeasureParams() {
        allLines.clear();
        lineHeights.clear();
    }

    /*
     * 1、度量子view
     * 2、获取子view的宽、高、换行等
     * 3、向父类索要宽高,判断是哪种MeasureSpecMode,根据不同的mode给出不同的区域
     * 4、保存记录
     * */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        clearMeasureParams();

        List<View> lineViews = new ArrayList<>();  // 保存一行中的所有的view
        int lineWidthUsed = 0;  // 记录这行已经使用了多宽的size
        int lineHeight = 0;  // 一行的行高

        int selfWidth = MeasureSpec.getSize(widthMeasureSpec);  // ViewGrooup解析的父亲给我的宽度,父view给我的宽度
        int selfHeight = MeasureSpec.getSize(heightMeasureSpec);  // ViewGroup解析的父亲给我的高度,父view给我的高度

        int flowLayoutNeedWidth = 0;  // measure过程中,FlowLayout要求的父ViewGroup的宽
        int flowLayoutNeedHeight = 0; // measure过程中,FlowLayout要求的父ViewGroup的高

        // 度量孩子大小
        int childCount = getChildCount();  // 获取子view数
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();

        for (int i = 0; i < childCount; i++){
            View childView = getChildAt(i);
            // 获取子view的layoutParams,通过layoutParams可得到子view的宽高具体值或者MATCH_PARENT还是WRAP_CONTENT
            LayoutParams childLp = childView.getLayoutParams();  // xml里的layout_width等
            if (childView.getVisibility() != View.GONE) {
                //将layoutParams转变成为 measureSpec  即设置子view的measureSpec
                /*
                 * widthMeasureSpec表示父view给予FlowLayout的宽度
                 * paddingLeft + paddingRight表示父view所设置的左右padding值
                 * childLP.width  表示 子view的宽度
                 * */
                int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight, childLp.width);
                int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom, childLp.height);
                // 通过子view的measureSpec度量子view
                childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
                // 获取子view的度量宽高
                int childViewMeasuredWidth = childView.getMeasuredWidth();
                int childViewMeasuredHeight = childView.getMeasuredHeight();

                // 换行, 通过宽度来判断是否需要换行,通过换行后的每行的行高来获取整个ViewGroup的行高
                // 如果需要换行
                if (childViewMeasuredWidth + lineWidthUsed + mHorizontalSpacing > selfWidth) {
                    //一旦换行,我们就可以判断当前行需要的宽和高,所以此时要记录下来
                    allLines.add(lineViews);
                    lineHeights.add(lineHeight);
                    //判断flowLayout到底需要多宽、多高
                    flowLayoutNeedWidth = Math.max(flowLayoutNeedWidth, lineWidthUsed);
                    flowLayoutNeedHeight = flowLayoutNeedHeight + lineHeight + mVerticalSpacing;

                    // 换行后初始化
                    // 此处不能用clear,用clear则allLines里面的item所指向的就是同一个内存地址了
                    lineViews = new ArrayList<>();
                    lineWidthUsed = 0;
                    lineHeight = 0;
                }

                //每行的设置,// 将节点布局进去,view是分行layout的,所以要记录每一行有哪些view,这样可以方便layout布局
                lineViews.add(childView);
                lineWidthUsed = lineWidthUsed + childViewMeasuredWidth + mHorizontalSpacing ;
                lineHeight = Math.max(lineHeight, childViewMeasuredHeight);

                //最后一行数据(因为最后一行的时候到不了换行的那句代码,所以不会显示,因此要单独判断)
                if(i == childCount - 1){
                    allLines.add(lineViews);
                    lineHeights.add(lineHeight);
                    //判断flowLayout到底需要多宽、多高
                    flowLayoutNeedWidth = Math.max(flowLayoutNeedWidth, lineWidthUsed);
                    flowLayoutNeedHeight = flowLayoutNeedHeight + lineHeight + mVerticalSpacing;
                }
            }
        }

        //根据子View的度量结果,来重新度量自己ViewGroup, 作为一个ViewGroup,它自己也是一个View,它的大小也需要根据它的父view给它提供的宽高来度量
        //首先获取到父view的MeasureSpec的mode
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        // 如果父view的MeasureSpec的mode是EXACTLY表示宽度是确切的,则selfWidth为最终宽度,否则为
        int flowLayoutWidth = (widthMode == MeasureSpec.EXACTLY) ? selfWidth : flowLayoutNeedWidth;
        int flowLayoutHeight = (heightMode == MeasureSpec.EXACTLY) ? selfHeight : flowLayoutNeedHeight;

        // 保存记录
        setMeasuredDimension(flowLayoutWidth, flowLayoutHeight);
    }

    // 布局: 所有的子View进行布局(每一行每一行的布局)
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // 获取行数
        int lineCount = allLines.size();
        // 获取flowLayout所设置的pandding值,布局从左上角开始
        int curL = getPaddingLeft();
        int curT = getPaddingTop();

        for (int i = 0; i < lineCount; i++) {
            // 获取到每一行的所有view
            List<View> lineViews = allLines.get(i);

            for (int j = 0; j < lineViews.size(); j++){
                //获取单个view
                View view = lineViews.get(j);
                //设置view的视图坐标系,
                int left = curL;
                int top = curT;
                int right = left + view.getMeasuredWidth();  // 度量之前必须用getMeasuredWidth不能用getWidth()
                int bottom = top + view.getMeasuredHeight();
                // view添加到布局
                view.layout(left,top,right,bottom);

                // 计算下一个view的宽度的开始位置
                curL = right + mHorizontalSpacing;
            }

            // 宽度位置初始化
            curL = getPaddingLeft();
            // 计算下一行view的高度的开始位置
            curT = curT + lineHeights.get(i) + mVerticalSpacing;

        }
    }

    // 动画:1s 12帧
    @Override
    protected void onDraw(Canvas canvas){

    }

    public static int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
    }
}

三、布局activity  xml文件

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="30dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            android:text="搜索历史"
            android:textColor="@android:color/black"
            android:textSize="18sp"/>
        <com.example.widgetsview.FlowLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="4dp">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="欧美影视"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="婚姻育儿"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="散文"
                android:textColor="#130F01" />


            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="程序员"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="大学生生活"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="运营互助帮"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="设计"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="读书"
                android:textColor="#130F01" />
        </com.example.widgetsview.FlowLayout>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            android:text="热门搜索"
            android:textColor="@android:color/black"
            android:textSize="18sp"/>
        <com.example.widgetsview.FlowLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="4dp">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="新冠疫情最新资讯"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="新冠疫情最新资讯test"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="散文"
                android:textColor="#130F01" />


            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="程序员"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="大学生生活"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="运营互助帮"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="设计"
                android:textColor="#130F01" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:background="#FDF0C7"
                android:padding="5dp"
                android:text="读书"
                android:textColor="#130F01" />
        </com.example.widgetsview.FlowLayout>
    </LinearLayout>

</LinearLayout>

  

 

posted @ 2022-12-29 17:31  清风_Z  阅读(26)  评论(0编辑  收藏  举报