自定义ViewGroup实现搜索栏历史记录流式布局

Android流式布局实现历史搜索记录功能 最近在开发项目的时候,有一个需求是展示历史搜索记录 ,展示的样式是流式布局(就是根据内容自动换行)。接下来通过本文给大家分享android流式布局实现历史搜索记录功能,感兴趣的的小伙伴参考下 立即下载


前言

自定义view和viewGroup是Android中重要的组成部分,自定义view只要在onDraw()方法中实现,需要判断大小是会用到onMeasure()方法,而自定义viewGroup主要用到onMeasure()方法和onLayout()方法


一、效果图

效果图

二、理论基础

UI绘制流程中:

1、onMeasure()

  • measure()方法被final修饰不可重写,onMeasure()可重写

  • 父view重写onMeasure()方法,调用子view的measure()方法,子view的measure()方法调用子view的onMeasure()方法,以此递归

  • 每个view的onMeasure()方法最终调用setMeasureDimension()方法保存测量结果

  • view:onMeasure()会计算出自己的尺寸然后保存

  • ViewGroup:onMeasure()会调用子view的measure()自我测量(期望值),在根据期望值来计算实际尺寸和位置,同时根据子view的尺寸来计算自己尺寸

  • 如何测量子view?getChildMeasureSpec(),三个参数:spec来自父类,padding来自自己,childDimension来自子view

2、onLayout():摆放子控件

  • onLayout()

  • layoutChildren()

  • clild.layout()

三、实现步骤

1.定义一个类继承自ViewGroup

定义变量:子控件的容器和行高

   //所有的子控件容器
    List<List<View>> list = new ArrayList<>();
    //每一行行高存起来
    List<Integer> listLineHeight = new ArrayList<>();

    //防止测量多次
    private boolean isMeasure = false;

构造方法:

	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);
    }

2.重写 generateLayoutParams

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

3.重写onMeasure()

onMeasure()方法是重点,首先获取父控件给的一个参考值,然后获取到自己的测量模式,保存当前控件里面的子控件的总宽高,每一行的高度值,然后计算实际宽高,注意最后一行要特殊处理,每一行的最后一个item也要特殊处理

	@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //获取父控件给的一个参考值
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        //获取到自己的测量模式
        // 在LinearLayout中measureChildBeforeLayout中把测量规则改成自view的
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        //保存当前控件里面的子控件的总宽高
        int childCountWidth = 0;
        int childCountHeight = 0;

        if(!isMeasure){
            isMeasure = true;
        }else{
            //当前控件中字控件一行使用的宽度值
            int lineCountWidth = 0;

            //保存一行中最高子控件
            int lineMaxHeight = 0;

            //存储每个子控件的宽高
            int iChildWidth = 0;
            int iChildHeight = 0;

            //创建一行的容器
            List<View> viewList = new ArrayList<>();

            //遍历所有子控件
            int childCount = getChildCount();
            for (int x = 0; x < childCount; x++) {
                //获得子控件
                View childAt = getChildAt(x);
                //先测量子控件
                measureChild(childAt,widthMeasureSpec,heightMeasureSpec);
                //从子控件中获取LayoutParams
                MarginLayoutParams layoutParams = (MarginLayoutParams)childAt.getLayoutParams();
                //计算当前控件实际宽高
                iChildWidth = childAt.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
                iChildHeight = childAt.getMeasuredHeight() + layoutParams.bottomMargin + layoutParams.topMargin;
                //当子控件宽度累加后是否大于父控件
                if (iChildWidth +lineCountWidth > widthSize){
                    //需要换行,保存一行的信息
                    //每次换行的时候比较当前行和上一行谁的宽度大
                    childCountWidth = Math.max(lineCountWidth,childCountWidth);
                    //如果需要换行,累加行高
                    childCountHeight += lineMaxHeight;
                    //把行高记录到集合中
                    listLineHeight.add(lineMaxHeight);
                    //把一行的数据放进总容器
                    list.add(viewList);
                    //把一行的容器重新创建一个,新的一行
                    viewList = new ArrayList<>();
                    //将每一行总宽高重新初始化
                    lineCountWidth = iChildWidth;
                    lineMaxHeight = iChildHeight;

                    viewList.add(childAt);
                }else {
                    lineCountWidth += iChildWidth;
                    //对比每个子控件高度谁最高
                    lineMaxHeight = Math.max(lineMaxHeight , iChildHeight);
                    //如果不需要换行,保存在一行中
                    viewList.add(childAt);
                }
                //这样做的原因是  之前的if else中 不会把最后一行的高度加进listLineHeight
                // 最后一行要特殊对待 不管最后一个item是不是最后一行的第一个item
                if(x == childCount - 1){
                    //保存当前行信息
                    childCountWidth = Math.max(lineCountWidth,childCountWidth);
                    childCountHeight +=lineMaxHeight;

                    listLineHeight.add(lineMaxHeight);
                    list.add(viewList);
                }
            }
        }

        //设置控件最终的大小
        int measureWidth = widthMode == MeasureSpec.EXACTLY?widthSize:childCountWidth;
        int measureHeight = heightMode == MeasureSpec.EXACTLY?heightSize:childCountHeight;
        setMeasuredDimension(measureWidth,measureHeight);

    }

4.重新onLayout()

摆放子控件位置,遍历每一行的控件,获取到当前这一行的position

	@Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        //摆放子控件位置
        int left,right,top,bottom;
        //保存上一个控件边距
        int countLeft = 0;
        //保存上一行的高度和边距
        int countTop = 0;
        //遍历所有行
        for (List<View> views : list) {
            //遍历每一行的控件
            for (View view : views) {
                //获取到控件的属性对象
                MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams();
                left = countLeft+layoutParams.leftMargin;
                top = countTop + layoutParams.topMargin;
                right = left+view.getMeasuredWidth();
                bottom = top+view.getMeasuredHeight();
                view.layout(left,top,right,bottom);

                countLeft += view.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
            }
            //获取到当前这一行的position
            int index = list.indexOf(views);
            countLeft = 0;
            countTop+= listLineHeight.get(index);
        }
        list.clear();
        listLineHeight.clear();
    }

5.xml文件

 <com.wuchen.flowui.FlowLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="大家好"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="10dp"
            android:textColor="@color/colorPrimary"
            android:background="@drawable/text_bg"
            android:layout_margin="5dp"
            android:paddingBottom="10dp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="我不好"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:textColor="@color/colorPrimary"
            android:paddingTop="10dp"
            android:layout_margin="5dp"
            android:background="@drawable/text_bg"
            android:paddingBottom="10dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="你好我也好"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="10dp"
            android:textColor="@color/colorPrimary"
            android:background="@drawable/text_bg"
            android:layout_margin="5dp"
            android:paddingBottom="10dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="good"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="10dp"
            android:textColor="@color/colorPrimary"
            android:background="@drawable/text_bg"
            android:layout_margin="5dp"
            android:paddingBottom="10dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="大家好才是真的好"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:textColor="@color/colorPrimary"
            android:paddingTop="10dp"
            android:background="@drawable/text_bg"
            android:layout_margin="5dp"
            android:paddingBottom="10dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="60dp"
            android:text="你不好我就不好"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="10dp"
            android:textColor="@color/colorPrimary"
            android:background="@drawable/text_bg"
            android:layout_margin="5dp"
            android:paddingBottom="10dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="50dp"
            android:text="你好我还是不好"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="10dp"
            android:textColor="@color/colorPrimary"
            android:background="@drawable/text_bg"
            android:layout_margin="5dp"
            android:paddingBottom="10dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="我很好"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="10dp"
            android:textColor="@color/colorPrimary"
            android:background="@drawable/text_bg"
            android:layout_margin="5dp"
            android:paddingBottom="10dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="50dp"
            android:text="你也要很好"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="10dp"
            android:textColor="@color/colorPrimary"
            android:background="@drawable/text_bg"
            android:layout_margin="5dp"
            android:paddingBottom="10dp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="那就大家一起真的好吧"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="10dp"
            android:textColor="@color/colorPrimary"
            android:background="@drawable/text_bg"
            android:layout_margin="5dp"
            android:paddingBottom="10dp"/>
    </com.wuchen.flowui.FlowLayout>

四、demo地址

Demo地址链接

运动目标成像必学:5分钟搞懂keystone变换原理与避坑指南 本文深入浅出地解析了雷达信号处理中的Keystone变换原理,它是解决运动目标距离徙动问题的关键技术。文章通过直观比喻和简化公式,阐述了该变换如何通过变量代换实现距离与慢时间的解耦合,将倾斜轨迹“掰直”,从而提升成像与检测性能。同时,文中重点指出了实际应用中关于大速度场景、插值误差、多目标处理及高阶运动补偿等关键避坑点,为工程实践提供了宝贵指南。 阅读详情

相关推荐

【多元统计分析】均值向量和协方差阵的检验——spss上机实验

均值向量和协方差阵的检验——spss上机实验 #参考书目为《多元统计分析》(第五版)——何晓群.中国人民大学出版社 #如有错误,请指正!谢谢~ #关注公众号搜索同名文章获取数据~ 习题2.3 现选取内蒙古、广西、贵州、云南、西藏、宁夏、新疆、甘肃和青海等9个内陆边远省区。选取人均GDP、第三产业比重、人均消费支出、人口自然增长率及文盲半文盲人口占15岁以上人口的比例等5项能较好的说明各地区社会经济发展水平的指标,验证边远及少数民族聚居区的社会经济发展水平与全国平均水平间有无显著差异。 将输入导入spss

suansuannainaizi的博客 3万+

Android学习 实现带自动补全与历史记录的自定义搜索框

带自动补全与历史记录的自定义搜索框,可

Nicholas_hzf的博客 1125

边表法

边表法是用来绘制填充一个面的,边表法的关键是边表和连接边表的扫描线。 对于边的描述利用边表数据结构,包含边与扫描线的交点x坐标,边的最大y坐标,这样可以知道边加入到哪些扫描线上,便还有一个指针来记录扫描线上其它边。 //边数据结构 #pragma once #ifndef __CAETZ__ #define __CAETZ__ #include "pch.h" #include "CPoin...

weixin_42376458的博客 2101

安卓自定义布局显示流式搜索记录

安卓自定义布局显示流式搜索记录 老规矩,先上效果图(环境:API 30 , AS 4.0) OKK,开始动手! 第一步:自定义流式布局 XFlowLayout ,继承ViewGroup,然后重写 onMeasure()和onLayout()方法 import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import and

C_biubiubiu的博客 536

ThingsBoard 3.1.1版本在window本地运行之TB-Gateway服务搭建(三)

TB-gateway服务是TB不可或缺的部分,很多的协议都需要TB-gateway来做分类处理,今天我们就来做TB-Gateway服务搭建。

晚点遇见你,余生都是你! 2473

易智联LM401模块学习(四)——双串口收发

串口的发送和接受有三种方式,分别是阻塞发送和接收(轮询)、中断方式发送和接收、DMA方式发送和接收。这里我使用阻塞+中断的方式实现双串口通信。简单的串口收发。

weixin_46046597的博客 990

iOS Swift 自适应宽度历史记录标签

自适应宽度历史记录标签 1.png 实现: 自定义 UICollectionViewFlowLayout // // AlignedCollectionViewFlowLayout.swift // // Created by Mischa Hildebrand on 12/04/2017. // Copyright © 2017 Mischa Hildebrand. // // ...

Lee优快云77的博客 799

KEPServerEX中,别名映射(Alias Mapping)数据标签重命名机制

目录核心机制应用场景配置步骤技术特性与参考内容的差异KEPServerEX中,别名映射(Alias Mapping) 是一种数据标签重命名机制,主要用于简化复杂标签路径的调用或实现跨系统兼容。其核心逻辑和功能如下:路径抽象 将设备层级结构中的完整标签路径(如 Channel1.Device1.Tag1)映射为简短别名(如 TempSensor1),降低客户端调用的复杂度。动态绑定 别名与实际标签实时关联,数据更新和读写操作通过别名直接传递到底层物理标签,无需修改客户端配置。多对一映射支持多个别名指向

「东哥说MES」 专注于MES制造执行系统,为你拆解核心架构、功能模块及与ERP/PLC的集成逻辑。提供从入门到精通的系列干货、精选行业报告与实用工具,构建你的数字化知识体系,关注我,用实战经验照亮你的数字化之路。 1591

关于iOS UICollectionView的使用UICollectionViewFlowLayout

1.UICollectionView使用通常伴随着布局文件的使用 当系统默认布局不满足条件的情况下,去请创建自定义类继承UICollectionViewFlowLayout 实现范例: - (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect{          NSMutableArray *attributes =

颜忠贤 JoesRain 684

ARKit面部动画实战:如何用blend shape让数字人表情更自然(附中文对照表)

本文深入探讨了如何利用ARKit的blend shape技术驱动数字人面部动画,以实现更自然生动的表情。文章提供了详细的blend shape中文对照表,并分享了实战技巧,包括构建复合表情动画序列、处理不对称性与微表情,以及性能优化策略,旨在帮助开发者高效提升数字人的表情表现力。

weixin_29292535的博客 324

【图像去噪】论文精读:FM2S: Self-Supervised Fluorescence Microscopy Denoising With Single Noisy Image

论文题目:FM2S: Self-Supervised Fluorescence Microscopy Denoising With Single Noisy Image —— FM2S:单噪声图像自监督荧光显微镜去噪arXiv 2024!显微图像去噪!通过可视化详细的细胞结构和生物过程,荧光显微镜具有显着先进的生物学研究。然而,由于难以精确建模固有噪声和获取干净图像进行训练,这种图像去噪任务往往面临挑战,这限制了大多数现有方法。在本文中,我们提出了一种有效的。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 408

87.android 简单的SearchView搜索框+系统布局搜索框+自定义布局搜索+去除搜索时弹出的黑框

//下面的是使用的系统item布局完成的搜索功能: //第一步 我的Activity布局,一个SearchView,一个ListView: &lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/an...

曹二少的博客 5867

华为OD机试双机位C卷:完美走位 (C++/C/Py/Java/Js/GO)

华为OD机试双机位C卷 完美走位, 提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解。华为OD机考 完美走位, 华为OD机试 完美走位。

qq_45776114的博客 1724

Android~ 流式布局(适用搜索记录)**

##Android~ 流式布局(适用搜索记录) 感谢龙旋分享的文章,今天在做个记录 1.FlowLayout public class FlowLayout extends ViewGroup { private int mHorizontalSpacing = dp2px(16); //每个item横向间距 private int mVerticalSpacing = dp2px(8); //每个item横向间距 private List<List<View>

m0_51282960的博客 368

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Android--实现搜索功能,历史记录显示

Demo地址:https://github.com/chaoyu168/searchHistory 效果图: 搜索界面,显示搜索历史,数据保存在本地数据库。词条维持在10条,短词条可以在三行全部显示完,多出的行数隐藏,长词条默认只显示两行,多出的部分隐藏,点击更多箭头展示全部词条,长按出现删除某个词条弹框,点击清理按钮可以清除所有历史记录。 布局使用到鸿洋大神的流式布局,GitHub地址:...

超宇的博客 1万+

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Android开源实战:手把手教你实现一个简单 & 好用的搜索框(含历史搜索记录

| — | — | | SearchView.java | 搜索框所有功能的实现 | | RecordSQLiteOpenHelper.java | 创建、管理数据库 & 版本控制 | | EditText_Clear.java | 自定义EdiText,丰富了自定义样式 & 一键删除 | | ICallBack.java | 点击搜索按键后的接口回调方法 | | bCallBack.java | 点击返回按键后的接口回调方法 | | SearchListView.java | 解决List

m0_67595789的博客 2528

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Android 实现搜索历史(2)

我这里整理了一份完整的学习思维以及Android开发知识大全PDF。当然实践出真知,即使有了学习线路也要注重实践,学习过的内容只有结合实操才算是真正的掌握。《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!**[外链图片转存中…(img-Tts7bKTT-1712156519271)][外链图片转存中…(img-hKLCjQsu-1712156519272)][外链图片转存中…(img-wJPEmJRZ-1712156519273)]

2401_84010816的博客 1010

自定义view实现历史记录流式布局

在 Android 开发中,流式布局是一种常见的 UI 设计模式,尤其适用于展示动态内容,如标签、历史记录等。本文将介绍如何通过自定义 View 来实现一个历史记录的流式布局。

weixin_63357306的博客 691

android 搜索历史流布局,FlowLayout流式布局实现搜索清空历史记录

本文实例为大家分享了FlowLayout实现搜索清空历史记录的具体代码,供大家参考,具体内容如下效果图:点击搜索框将搜索的历史在流式布局中展示出来,清空历史记录就会将历史清空,每次搜索后都存入sp中,每次进入页面都先判断sp里是否有值并展示首先需要导入一个module,下载地址下载完这个工程后,需要将里面的flowlayout-lib导入到工程中,导入工程的步骤:File - New - Impo...

weixin_35501677的博客 483

Android 自定义流式布局 实现淘宝搜索记录

效果如下: 废话不多说 实现代码: attrs.xml <declare-styleable name="TagFlowLayout"> <!--最大选择数量--> <attr name="max_select" format="integer"/> <!--最大可显示行数--> <attr name="limit_line_count" format="integer"/>

qq_42795723的博客 1369

Android自定义流式布局(搜索历史记录)

仅供参考: 外层布局: //布局名为layout_liu <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> ...

_万能的博哥的博客 1620

android自定义搜索记录,Android流式布局实现历史搜索记录功能

最近在开发项目的时候,有一个需求是展示历史搜索记录 ,展示的样式是流式布局(就是根据内容自动换行)。在网上看到了一个不错的类库跟大家分享一下首先在AndroidStudio简历一个工程项目导入module类库,我会把项目demo方法GitHub上说一下demo中的实现方式在 activity_main.xml中xmlns:app="http://schemas.android.com/apk/re...

weixin_42422648的博客 452

Android常用:手把手教你实现搜索框(含历史搜索记录)

<div class="bog_copyright"> <p class="copyright_p" >版权声明:本文为博主原创文章,未经博主允许不得转载,更多文章请继续关注Carson_Ho的博客!</p> </div> 前言 像下图的搜索功能在Android开发中非常常见今天我将手把手教大家如何实现具备历史搜索记录的搜索框 目录

l_blackeagle的博客 4772

自定义View 流式布局(历史搜索,热门搜索)

xml布局 &lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools=&quot

weixin_43575489的博客 1688

流式布局清空历史搜索

依赖: dependencies { compile 'com.github.LRH1993:AutoFlowLayout:1.0.5' } public class MainActivity extends AppCompatActivity implements View.OnClickListener { private MySearch mMysearch; ...

user_app的博客 354

android -----自定义View实现搜索添加到历史纪录

===============Flowlayout============= public class XCflowLayout extends ViewGroup { //储存所有子View List<List<View>> mAllChildV5iews = new ArrayList<>(); //每行的高度 List&l...

why_111的博客 670
上一篇: Glide中图片处理
下一篇: kotlin语法总结(一)
浮空over
博客等级 码龄7年 6粉丝 33原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值