4.3 自定义控件 之 继承ViewGroup实现标签云

开源库3dTagCloudAndroid使用,实现3D球形云标签tag效果 TagCloudView是一个完全基于Android ViewGroup编写的控件,支持 将一组View展示为一个3D球形集合,并支持全方向滚动。 GitHub中的链接地址:https://github.com/misakuo/3dTagCloudAndroid 页面上标签的数据可以自己定义,数据页面可以滑动选择。 使用 1.Eclipse copy代码,或使用ADT的maven插件 2.And... 阅读详情

点此进入:从零快速构建APP系列目录导图
点此进入:UI编程系列目录导图
点此进入:四大组件系列目录导图
点此进入:数据网络和线程系列目录导图

实现一个知识点的标签显示,每个标签的长度未知,如下图所示:

本篇的控件涉及到的内容比较多,所以先介绍下View的绘制流程、相关回调方法等,避免后面用到的时候不知道什么意思。

一、View绘制流程

1、mesarue() 测量过程

主要作用:为整个 View 树计算实际的大小,即设置实际的高(对应属性:mMeasuredHeight)和宽(对应属性:mMeasureWidth),每个 View 的控件的实际宽高都是由父视图和本身视图决定的。

具体的调用链如下:ViewRoot 根对象的属性 mView(其类型一般为 ViewGroup 类型)调用 measure()方法去计算 View 树的大小,回调 View/ViewGroup 对象的 onMeasure() 方法,该方法实现的功能如下:
1、设置本 View 视图的最终大小,该功能的实现通过调用 setMeasuredDimension()方法去设置实际的高(对应属性:mMeasuredHeight)和宽(对应属性:mMeasureWidth)。
2 、如果该 View 对象是个 ViewGroup 类型,需要重写该 onMeasure() 方法,对其子视图进行遍历的measure() 过 程 。 对 每 个 子 视 图 的 measure() 过 程 , 是 通 过 调 用 父 类 ViewGroup.java 类 里 的measureChildWithMargins() 方法去实现,该方法内部只是简单地调用了 View 对象的 measure() 方法。

2、layout() 布局过程

主要作用:为将整个根据子视图的大小以及布局参数将 View 树放到合适的位置上。
具体的调用链如下:
1、layout 方法会设置该 View 视图位于父视图的坐标轴,即 mLeft,mTop,mLeft,mBottom(调用setFrame()函数去实现)接下来回调 onLayout()方法(如果该 View 是 ViewGroup 对象,需要实现该方法,对每个子视图进行布局)。
2、如果该 View 是个 ViewGroup 类型,需要遍历每个子视图 childView,调用该子视图的 layout() 方法去设置它的坐标值。
3、draw()绘图过程
由 ViewRoot 对象的 performTraversals() 方法调用 draw() 方法发起绘制该 View 树,值得注意的是每次发起绘图时,并不会重新绘制每个 View 树的视图,而只会重新绘制那些“需要重绘”的视图,View 类内部变量包含了一个标志位 DRAWN,当该视图需要重绘时,就会为该 View 添加该标志位。

调用流程 :

1 、绘制该 View 的背景
2 、为显示渐变框做一些准备操作(大多数情况下,不需要改渐变框)
3、调用 onDraw() 方法绘制视图本身(每个 View 都需要重载该方法,ViewGroup 不需要实现该方法)
4、调用 dispatchDraw() 方法绘制子视图(如果该 View 类型不为 ViewGroup,即不包含子视图,不需要重载该方法)

值得说明的是,ViewGroup 类已经为我们重写了 dispatchDraw() 的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。

另外,关于 invalidate() 方法的介绍,大家可以参照这篇:Android中View绘制流程以及invalidate()等相关方法分析

二、自定义标签云类的实现

1、自定义属性

<declare-styleable name="TagsLayout">
        <attr name="tagVerticalSpace" format="dimension" />
        <attr name="tagHorizontalSpace" format="dimension" />
</declare-styleable>

2、构造函数中获取自定义属性值

    public TagsLayout(Context context) {
        super(context);
        mContext = context;
        init();
    }

    public TagsLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        mAttributeSet = attrs;
        init();
    }

    public TagsLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        mAttributeSet = attrs;
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public TagsLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mContext = context;
        mAttributeSet = attrs;
        init();
    }

    private void init() {
        TypedArray attrArray = mContext.obtainStyledAttributes(mAttributeSet, R.styleable.TagsLayout);
        if (attrArray != null) {
            mChildHorizontalSpace = attrArray.getDimensionPixelSize(R.styleable.TagsLayout_tagHorizontalSpace, 0);
            mChildVerticalSpace = attrArray.getDimensionPixelSize(R.styleable.TagsLayout_tagVerticalSpace, 0);
            attrArray.recycle();
        }
    }

3、onMeasure函数测量子控件大小,然后设置当前控件大小

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 获得它的父容器为它设置的测量模式和大小
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
        // 遍历每个子元素
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == GONE)
                continue;
            // 测量每一个child的宽和高
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            // 得到child的lp
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            // 当前子空间实际占据的宽度
            int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin + childHorizontalSpace;
            // 当前子空间实际占据的高度
            int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin + childVerticalSpace;
            /**
             * 如果加入当前child,则超出最大宽度,则的到目前最大宽度给width,类加height 然后开启新行
             */
            if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) {
                width = Math.max(lineWidth, childWidth);// 取最大的
                lineWidth = childWidth; // 重新开启新行,开始记录
                // 叠加当前高度,
                height += lineHeight;
                // 开启记录下一行的高度
                lineHeight = childHeight;
                child.setTag(new Location(left, top + height, childWidth + left - childHorizontalSpace, height + child.getMeasuredHeight() + top));
            } else {// 否则累加值lineWidth,lineHeight取最大高度
                child.setTag(new Location(lineWidth + left, top + height, lineWidth + childWidth - childHorizontalSpace + left, height + child.getMeasuredHeight() + top));
                lineWidth += childWidth;
                lineHeight = Math.max(lineHeight, childHeight);
            }
        }
        width = Math.max(width, lineWidth) + getPaddingLeft() + getPaddingRight();
        height += lineHeight;
        sizeHeight += getPaddingTop() + getPaddingBottom();
        height += getPaddingTop() + getPaddingBottom();
        setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight : height);
    }

通过遍历所有子控件调用measureChild函数获取每个子控件的大小,然后通过宽度叠加判断是否换行,叠加控件的高度,同时记录下当前子控件的坐标,这里记录坐标引用了自己写的一个内部类Location.java。

4、onLayout函数对所有子控件重新布局

    private class Location {
        private int left;
        private int top;
        private int right;
        private int bottom;
        private Location(int left, int top, int right, int bottom) {
            this.left = left;
            this.top = top;
            this.right = right;
            this.bottom = bottom;
        }
    }

三、自定义标签云的使用

1、在布局文件中直接引用

    <com.wgh.willflowcloudtag.TagsLayout
        android:id="@+id/tagsLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal">
    </com.wgh.willflowcloudtag.TagsLayout>

2、在MainActivity中用代码添加标签

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TagsLayout tagsLayout = (TagsLayout) findViewById(R.id.tagsLayout);
        ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        String[] string={"锄禾日当午","汗滴禾下土", "metacognition", "WillFlow","谁知盘中餐","粒粒皆辛苦"};
        for (int i = 0; i < string.length; i++) {
            TextView textView = new TextView(this);
            textView.setText(string[i]);
            textView.setTextColor(getResources().getColor(R.color.colorAccent));
            textView.setBackgroundResource(R.drawable.a);
            tagsLayout.addView(textView, lp);
        }
    }

至此有关简单的自定义控件已经介绍的差不多了,项目中很复杂的控件现在涉及的比较少,以后用到之后再做记录。

最后,给大家介绍一款开源3D标签云:3D标签云

点此进入:GitHub开源项目“爱阅”。“爱阅”专注于收集优质的文章、站点、教程,与大家共分享。下面是“爱阅”的效果图:


联系方式:

简书:WillFlow
优快云:WillFlow
微信公众号:WillFlow

微信公众号:WillFlow

AAC编解码器在实时通信中的性能对比与应用选择 本文深入分析了AAC编解码器在实时通信中的性能优化,重点对比了faac、faad2与fdk-aac三大开源方案在WebRTC场景下的表现。通过实测数据揭示其在延迟、音质和资源消耗等关键指标的差异,并提供场景化选型建议与调优技巧,帮助开发者提升实时音视频通信质量。 阅读详情

相关推荐

STM32F407 CubeMX UART DMA高效解析BNO085九轴数据

本文详细介绍了如何在STM32F407平台上使用CubeMX配置UART和DMA高效解析BNO085九轴传感器数据。通过DMA循环接收机制实现低CPU占用的实时数据采集,并分享数据解析技巧、错误处理及实战调试经验,为嵌入式开发者提供完整解决方案。

o4p5q6r7s的博客 236

自定义ViewGroup实现标签云效果。

参考了这篇博文:http://blog.csdn.net/wangjinyu501/article/details/38089061 实现效果: 1.标签排列,右对齐。 2.点击第一个标签添加标签。 3.点击非第一个标签,可以将标签删除。 效果图: 因为是在别人的基础上修改的,我就只把我修改的部分代码贴出来 第一点 为了实现从右边开始排列,重写了FlowLayout

拂过琴弦的风的专栏 555

深信服AC1000-B1200到手第一步:从开箱到激活上网的保姆级图文指南

本文提供深信服AC1000-B1200企业级行为管理设备的详细开局使用指南,涵盖从开箱验机、物理连接到网络配置、安全加固及设备激活的全流程。通过图文并茂的步骤解析,帮助用户快速完成设备初始化并实现安全上网,特别适合初次接触该设备的网络管理员参考。

weixin_42546392的博客 548

Android中View绘制流程以及invalidate()等相关方法分析

转载请注明出处:http://blog.csdn.net/qinjuning 前言: 本文是我读《Android内核剖析》第13章----View工作原理总结而成的,在此膜拜下作者。同时真挚地向渴望了解 Android 框架层的网友,推荐这本书,希望你们能够在Android开发里学到更多的知识 。 整个View树的绘图流程是在ViewRoot.java类的perfor...

SunRaIN_的专栏 1397

NLP实践——利用自己的语料进行Mask Language Model预训练

#!/usr/bin/env python # coding: utf-8 # In[1]: import os import json import copy from tqdm.notebook import tqdm import torch from torch.optim import AdamW from torch.utils.data import DataLoader, Dataset from transformers import BertForMaskedLM, BertTo

weixin_44826203的博客 1万+

MFC Tab Control控件完全指南:从基础使用到高级定制

本文详细介绍了MFC中TabControl控件的使用方法和高级技巧。主要内容包括:1) TabControl基础属性设置与绑定;2) 样式定制和外观自定义;3) 创建派生类实现扩展功能;4) 动态页面管理和子对话框控制;5) 高级功能如标签拖拽排序和右键菜单。通过完整代码示例,展示了从基本应用到复杂场景的实现方案,包括多标签页管理、自定义绘制、动态添加/删除标签等核心功能,为MFC开发者提供了全面的TabControl开发指南。

专注现代C++实战,从核心语法到高性能编程,助力开发者深入掌握系统级编程精髓。 259

了解自定义View和继承View,继承ViewGroup,继承已有View,继承已有ViewGroup实例ji

自定义View的分类 继承View 当我们需要实现的效果是一个不规则效果的时候,那么这时就需要继承 View 来实现了,我们需要重写 onDraw 方法,在该方法里实现各种不规则的图形和效果。当我们使用这种方式的时候,需要自己去处理 warp_content 和 padding。 继承ViewGroup 当系统所提供的 LinearLayout、FrameLayout 等布局控件无法满

github_37217206的博客 3487

122、Android Camera HAL3 框架:Request、Result、Metadata 的异步管道模型

本文深入剖析了Android Camera HAL3框架中的异步管道模型,围绕Request、Result和Metadata三个核心要素展开。作者通过实际案例(黑屏闪退问题)切入,揭示了异步模型中常见的帧序错乱陷阱。文章详细阐释了三个角色的交互机制,指出HAL3采用生产者-消费者模式的关键特征:Request与Result一一对应但不保证顺序。针对开发实践,总结了三个必须加锁的场景和回调函数的锁使用禁忌,并给出解决帧序问题的具体方案(独立帧号生成器)。最后提出五项经验性建议,强调异步模型带来的灵活性与复杂性

sundong_81的博客 214

Android 自定义ViewGroup之实现FlowLayout-标签流容器

本篇文章讲的是Android 自定义ViewGroup之实现标签流式布局-FlowLayout,开发中我们会经常需要实现类似于热门标签等自动换行的流式布局的功能,网上也有很多这样的FlowLayout,但不影响我对其的学习。和往常一样,主要还是想总结一下自定义ViewGroup的开发过程以及一些需要注意的地方。按照惯例,我们先来看看效果图 一、写代码之前,有几个是问题是我们先要弄清楚的: 1、什

Angelica 7781

从入门到精通|Yalmip+Cplex在电力系统中的应用(Matlab代码实现)

​ 火电成本包括运行成本、碳捕集成本,其中火电运行成本由运行维护成 本和发电煤耗成本构成,发电煤耗与其出力关系: ,F 为机组煤耗量(kg/h),P 为机组出力/MW;运行维护成本按照 0.5 倍煤耗成本考虑,碳捕集成本取决于碳排放量及碳捕集单价,火电机组相关参数如附表 1 所示,电煤价格为 700 元/t ​

weixin_46039719的博客 1553

【嵌入式Linux驱动开发】二十四、Linux I2C 驱动上手尝试

  人的前程关于眼界、关乎格局。   志之所趋,无远弗届,穷山复海不能限也;   志之所向,无坚不入,锐兵精甲不能御也。 一、I2C驱动框架简介   Linux内核将 I2C 驱动分为两部分: ①、 I2C 总线驱动, I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。 ②、 I2C 设备驱动, I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。 1....

悟已往之不谏 知来者之可追 1069

Android自定义控件(2)--自定义ViewGroup/组合控件

自定义ViewGroup

SPACESTUDIO的博客 1759

如何离线环境下安装Dify插件 (可提供远程指导协助)

离线环境下安装Dify插件

litlit023的专栏 8101

android标签容器,Android自定义ViewGroup实现标签流容器FlowLayout

本篇文章讲的是Android 自定义ViewGroup之实现标签流式布局-FlowLayout,开发中我们会经常需要实现类似于热门标签等自动换行的流式布局的功能,网上也有很多这样的FlowLayout,但不影响我对其的学习。和往常一样,主要还是想总结一下自定义ViewGroup的开发过程以及一些需要注意的地方。按照惯例,我们先来看看效果图一、写代码之前,有几个是问题是我们先要弄清楚的:1、什么是V...

weixin_29415667的博客 362

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

假人有限元模型

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

Android中常见的热门标签的流式布局的实现——云标签

一、概述: 在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧(源码下载在下面最后给出) 类似的自定义布局。下面我们就来详细介绍流式布局的应用特点以及用的的技术点: 1.流式布局的特点以及应用场景     特点:当上面一行的空间不够容纳新的TextView时候,    

尐新^^沒蜡笔的博客 1617

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

自定义ViewGroup实现动态创建可换行标签

数 3.实现onMeasure方法 a.在这个方法里面首先要做是要知道自己的大小,onMeasure方法会通过父类获取具体的模式和大小。通过getMode方法获得模式(三种模式就不详细说了),然后通过getSize方法获取具体的尺寸。 b.通过遍历子控件得到ViewGroup显示时的高度。当子控件(即标签)的宽度之和大于父控件的时候开启行并累加高度 c.setMeasuredDimension((modeWidth == MeasureSpec.EXACTL

renyuan1991的博客 158

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

android+标签云效果,Android实现3D标签云简单效果

本文实例为大家分享了Android实现3D标签云效果展示的具体代码,供大家参考,具体内容如下一、关于3D标签云TagCloudView是一个完全基于Android ViewGroup编写的控件,支持将一组View展示为一个3D标签云,并支持全方向滚动。GitHub中的链接地址(一)效果页面上标签的数据可以自己定义,数据页面可以滑动选择。(二)AndroidStudio中使用1.在build.gra...

weixin_33168819的博客 457

Android自定义View详解

一、自定义View基础 1、分类 自定义View的实现方式有以下几种: 自定义组合控件: 多个控件组合成为一个新的控件,方便多处复用 继承系统View控件: 继承自TextView等系统控件,在系统控件的基础功能上进行扩展 继承View: 不复用系统控件逻辑,继承View进行功能定义 继承系统ViewGroup: 继承自LinearLayout等系统控件,在系统控件的基础功能上进行扩展 继承V...

恬静释然的博客 1013

Android标签流控件的实现

在我们的开发过程中,常常会遇到这样的场景: 我们展示一种物品或者为某一事物添加一些标签。比如说,我们买一件衣服,可以有以下几种标签:杰克琼斯,男士,运动等等。 但我们这时候可能并不知道标签的数量和每个标签的文字,所以,我们在开发过程中,需要实现下面的功能: 我们从服务器端获取标签的信息,然后将其动态的添加到布局中,并且我们能够得到我们选择容器的信息,并将选中的标签重新返回至服务器。 因此,

wk843620202的专栏 4578

android tag的使用方法,开源库3dTagCloudAndroid使用,实现3D球形云标签tag效果

TagCloudView是一个完全基于Android ViewGroup编写的控件,支持将一组View展示为一个3D球形集合,并支持全方向滚动。页面上标签的数据可以自己定义,数据页面可以滑动选择。效果图使用1.Eclipsecopy代码,或使用ADT的maven插件2.Android Studio / IDEA在build.gradle中添加implementation compile 'com....

weixin_35738619的博客 554

Android自定义控件之基本原理

前言: 在日常的Android开发中会经常和控件打交道,有时Android提供的控件未必能满足业务的需求,这个时候就需要我们实现自定义一些控件,今天先大致了解一下自定义控件的要求和实现的基本原理。 自定义控件相关文章地址: Android自定义控件之基本原理 Android自定义控件之自定义属性 Android自定义控件之自定义组合控件 Android...

231

Android中常见的热门标签的流式布局的实现

Android中常见的热门标签的流式布局的实现

qq_43685172的博客 2167

android自定义view流布局,Android控件进阶-自定义流式布局和热门标签控件

一、概述:在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何自定义一个类似热门标签那样的流式布局吧类似的自定义换行流式布局控件。下面我们就来详细介绍流式布局的应用特点以及用的的技术点:1.流式布局的特点以及应用场景 特点:当上面一行的空间不够容纳新的TextView时候, 才开辟下一行的空间原理图:场景:主要用于关键词搜索...

weixin_35881820的博客 230

android自定义view获取控件,Android自定义View全解

目录目录.png1. 自定义View基础1.1 分类自定义View的实现方式有以下几种类型定义自定义组合控件多个控件组合成为一个新的控件,方便多处复用继承系统View控件继承自TextView等系统控件,在系统控件的基础功能上进行扩展继承View不复用系统控件逻辑,继承View进行功能定义继承系统ViewGroup继承自LinearLayout等系统控件,在系统控件的基础功能上进行扩展继承View...

weixin_36335903的博客 1040
上一篇: 4.2 自定义控件 之 自定义属性与引入布局
下一篇: 用 Kotlin 开启我们的 Activity
WillFlow
博客等级 码龄11年 61粉丝 81原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值