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

本文实例为大家分享了Android自定义ViewGroup实现标签流效果的具体代码,供大家参考,具体内容如下

自定义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>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

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

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

  • Android自定义View实现标签流效果

    本文实例为大家分享了Android自定义View实现标签流效果的具体代码,供大家参考,具体内容如下 一.概述 Android自定义View实现标签流效果,一行放不下时会自动换行,用户可以自己定义单个标签的样式,可以选中和取消,可以监听单个标签的点击事件,功能还算强大,可以满足大部分开发需求,值得推荐,效果图如下: 二.实现代码 1.自定义View 定义属性文件 <declare-styleable name="FlowTagView">         <attr n

  • Android实现单行标签流式布局

    近期产品提了有关流式布局的新需求,要求显示字数不定的标签,如果一行显示不完,就只显示一行的内容,而且还在一两个页面采取了多种样式,无语了 自己归类了一下,需求有以下几个区别 1:可选择添加标签与否 2:是否有具体数量限制(比如最多显示3个) 3:是否要靠右对齐 有需求,先找一波现有的工具,看是不是可以直接用 参考Android流式布局实现热门标签效果 FlowLayout原样式: 这个和我们的需求已经比较符合了,但是他并不能控制只显示单行内容 如果要实现这样的布局,官方也提供了Flexbox和F

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

    本文实例为大家分享了Android自定义ViewGroup实现标签流效果的具体代码,供大家参考,具体内容如下 自定义View,一是为了满足设计需求,二是开发者进阶的标志之一.随心所欲就是我等奋斗的目标!!! 效果 实现逻辑 明确需求 1.标签流效果;2.可以动态添加标签;3.标签需要有点击效果以及回调; 整理思路 既然要装载标签,就需要自定义ViewGroup ,而自定义ViewGroup 比较复杂的就是onLayout()中对子View的排版.既然是标签,在一行中一定要显示完整,在排版的时候注

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

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

  • Android自定义ViewGroup实现流式布局

    本文实例为大家分享了Android自定义ViewGroup实现流式布局的具体代码,供大家参考,具体内容如下 1.概述 本篇给大家带来一个实例,FlowLayout,什么是FlowLayout,我们常在App 的搜索界面看到热门搜索词,就是FlowLayout,我们要实现的就是图中的效果,就是根据容器的宽,往容器里面添加元素,如果剩余的控件不足时候,自行添加到下一行,FlowLayout也叫流式布局,在开发中还是挺常用的. 2.对所有的子View进行测量 onMeasure方法的调用次数是不确定的

  • Android自定义ViewGroup实现绚丽的仿支付宝咻一咻雷达脉冲效果

    去年春节的时候支付宝推行的集福娃活动着实火的不能再火了,更给力的是春晚又可以全民参与咻一咻集福娃活动,集齐五福就可平分亿元大红包,只可惜没有敬业福--那时候在家没事写了个咻一咻插件,只要到了咻一咻的时间点插件就可以自动的点击咻一咻来咻红包,当时只是纯粹练习这部分技术代码没有公开,后续计划写篇关于插件这方面的文章,扯远了(*^__^*) --我们知道在支付宝的咻一咻页面有个雷达扩散的动画效果,当时感觉动画效果非常棒,于是私下尝试着实现了类似的效果,后来在github发现有大神也写有类似效果,于是读

  • Android自定义ViewGroup实现弹性滑动效果

    自定义View实现一个弹性滑动的效果,供大家参考,具体内容如下 实现原理 onMeasure()中测量所有子View @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 测量所有子View int count = getChildCount(); for (int i = 0; i < count; i++) { View childView = getChildAt(i); m

  • Android自定义ViewGroup多行多列效果

    本文实例为大家分享了Android自定义ViewGroup多行多列的具体代码,供大家参考,具体内容如下 先看下效果图 每行两个子孩子 每行一个子孩子 实现思路 自定义viewGroup,实现测量和布局,使控件适应业务场景. 测量 根据父控件的宽度,平均分给每个子孩子固定的宽度.高度就是行数乘以一个子孩子的高度,再加上空隙的高度. 根据子孩子个数计算行数 val rows = if (childCount % perLineChild == 0) { childCount / perLineChi

  • Android自定义ViewGroup之FlowLayout(三)

    本篇继续来讲自定义ViewGroup,给大家带来一个实例:FlowLayout.何为FlowLayout,就是控件根据ViewGroup的宽,自动的往右添加,如果当前行剩余空间不足,则自动添加到下一行,所以也叫流式布局.Android并没有提供流式布局,但是某些场合中,流式布局还是非常适合使用的,比如关键字标签,搜索热词列表等,比如下图: 定义FlowLayout LayoutParams,onLayout的写法都和上一篇讲WaterfallLayout一模一样,在此不再赘述了,没看过的可以参照

  • Android自定义ViewGroup实现九宫格布局

    目录 前言 一.九宫格的测量 二.九宫格的布局 三.单图片与四宫格的单独处理 四.自定义布局的抽取 4.1 先布局再隐藏的思路 4.2 数据适配器的思路 前言 在之前的文章我们复习了 ViewGroup 的测量与布局,那么我们这一篇效果就可以在之前的基础上实现一个灵活的九宫格布局. 那么一个九宫格的 ViewGroup 如何定义,我们分解为如下的几个步骤来实现: 先计算与测量九宫格内部的子View的宽度与高度. 再计算整体九宫格的宽度和高度. 进行子View九宫格的布局. 对单独的图片和四宫格的

随机推荐