Android自定义ViewGroup实现标签流效果

Android自定义View实现标签流效果 一、概述Android自定义View实现标签流效果,一行放不下时会自动换行,用户可以自己定义单个标签的样式,可以选中和取消,效果图如下: 二、实现代码1.自定义View定义属性文件<declare-styleable name="FlowTagView"> <attr name="lineSpacing" format="dimension" /> <attr na 阅读详情

logo

自定义View,一是为了满足设计需求,二是开发者进阶的标志之一。随心所欲就是我等奋斗的目标!!!

效果

在这里插入图片描述

实现逻辑

  • 明确需求

    1、标签流效果;
    2、可以动态添加标签;
    3、标签需要有点击效果以及回调;

  • 整理思路

    既然要装载标签,就需要自定义ViewGroup ,而自定义ViewGroup 比较复杂的就是onLayout()中对子View的排版。既然是标签,在一行中一定要显示完整,在排版的时候注意这一点,需要添加判断!其次,标签要有点击事件,这里的实现我们可以借助子View的点击事件封装一个接口,实现我们自己的点击事件!

  • 要点

    1、对于子View的测量;
    2、对于子View的排版;

  • Ps: 需要注意的是给子View设置背景Drawable的时候不可以设置同一个Drawable,否则会出现错乱情况!见getSonView()方法中

完整代码

/**
 * 自定义ViewGroup实现标签流效果
 *
 * @attr customInterval  //标签之间的间隔
 * @attr customSelectMode  //标签选项模式
 * @attr customSonBackground   //标签背景
 * @attr customSonPaddingBottom   //标签底部内边距
 * @attr customSonPaddingLeft  //标签左边内边距
 * @attr customSonPaddingRight  //标签右边内边距
 * @attr customSonPaddingTop  //标签顶部内边距
 * @attr customSonTextColor  //标签文字颜色
 * @attr customSonTextSize  //标签文字尺寸
 */
public class CustomLableView extends ViewGroup {
    private static final String TAG = "CustomLableView";
    private int customInterval = 15;
    private int customSonPaddingLeft = 20;
    private int customSonPaddingRight = 20;
    private int customSonPaddingTop = 10;
    private int customSonPaddingBottom = 10;
    private Drawable customSonBackground = null;
    private float customSonTextSize = 0;
    private ColorStateList customSonTextColor = ColorStateList.valueOf(0xFF000000);
    private ArrayList<String> mSonTextContents = new ArrayList<>();
    private ArrayList<TextView> mSonTextViews = new ArrayList<>();
    private Context mContext = null;
    private OnItemClickListener mOnItemClickListener;
    private int customSelectMode;

    public CustomLableView(Context context) {
        this(context, null);
    }

    public CustomLableView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomLableView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        //初始化自定义属性
        initAttrs(context, attrs);
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int left = customInterval;
        int top = customInterval;
        int mMeasuredWidth = this.getMeasuredWidth();
        //防止重复添加
        CustomLableView.this.removeAllViews();
        for (int i = 0; i < mSonTextViews.size(); i++) {
            final TextView mTextView = mSonTextViews.get(i);
            //将当前子View添加到ViewGroup中
            CustomLableView.this.addView(mTextView);
            //获取当前子View的宽高
            int measuredHeight = mTextView.getMeasuredHeight();
            int measuredWidth = mTextView.getMeasuredWidth();
            //判断一行是否可显示
            if ((mMeasuredWidth - left) >= (measuredWidth + customInterval * 2)) {//一行可显示
                /**
                 * 1、(mMeasuredWidth - left) X轴剩余空间
                 * 2、(measuredWidth + customInterval * 2) 当前子View和间隔需要的空间
                 */
                mTextView.layout(left, top, left + measuredWidth, top + measuredHeight);
                left += (measuredWidth + customInterval);
            } else {//需要换行显示
                //还原X轴的起始位置
                left = customInterval;
                //Y轴高度增加
                top += (measuredHeight + customInterval);
                mTextView.layout(left, top, left + measuredWidth, top + measuredHeight);
                left += (measuredWidth + customInterval);
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //控件宽度
        int mMeasureViewWidht = MeasureSpec.getSize(widthMeasureSpec);
        //显示行数
        int line = 1;
        //每行当前宽度
        int lineWidht = customInterval;
        //每行高度(子View的高度)
        int mSonMeasuredHeight = 0;
        mSonTextViews.clear();
        for (int i = 0; i < mSonTextContents.size(); i++) {
            TextView mSonView = getSonView(i, mSonTextContents.get(i));
            //对子View进行测量
            mSonView.measure(0, 0);
            //获取子View的测量尺寸
            int mSonMeasuredWidth = mSonView.getMeasuredWidth() + customInterval;
            mSonMeasuredHeight = mSonView.getMeasuredHeight() + customInterval;
            //添加到数组中
            mSonTextViews.add(mSonView);
            if (mMeasureViewWidht >= mSonMeasuredWidth) {
                if ((mMeasureViewWidht - lineWidht) >= mSonMeasuredWidth) {
                    lineWidht += mSonMeasuredWidth;
                } else {
                    //行数自加1
                    line += 1;
                    lineWidht = customInterval + mSonMeasuredWidth;
                }
            } else {
                mSonTextViews.clear();
                setMeasuredDimension(0, 0);
                return;
            }
        }
        //设置控件尺寸
        setMeasuredDimension(mMeasureViewWidht, mSonMeasuredHeight * line + customInterval);
    }

    /**
     * 设置标签内容集合
     *
     * @param sonContent 标签内容
     */
    public void setSonContent(ArrayList<String> sonContent) {
        if (sonContent != null) {
            mSonTextContents.clear();
            mSonTextContents.addAll(sonContent);
            requestLayout();
        }
    }

    /**
     * 添加一个标签
     *
     * @param sonContent 标签内容
     */
    public void addSonContent(String sonContent) {
        if (!TextUtils.isEmpty(sonContent)) {
            mSonTextContents.add(0, sonContent);
            requestLayout();
        }
    }

    /**
     * 设置标签点击事件
     *
     * @param onItemClickListener 回调接口
     */
    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.mOnItemClickListener = onItemClickListener;
    }

    /**
     * 获取子View
     *
     * @return TextView
     */
    private TextView getSonView(final int i, final String content) {
        if (mContext != null) {
            TextView mTextView = new TextView(mContext);
            mTextView.setTextColor(customSonTextColor);
            mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, customSonTextSize);
            //不可以设置相同的Drawable
            mTextView.setBackgroundDrawable(customSonBackground.getConstantState().newDrawable());
            mTextView.setText(content);
            mTextView.setPadding(customSonPaddingLeft, customSonPaddingTop, customSonPaddingRight, customSonPaddingBottom);
            //消除TextView默认的上下内边距
            mTextView.setIncludeFontPadding(false);
            mTextView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    //选择模式
                    if (customSelectMode != 102) {
                        for (int j = 0; j < mSonTextViews.size(); j++) {
                            mSonTextViews.get(j).setSelected(false);
                        }
                        v.setSelected(true);
                    } else {
                        v.setSelected(true);
                    }
                    if (mOnItemClickListener != null) {
                        mOnItemClickListener.onItemClick(v, i, content);
                    }
                }
            });
            return mTextView;
        }
        return null;
    }

    /**
     * 初始化自定义属性
     *
     * @param context
     * @param attrs
     */
    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomLableView);
        customSonBackground = mTypedArray.getDrawable(R.styleable.CustomLableView_customSonBackground);
        customInterval = (int) mTypedArray.getDimension(R.styleable.CustomLableView_customInterval, customInterval);
        customSonPaddingLeft = (int) mTypedArray.getDimension(R.styleable.CustomLableView_customSonPaddingLeft, customSonPaddingLeft);
        customSonPaddingRight = (int) mTypedArray.getDimension(R.styleable.CustomLableView_customSonPaddingRight, customSonPaddingRight);
        customSonPaddingTop = (int) mTypedArray.getDimension(R.styleable.CustomLableView_customSonPaddingTop, customSonPaddingTop);
        customSonPaddingBottom = (int) mTypedArray.getDimension(R.styleable.CustomLableView_customSonPaddingBottom, customSonPaddingBottom);
        customSonTextSize = (int) mTypedArray.getDimension(R.styleable.CustomLableView_customSonTextSize, 0);
        customSonTextColor = mTypedArray.getColorStateList(R.styleable.CustomLableView_customSonTextColor);
        customSelectMode = mTypedArray.getInt(R.styleable.CustomLableView_customSelectMode, 101);
        if (customSonTextSize == 0) {
            customSonTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics());
        }
        mTypedArray.recycle();
    }


    public interface OnItemClickListener {
        void onItemClick(View view, int position, String sonContent);
    }
}

  • 自定义属性
    <declare-styleable name="CustomLableView">
        <attr name="customSonBackground" format="reference" />
        <attr name="customInterval" format="dimension" />
        <attr name="customSonPaddingLeft" format="dimension" />
        <attr name="customSonPaddingRight" format="dimension" />
        <attr name="customSonPaddingTop" format="dimension" />
        <attr name="customSonPaddingBottom" format="dimension" />
        <attr name="customSonTextSize" format="dimension" />
        <attr name="customSonTextColor" format="color" />
        <attr name="customSelectMode">
            <enum name="alone" value="101" />
            <enum name="multi" value="102" />
        </attr>
    </declare-styleable>
Android流式标签可动态添加FlowLayout Android流式标签 可动态添加FlowLayout 可点击 立即下载

相关推荐

android商品属性选择标签控件,可实现自动换行

android 商品属性 选择标签控件,可实现 自动换行, 默认选中第一个,可多选; 点击获取标签内容等

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

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

Angelica 7781

Android自定义标签选择器TagView

Android自定义标签选择器TagView [注:本内容来自网络,在此分享仅为帮助有需要的网友,如果侵犯了您的权利,麻烦联系我,我会第一时间删除,谢谢您。]

Android标签流控件的实现

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

wk843620202的专栏 4578

Android代码-Android流式标签布局

FlowLayout A FlowLayout for Android, which allows child views flow to next row when there is no enough space. The spacing between child views can be calculated by the FlowLayout so that the views are evenly placed. Gradle compile 'com.nex3z:flow-layout:1.2.4' Usage <!--...--> Attributes Attribute Format Description flFlow boolean true to allow flow. false to restrict all child views in one row. The default is true. flChildSpacing auto/dimension The horizontal spacing betw

一个可以自适应换行的标签列表控件

一个可以自适应换行的标签列表控件,复制即可用,欢迎下载 原博客地址 https://blog.youkuaiyun.com/Sure_Min/article/details/79059737

Android自定义ViewGroup实现标签流效果 (仿天猫)

实现标签效果原理很简单,就是使用addView方法将TextView控件添加到ViewGroup中,然后重写ViewGroup中的onLayout方法来对添加view进行一次摆放。在这里是实现的标签支持自定义background、字体颜色、字体大小、标签的内边距和标签之间的间隔大小。 1.自定义上文提到的属性。在资源文件夹values下创建attr.xml文件一次添加属性,代码如下: at...

qq_34351538的博客 493

Android流式标签(TAG)

Android自定义流式Tag,使用adapter进行数据绑定 github地址:https://github.com/inexistence/FlowTagsLayout

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

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

拂过琴弦的风的专栏 555

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

主要介绍了Android自定义ViewGroup实现FlowLayout标签流容器,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

STM32Cubemx+CanFestival:从零到一的CANopen移植实战

本文详细介绍了如何使用STM32CubeMX和CanFestival实现CANopen协议栈的移植,从开发环境搭建到核心配置步骤,再到调试与问题排查。通过实战经验分享,帮助开发者快速掌握STM32与CANopen的结合应用,提升嵌入式系统开发效率。

weixin_42558093的博客 420

自定义ViewGroup--浮动标签的实现

前面在学习鸿洋大神的一些自定义的View文章,看到了自定义ViewGroup实现浮动标签,初步看了下他的思路以及结合自己的思路完成了自己的浮动标签的自定义ViewGroup。目前实现的可以动态添加标签、可点击。效果图如下: 1、思路 首先在onMeasure方法中测量ViewGroup的宽和高,重点是处理当我们自定义的ViewGroup设置为wrap_content的情况下,如

开心阳 3524

STM32F407用FSMC+DMA驱动LVGL,3.5寸屏流畅度翻倍实战(附CubeMX配置避坑)

本文详细介绍了如何在STM32F407上通过FSMC+DMA驱动LVGL,显著提升3.5寸屏的显示流畅度。从FSMC与DMA的配置原理到LVGL驱动层适配,提供了完整的优化方案和实测数据,帮助开发者解决界面卡顿问题,实现流畅度翻倍的效果。

weixin_30872499的博客 559

Android 自定义ViewGroup 实战篇 -> 实现FlowLayout

转载请标明出处:http://blog.youkuaiyun.com/lmj623565791/article/details/38352503 ,本文出自【张鸿洋的博客】1、概述上一篇已经基本给大家介绍了如何自定义ViewGroup,如果你还不了解,请查看:Android 手把手教您自定ViewGroup ,本篇将使用上篇介绍的方法,给大家带来一个实例:实现FlowLayout,何为FlowLayout,如

Hongyang 14万+

从MIPS汇编到C语言:手把手教你用Mars模拟器理解指令集(附实战代码)

本文详细介绍了如何使用Mars模拟器从MIPS汇编过渡到C语言编程,通过实战代码解析ADD、LW、BEQ等指令集与C语言结构的对应关系。内容涵盖环境搭建、算术运算、内存访问、条件分支及函数调用等核心概念,帮助读者深入理解MIPS指令分类和计算机底层运行机制。

weixin_26821003的博客 357

【GIS效率手册】告别C盘爆满:三步重置ArcMap默认地理数据库路径

本文针对ArcMap用户C盘空间常被占满的问题,提供了三步重置默认地理数据库路径的详细教程。通过修改地图文档属性,将数据存储位置从C盘迁移至其他驱动器,不仅能有效释放系统盘空间,还能提升数据管理效率与安全性,是GIS数据规范化管理的关键一步。

g2h3i4j5的博客 543

Android自定义ViewGroup实现标签流式布局

Android自定义ViewGroup实现标签流式布局

李波的专栏 978

红队内网攻防渗透:内网渗透之内网对抗:实战项目&VPC1打靶&PHP-RCE&三层代理路由&防火墙上线&密码喷射&域控提权

上面已经抓取到了密码,但是我们通过拓扑分析看,要想上线win10只能通过正向shell,因为win7的防火墙是开的,win10反向主动连接肯定入站规则拦截,而win10自己没有开防火墙,所以win7主动正向连接就可以上线。win7开启了防火墙,入站规则肯定是会拦截我们的请求的,所以我们需要反向上线,让win7主动来连接我们。这里扫描发现是存在ms17010漏洞的,但是想上线192.168.2.142的话,我们没有路由是上线不了的,这里有两种办法:一种msf直接生成后门,另一种cs移植后门到msf。

HACKONE的博客 909

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

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

weixin_29415667的博客 362

LVGL core group焦点分组管理(lv_group.c)

lv_group_t是用于焦点管理的,将需要获取焦点的对像加入到同一个分组内,再将其通过挂到指定的事件驱动中,就可以控制焦点了。而被加入的对象又可以通过焦点状态事件和事件来控制对象的焦点逻辑了。创建好的分组通过接口挂事件驱动中,这样就可以指定一个自定义的焦点分组了。;) {if(!} }友情提示:无论是接口还是都是单例的,即被设置后前一个被设置的值就失效了;此方案可以保证焦点的唯一 性,但也一定要记住要在适合的位置设置回来,以防丢失焦点。

Bigan(安)的博客 3907

OpenCASCADE:Direct3D CSharp 示例

OpenCASCADE:Direct3D CSharp 示例OpenCASCADE:Direct3D CSharp 示例 OpenCASCADE:Direct3D CSharp 示例 此示例演示了如何在使用CSharp和Windows Presentation Foundation (WPF)编写的.Net应用程序中使用 OCCT 和 DirectX 库。 .Net、OCCT (C++) 和 DirectX 级别之间的连接由用 C++/CLI 编写的代理库OCCProxy和D3DProxy提供。代理库OCC

希望我的博客,能帮上你解决学习中工作中所遇到的问题 1824

android浮动效果,Android自定义ViewGroup实现标签浮动效果

前面在学习鸿洋大神的一些自定义的View文章,看到了自定义ViewGroup实现浮动标签,初步看了下他的思路以及结合自己的思路完成了自己的浮动标签的自定义ViewGroup。目前实现的可以动态添加标签、可点击。效果图如下:1、思路首先在onMeasure方法中测量ViewGroup的宽和高,重点是处理当我们自定义的ViewGroup设置为wrap_content的情况下,如何去测量其大小的问题。当...

weixin_32047681的博客 331

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

android 自定义标签云,Android自定义控件ViewGroup实现标签云

本文实例为大家分享了Android自定义控件ViewGroup实现标签云的具体代码,供大家参考,具体内容如下实现的功能:基本绘制流程:构造函数获取自定义属性onMeasure()方法,测量子控件的大小onLayout()方法,对子控件进行布局1、自定义属性2、构造函数中获取自定义属性值public TabsViewGroup(Context context, AttributeSet attrs)...

weixin_29955355的博客 285

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Kotlin 第一弹:自定义 ViewGroup 实现流式标签控件

古人学问无遗力, 少壮工夫老始成。纸上得来终觉浅, 绝知此事要躬行。 – 陆游 《冬夜读书示子聿》 上周 Google I/O 大会的召开,宣布了 Kotlin 语言正式成为了官方开发语言。一时间 Android 开发者的圈子炸开了锅,各种关于 Kotlin 的资料介绍也如雨后春笋不断的冒出。大家都对这比较关心,我觉得最大的原因是,当初宣布 Android Studio 成为官方 IDE 后,很多开

frank 的专栏 1万+

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Android使用ViewGroup实现流式标签布局(靠左,靠右,居中)

在此之前都所使用的的布局都是第三方的,看了一些资料,自己稍微修改了下,今天借此机会来实现一个简单的逆向流式布局。作为一个little tinny note吧,哈哈哈! 参考文章:http://www.jianshu.com/p/e36ee95c18dc 大致的实现思路: 1.继承ViewGroup重写onMeasure()和onLayout()方法; 2.在onMeasure()方

qq_27343905的博客 5237
上一篇: 了解RecyclerView的分割线
下一篇: Android Studio快捷键以及常用配置
Android师哥
博客等级 码龄10年 3粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值