首页 > 其他分享 >自定义View,流式布局实践

自定义View,流式布局实践

时间:2022-12-29 17:47:11浏览次数:35  
标签:自定义 int 流式 FlowLayout lineHeight import View 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>

  

 

标签:自定义,int,流式,FlowLayout,lineHeight,import,View,view
From: https://www.cnblogs.com/zwq-/p/17013105.html

相关文章

  • 【维生素C语言】第十二章 - C语言自定义类型讲解(联合体、枚举、联合体)
     原标题:自定义类型讲解?楼下保安大爷直呼内行!!【C语言】前言:本章将对C语言自定义类型进行讲解,前期我们讲过结构体,这章将会把前面结构体还没讲完的知识继续补充。一、结构体(st......
  • 图形查看器丨IrfanView功能简介
    IrfanView是一款快速、紧凑和创新的图形查看器,适用于WindowsXP、Vista、7、8、10和11。 IrfanView寻求创建独特、新颖和有趣的功能,与其他一些图形查看器不同,它......
  • vue3+TS 自定义指令:长按触发绑定的函数
    vue3+TS自定义指令:长按触发绑定的函数而然间看到一个在vue2中写的长按触发事件的自定义指定,想着能不能把他copy到我的vue3项目中呢。编写自定义指令时遇到的几个难点1.......
  • windows 10 桌面ctrl alt 自定义快捷键打开程序慢 响应很慢 延迟问题
    思路为:关闭后台应用具体步骤如下:1.)设置,搜索 隐私  2.)找到隐私设置  3.)关闭后台这些后台应用,我都不需要,所以直接全部关闭了,如果有需要可以逐个实验,查找出来到底是哪个后......
  • C# .Net Gridview
    1、分页查询导图 前台:<%@PageLanguage="C#"AutoEventWireup="true"CodeBehind="WebGv2.aspx.cs"Inherits="WebApplication6.WebGv2"%><!DOCTYPEhtml><h......
  • WPF 自定义附加事件
    我们都知道路由事件,而附加事件用的比较少。但如果是通用的场景,类似附加属性,附加事件就很有必要的。举个例子,输入设备有很多种,WPF中输入事件主要分为鼠标、触摸、触笔:WPF......
  • 自定义view01 - 综述
    TypedArrayvaltypedArray:TypedArray=context.obtainStyledAttributes(attrs,R.styleable.EdgeTransparentView)position=typedArray.getI......
  • 自定义View03 - Canvas
    save/savelayerCanvas里面牵扯两种坐标系:Canvas自己的坐标系、绘图坐标系,当Canvas画布被创建时,Canvas的坐标系就被创建了,并且此坐标系是固定不变的,就是(0,0)到Canvas的宽高......
  • Python 自定义label clicked
    classMyQLabel(QtWidgets.QLabel):#自定义信号,注意信号必须为类属性clicked=QtCore.pyqtSignal()def__init__(self,parent=None):super......
  • u-view---calendar日历
    u-view的日历组件默认是无法选择当天之前的日期;例如:   无法选择2022年12月29日之前的日期;解决方法,可以写一个方法,设置最小可选日期和最大可选日期 ......