(转载)Android自定义标签列表控件LabelsView解析

Android自定义标签列表控件LabelsView解析

作者 donkingliang 关注

2017.03.15 20:59* 字数 759 阅读 406评论 0喜欢 3

无论是在移动端的App,还是在前端的网页,我们经常会看到下面这种标签的列表效果:

标签列表

标签从左到右摆放,一行显示不下时自动换行。这样的效果用Android源生的控件很不好实现,所以往往需要我们自己去自定义控件。我在开发中就遇到过几次要实现这样的标签列表效果,所以就自己写了个控件,放到我的GitHub,方便以后使用。有兴趣的同学也欢迎访问我的GitHub、查看源码实现和使用该控件。下面我将为大家介绍该控件的具体实现和使用。
要实现这样一个标签列表其实并不难,列表中的item可以直接用TextView来实现,我们只需要关心列表控件的大小和标签的摆放就可以了。也就是说我们需要做的只要两件事:测量布局(onMeasure)和摆放标签(onLayout)。这是自定义ViewGroup的基本步骤,相信对自定义View有所了解的同学都不会陌生。下面我们就来看看具体的代码实现。
控件的测量:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int count = getChildCount();
        int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();

        int contentHeight = 0; //记录内容的高度
        int lineWidth = 0; //记录行的宽度
        int maxLineWidth = 0; //记录最宽的行宽
        int maxItemHeight = 0; //记录一行中item高度最大的高度
        boolean begin = true; //是否是行的开头

        //循环测量item并计算控件的内容宽高
        for (int i = 0; i < count; i++) {
            View view = getChildAt(i);
            measureChild(view, widthMeasureSpec, heightMeasureSpec);

            //当前行显示不下item时换行。
            if (maxWidth < lineWidth + view.getMeasuredWidth()) {
                contentHeight += mLineMargin;
                contentHeight += maxItemHeight;
                maxItemHeight = 0;
                maxLineWidth = Math.max(maxLineWidth, lineWidth);
                lineWidth = 0;
                begin = true;
            }
            maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight());
            if(!begin) {
                lineWidth += mWordMargin;
            }else {
                begin = false;
            }
            lineWidth += view.getMeasuredWidth();
        }

        contentHeight += maxItemHeight;
        maxLineWidth = Math.max(maxLineWidth, lineWidth);

        //测量控件的最终宽高
        setMeasuredDimension(measureWidth(widthMeasureSpec,maxLineWidth),
                measureHeight(heightMeasureSpec, contentHeight));

    }

    //测量控件的宽
    private int measureWidth(int measureSpec, int contentWidth) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = contentWidth + getPaddingLeft() + getPaddingRight();
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        //这一句是为了支持minWidth属性。
        result = Math.max(result, getSuggestedMinimumWidth());
        return result;
    }

    //测量控件的高
    private int measureHeight(int measureSpec, int contentHeight) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = contentHeight + getPaddingTop() + getPaddingBottom();
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        //这一句是为了支持minHeight属性。
        result = Math.max(result, getSuggestedMinimumHeight());
        return result;
    }

标签的摆放:

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

        int x = getPaddingLeft();
        int y = getPaddingTop();

        int contentWidth = right - left;
        int maxItemHeight = 0;

        int count = getChildCount();
        //循环摆放item
        for (int i = 0; i < count; i++) {
            View view = getChildAt(i);

            //当前行显示不下item时换行。
            if (contentWidth < x + view.getMeasuredWidth() + getPaddingRight()) {
                x = getPaddingLeft();
                y += mLineMargin;
                y += maxItemHeight;
                maxItemHeight = 0;
            }
            view.layout(x, y, x + view.getMeasuredWidth(), y + view.getMeasuredHeight());
            x += view.getMeasuredWidth();
            x += mWordMargin;
            maxItemHeight = Math.max(maxItemHeight, view.getMeasuredHeight());
        }
    }

onMeasure和onLayout的实现代码基本是一样的,不同的只是一个是测量宽高,一个是摆放位置而已。实现起来非常的简单。
以上是LabelsView的核心代码,LabelsView除了实现了item的测量和摆放以外,还提供了一系列的方法让使用者可以方便设置标签的样式(包括标签被选中的样式)和标签点击、选中的监听等。下面LabelsView的使用介绍。

1、引入依赖
在Project的build.gradle在添加以下代码

allprojects {
    repositories {
        ...
        maven { url ‘https://jitpack.io‘ }
    }
}

在Module的build.gradle在添加以下代码

dependencies {
    compile ‘com.github.donkingliang:LabelsView:1.2.0‘
}

2、编写布局:

   <com.donkingliang.labels.LabelsView
       xmlns:app="http://schemas.android.com/apk/res-auto"
       android:id="@+id/labels"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       app:labelBackground="@drawable/label_bg"     //标签的背景
       app:labelTextColor="@drawable/label_text_color" //标签的字体颜色 可以是一个颜色值
       app:labelTextSize="14sp"      //标签的字体大小
       app:labelTextPaddingBottom="5dp"   //标签的上下左右边距
       app:labelTextPaddingLeft="10dp"
       app:labelTextPaddingRight="10dp"
       app:labelTextPaddingTop="5dp"
       app:lineMargin="10dp"   //行与行的距离
       app:wordMargin="10dp"   //标签与标签的距离
       app:selectType="SINGLE"   //标签的选择类型 有单选、多选、不可选三种类型
       app:maxSelect="5" />  //标签的最大选择数量,只有多选的时候才有用,0为不限数量

这里有两个地方需要说明一下:
1)标签的正常样式和选中样式是通过drawable来实现的。比如下面两个drawable。

<!-- 标签的背景 label_bg -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 标签选中时的背景 -->
    <item android:state_selected="true">
        <shape>
            <stroke android:width="2dp" android:color="#fb435b" />
            <corners android:radius="8dp" />
            <solid android:color="@android:color/white" />
        </shape>
    </item>
    <!-- 标签的正常背景 -->
    <item>
        <shape>
            <stroke android:width="2dp" android:color="#656565" />
            <corners android:radius="8dp" />
            <solid android:color="@android:color/white" />
        </shape>
    </item>
</selector>
<!-- 标签的文字颜色 label_text_color -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 标签选中时的文字颜色 -->
    <item android:color="#fb435b" android:state_selected="true" />
    <!-- 标签的正常文字颜色 -->
    <item android:color="#2d2b2b" />
</selector>

TextView的textColor属性除了可以设置一个颜色值以外,也可以通过资源来设置的,这一点很多同学都不知道。
2)标签的选择类型有三种:
NONE :标签不可选中,也不响应选中事件监听,这是默认值。
SINGLE:单选。
MULTI:多选,可以通过设置maxSelect限定选择的最大数量,0为不限数量。maxSelect只有在多选的时候才有效。

3、设置标签:

labelsView = (LabelsView) findViewById(labels);
ArrayList<String> label = new ArrayList<>();
label.add("Android");
label.add("IOS");
label.add("前端");
label.add("后台");
label.add("微信开发");
label.add("游戏开发");
label.add("Java");
label.add("JavaScript");
label.add("C++");
label.add("PHP");
label.add("Python");
label.add("Swift");
labelsView.setLabels(label); //直接设置一个字符串数组就可以了。

4、设置事件监听:(如果需要的话)

//标签的点击监听
labelsView.setOnLabelClickListener(new LabelsView.OnLabelClickListener() {
    @Override
    public void onLabelClick(View label, String labelText, int position) {
         //label是被点击的标签,labelText是标签的文字,position是标签的位置。
    }
});
//标签的选中监听
labelsView.setOnLabelSelectChangeListener(new LabelsView.OnLabelSelectChangeListener() {
    @Override
    public void onLabelSelectChange(View label, String labelText, boolean isSelect, int position) {
        //label是被点击的标签,labelText是标签的文字,isSelect是是否选中,position是标签的位置。
    }
});

5、常用方法

//设置选中标签。
//positions是个可变类型,表示被选中的标签的位置。
//比喻labelsView.setSelects(1,2,5);选中第1,3,5个标签。如果是单选的话,只有第一个参数有效。
public void setSelects(int... positions);

//获取选中的标签。返回的是一个Integer的数组,表示被选中的标签的下标。如果没有选中,数组的size等于0。
public ArrayList<Integer> getSelectLabels();

//取消所有选中的标签。
public void clearAllSelect();

//设置标签的选择类型,有NONE、SINGLE和MULTI三种类型。
public void setSelectType(SelectType selectType);

//设置最大的选择数量,只有selectType等于MULTI是有效。
public void setMaxSelect(int maxSelect);

//设置标签背景
public void setLabelBackgroundResource(int resId);

//设置标签的文字颜色
public void setLabelTextColor(int color);
public void setLabelTextColor(ColorStateList color);

//设置标签的文字大小(单位是px)
public void setLabelTextSize(float size);

//设置标签内边距
public void setLabelTextPadding(int left, int top, int right, int bottom);

//设置行间隔
public void setLineMargin(int margin);

//设置标签的间隔
public void setWordMargin(int margin);

所以的set方法都有对应的get方法,这里就不说了。

效果图:

效果图.gif

最后给出该控件在GitHub中的地址,欢迎大家访问和使用。
https://github.com/donkingliang/LabelsView

时间: 2024-10-14 00:52:26

(转载)Android自定义标签列表控件LabelsView解析的相关文章

Android自定义标签列表控件LabelsView解析

版权声明:本文为博主原创文章,未经博主允许不得转载. 无论是在移动端的App,还是在前端的网页,我们经常会看到下面这种标签的列表效果:   标签从左到右摆放,一行显示不下时自动换行.这样的效果用Android源生的控件很不好实现,所以往往需要我们自己去自定义控件.我在开发中就遇到过几次要实现这样的标签列表效果,所以就自己写了个控件,放到我的GitHub,方便以后使用.有兴趣的同学也欢迎访问我的GitHub.查看源码实现和使用该控件.下面我将为大家介绍该控件的具体实现和使用. 要实现这样一个标签列

android 自定义圆形imageview控件

首先,定义定义圆形Imageview类: import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDu

Android 自定义SwitchButton开关控件

SwitchButton开关控件早已经非常流行.有各种各样的样式,SwitchButton开关控件一般用于app软件设置那里,控制缓存.声音.提示.下载等等.是具有很好的UI体验以及用户的习惯性.那么再下面介绍一个SwitchButton开关控件.并附上源码. 源码下载:点击 一.看实现的效果图 二.自定义SwitchButton 这是一个继承CheckBox的SwitchButton类.来实现做这些动画效果的,首先准备好这些图片,然后canvas绘制控件 的边框.背景.以及按钮.绘制时候加上相

如何打造Android自定义的下拉列表框控件

一.概述 Android中的有个原生的下拉列表控件Spinner,但是这个控件有时候不符合我们自己的要求, 比如有时候我们需要类似windows 或者web网页中常见的那种下拉列表控件,类似下图这样的: 这个时候只有自己动手写一个了.其实实现起来不算很难, 本文实现的方案是采用TextView +ImageView+PopupWindow的组合方案. 先来看看我们的自己写的控件效果图吧:(源码在文章下面最后给出哈!) 二.自定义下拉列表框控件的实现 1. 自定义控件用到的布局文件和资源: 结果框

Android——自定义镂空遮盖控件

刚学完ViewDragHelper和PorterDuffXferMode的我,突然想做一个这样效果的自定义控件:点击ListView的列表项,通过ViewDragHelper用动画方式上下各弹出一个控件遮盖住ListView,这两个控件在遮盖listView的过程中有一部分是镂空的.先上效果图: 首先是进行页面的布局,让自定义控件PlayLayout继承自Franlayout,在最底层放的就是listView所在的子FramLayout(Id:midContent),然后依次在上面加上下两个看起

Android自定义星星评分控件,高效

下面为控件的实现历程: 此控件高效,直接使用ondraw绘制,先亮照: 由于Android自身的星星评分控件样式可以改,但是他的大小不好调整的缺点,只能用small normal这样的style调整,自定义不强,因此击发了我自定义星星控件的欲望. 星星评分控件的设计,大体规划为: 需要两张图片,一颗亮星星,一颗空星星:(当然图片不一定是星星,其他图片也可以,现在实验就用星星就好了)星星数量,间距可以自定义,星星的最小步进为0.1,在用户使用的时候与Android自带的方法一样. 星星控件大体分为

android自定义刷新类控件

android虽然定义了种类非常丰富的控件,但是有的时候这些自定义的控件还是不能满足我的要求,为了能够适配更多的需求,我们需要在原有的基础上进行自定义控件. 今天我向大家介绍的就是android中最常见的刷新类控件.因为我们最近正在参加一个项目,在项目组长的带领下,我学到了很多的东西,这对我的android技术的提升非常大,定义一个自定义控件可能不是很难,但是如何让这个自定义控件更加有效.更加快速地运行. 首先我们需要建立一个自定义控件类: package com.example.ui.widg

[转]android 自定义圆形imageview控件

android布局 首先,定义定义圆形Imageview类: [java] view plaincopy import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint;

Android自定义“图片+文字”控件四种实现方法之 二--------个人最推荐的一种

http://blog.csdn.net/yanzi1225627/article/details/8633872 第二种方法也要新建一个图片+文字的xml布局文件,然后写一个类继承自LinearLayout.在主程序里实例化并设置相应参数.这种方式也是我最推荐的一种. 第一部分:myimgbtn_layout.xml [html] view plaincopyprint? <?xml version="1.0" encoding="utf-8"?> &