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标签流容器,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

SPSS&Mplus—共同方法偏差(Common Method Bias)控制与检验

共同方法偏差(Common Method Bias)的控制方法:程序控制(Procedural)和统计控制(Statistical)。

m0_54401011的博客 1万+

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

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

开心阳 3524

CISSP/CCSP证书到手后别松懈!保姆级维持攻略:CPE积分怎么攒?年费怎么交?

本文为CISSP/CCSP持证者提供详细的维持认证攻略,重点介绍如何高效积累CPE积分和避免年费缴纳中的常见问题。通过免费与付费资源结合的策略,帮助持证者轻松完成年度认证要求,同时利用认证创造职业价值。

weixin_30859423的博客 416

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

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

Hongyang 14万+

UE5.4像素流送实战:GPU驱动、WebRTC与系统级调优全解析

像素流送是将实时3D渲染画面通过WebRTC编码推流至浏览器的关键技术,其本质是GPU硬件编码(如NVENC)与低延迟网络传输(UDP+BBR)的深度协同。理解其原理需跨越图形API(Vulkan/DX12)、实时音视频(H.264/H.265)、WebRTC信令与NAT穿透四大技术层。技术价值在于突破终端算力限制,支撑远程工业仿真、云VR看房等高交互场景;但落地难点集中于基础设施层——如NVIDIA驱动与UE5.4.3的CUDA Toolkit 12.2兼容性、Linux内核TCP/UDP参数调优、GPU

weixin_32943857的博客 615

别再写前端了!用Gradio 3.x + HuggingFace Spaces,5分钟把你的Python算法变成网页应用

本文介绍如何利用Gradio 3.x和HuggingFace Spaces快速将Python算法转化为网页应用,无需前端开发经验。通过5行代码和简单的Git命令,即可实现专业级Web界面部署,大幅提升算法展示效率。特别适合算法工程师和数据科学家快速分享模型成果。

weixin_33443333的博客 332

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

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

李波的专栏 978

STM32进入HardFault_Handler时,如何找到问题所在?

当STM32进入HardFault_Handler时,定位问题发生的点

m0_60831979的博客 2150

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

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

weixin_29415667的博客 362

本地部署静态网站托管平台 Netlify 并实现外部访问(Windows 版本)

Netlify 是一款基于云计算的网站部署和托管平台,专为开发者提供便捷的静态网站和 Jamstack 应用部署服务。它的能力在于将网站构建、部署、托管以及持续集成等流程高度集成,简化了开发和运维的复杂程度。用户无需关心服务器的配置、维护等工作。本文将详细介绍如何在本地安装 Netlify 以及结合路由侠内网穿透实现外网访问 Netlify。

lyxnwct的博客 523

Windows上MSYS2的安装和使用

MSYS2这个工具我是越用越喜欢,很多东西放在Linux上如鱼得水但是放在Windows上就没那么好用,比如ffmpeg,opencv等直接引用就行了,这个工具真的很省事。接下来带你们看看它的妙用。1、好好研究确实妙用无穷。

jiexijihe945的博客 4141

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

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

weixin_32047681的博客 331

计算机组成原理(第三版)唐朔飞-第十章控制单元的设计-课后习题

计算机组成原理(第三版)唐朔飞-第十章控制单元的设计-课后习题

weixin_45735391的博客 3万+

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

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

weixin_29955355的博客 285

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

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

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

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

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

frank 的专栏 1万+

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

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

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

qq_27343905的博客 5237

精雕软件(JDpaint) 5.5 绿色版

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

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

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值