前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >自定义侧边快速索引栏

自定义侧边快速索引栏

作者头像
NanBox
发布于 2019-07-09 11:12:25
发布于 2019-07-09 11:12:25
64700
代码可运行
举报
文章被收录于专栏:NanBoxNanBox
运行总次数:0
代码可运行

介绍

现在有通讯录的项目基本都会用到侧边快速索引栏,网上也有不少第三方开源控件可以使用。但我讲的这个还是稍稍有点不一样的。

和一般索引栏不太一样的地方:

  • 只显示有出现的首字母
  • 出现的索引字母居中显示
  • 代码相对较简单

好吧,编不下去了,其实也没什么不一样的,可以看一下效果图。

思路

大致的实现过程如下:

  1. 获取要显示的字母
  2. 获取需要用到的宽高数值
  3. 绘制各个字母
  4. 处理Touch事件

还是挺简单的,稍稍有点难度的地方应该在计算。

代码

自定义控件的实现方法有很多,这里我是用继承 View 来实现的,下面看看具体怎么实现。

获取要显示的字母

通过set方法获取到要显示的字母集合后,重绘控件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void setIndexs(String[] indexs) {
    this.indexs = indexs;
    invalidate();
}
复制代码

获取需要用到的宽高数值

绘制侧边栏的时候,我们需要用到以下几个参数来计算字母所在的坐标:

  • 控件的总宽度 mWidth
  • 每个字母可以分配到的高度 mCellHeight
  • 第一个字母和控件顶部的距离 mMarginTop (用于居中显示)
  • 每个字母本身的宽高

当控件的的宽高发生变化时,会执行 onSizeChanged 这个方法,在首次初始化时也会调用,所以前三个参数我们可以在这里获取到。 这里考虑最多会出现 27 个字符,如果首字母是数字或者特殊字符,我们用「#」表示。 mMarginTop 可能要理解一下,第一个字母距离控件顶部的距离,是控件总高度减去所有字母的总高度的一半,应该不是很难理解:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = getMeasuredWidth();
    mHeight = getMeasuredHeight();
    mCellHeight = (mHeight * 1f / 27);    //26个字母加上“#”
    mMarginTop = (mHeight - mCellHeight * indexs.length) / 2;
}
复制代码

但要说明一下的是,加载控件是需要时间的(处理过 SwipeRefreshLayout 一进去就要转圈的应该理解)。也就是说 onSizeChanged 可能在 setIndexs 之前执行,也可能在它之后执行。这就可能会导致获取不到 mMarginTop,所以我们在 setIndexs 里也加上 mMarginTop = (mHeight - mCellHeight * indexs.length) / 2 这句代码,保证可以拿到这个参数。

至于字母的宽高,不同字母的宽高其实都不一样的,我们可以通过以下方法获取到:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public float getTextWidth(String text) {
    Rect bounds = new Rect();
    mPaint.getTextBounds(text, 0, text.length(), bounds);
    return bounds.width();
}

public float getTextHeight(String text) {
    Rect bounds = new Rect();
    mPaint.getTextBounds(text, 0, text.length(), bounds);
    return bounds.height();
}
复制代码

绘制各个字母

通过上面获取到的参数,我们可以计算出每个字母的坐标。 简单说明一下,使用 drawText 绘制时使用x坐标是字母最左边的坐标,而 y 坐标是指字母 baseline 的坐标。我们可以简单理解为是字母左下角的坐标。

x 坐标

为了让字母在控件里左右居中显示,x 坐标就是控件总宽度的一半减去字母宽度的一半:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
float x = mWidth / 2 - getTextWidth(letter) / 2;
复制代码

y 坐标

y 坐标的计算可能要理解一下。首先和宽度类似,让字母在它占有的空间里面上下居中,也就是字母占有高度的一半加上字母高度的一半。然后要加上它前面所有字母的高度,及首字母距离顶部的距离:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
float y = mCellHeight / 2 + getTextHeight(letter) / 2 + mCellHeight * i + mMarginTop;
复制代码

所以 onDraw 的代码可以这样写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 @Override
protected void onDraw(Canvas canvas) {
    if (indexs.length <= 0) {
        return;
    }
    for (int i = 0; i < indexs.length; i++) {
        String letter = indexs[i];
        float x = mWidth / 2 - getTextWidth(letter) / 2;
        float y = mCellHeight / 2 + getTextHeight(letter) / 2 + mCellHeight * i + mMarginTop;
        canvas.drawText(letter, x, y, mPaint);
    }
}
复制代码

处理 Touch 事件

这个控件的点击和滑动事件做了相同的操作,通过计算当前按下字母的下标来获取到该字母,显示吐司,并且通过回调函数让列表定位。当手指抬起时,把吐司隐藏就好了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:    // 按下
        case MotionEvent.ACTION_MOVE:    // 滑动
            // 按下字母的下标
            int letterIndex = (int) ((event.getY() - mMarginTop) / mCellHeight);
            // 判断是否越界
            if (letterIndex >= 0 && letterIndex < indexs.length) {
                // 显示按下的字母
                if (textView != null) {
                    textView.setVisibility(View.VISIBLE);
                    textView.setText(indexs[letterIndex]);
                }
                //通过回调方法通知列表定位
                if (mOnIndexChangedListener != null) {
                mOnIndexChangedListener.onIndexChanged(indexs[letterIndex]);
                }
            }
            break;
        case MotionEvent.ACTION_UP:        // 提起
            if (textView != null) {
                textView.setVisibility(View.GONE);
            }
            break;
    }
    return true;
}
复制代码

到这里,自定义控件就算完成啦。接下来看看它是如何使用的。

使用

现在布局里面使用我们的自定义控件,我是这样写的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<com.southernbox.indexbar.widget.IndexBar
    android:id="@+id/indexbar"
    android:layout_width="20dp"
    android:layout_height="match_parent"
    android:layout_alignParentRight="true" />
复制代码

然后就是设置索引字母数组,设置要吐司的 TextView,设置回调方法。在 RecyclerView 中,我们可以使用 LayoutManager 来定位。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mIndexBar.setIndexs(letters);
mIndexBar.setSelectedIndexTextView(tvToast);
mIndexBar.setOnIndexChangedListener(new IndexBar.OnIndexChangedListener() {
            @Override
            public void onIndexChanged(String index) {
                for (int i = 0; i < mList.size(); i++) {
                    String firstword = mList.get(i).getFirstword();
                    if (index.equals(firstword)) {
                        // 滚动列表到指定的位置
                        layoutManager.scrollToPositionWithOffset(i, 0);
                        return;
                    }
                }
            }
        });
复制代码

最后,我们需要写一个方法用来排序,插入索引item,顺便把要显示的索引字母数组获取到。在 item 的实体类里面,我用 isIndex 这个字段来区分是索引 item,还是普通 item。可以参考一下我的排序方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public Map<String, Object> convertSortList(List<Entity> list) {
    HashMap<String, List<Entity>> map = new HashMap<>();
    for (Entity item : list) {
        String firstWord;
        if (TextUtils.isEmpty(item.getFirstWord())) {
            firstWord = "#";
        } else {
            firstWord = item.getFirstWord().toUpperCase();
        }
        if (map.containsKey(firstWord)) {
            map.get(firstWord).add(item);
        } else {
            List<Entity> mList = new ArrayList<>();
            mList.add(item);
            map.put(firstWord, mList);
        }
    }

    Object[] keys = map.keySet().toArray();
    Arrays.sort(keys);
    List<Entity> sortList = new ArrayList<>();

    for (Object key : keys) {
        Entity t = getIndexItem(key.toString());
        sortList.add(t);
        sortList.addAll(map.get(key));
    }

    HashMap<String, Object> resultMap = new HashMap();
    resultMap.put("sortList", sortList);
    resultMap.put("keys", keys);
    return resultMap;
}
复制代码

还有一点要说明一下的是,一般情况下侧边索引栏的「#」是放在最后一个的,但是用 Arrays.sort 排序的话会把它放在第一个,要放最后一个的话还要再做一个转换,这里就不再赘述了。

结语

这个控件还是比较简单的,这也是一个比较适合用来入门的控件,可能要理解一下的地方在计算方面。 个人觉得,如果一个控件不是太复杂的话,最好是自己实现,或者至少要看明白源码。这样一方面可以修改控件适合自己的项目使用,一方面出现了 bug 也可以快速定位修改。 妥妥的。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017年12月13日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
用户调研怎么做?
第一、产品调研 1.识别和理解目标用户是开始产品设计的第一步,同样重要的是分析市场上类似的产品,分析类似产品针对的用户群,甄别其是否是竞争对手,这些工作对于设计将非常有借鉴意义。理解其他产品的过程有利于比较和理解自己产品目标用户的需求。
葆宁
2020/10/26
7420
【用户体验要素】范围层解读
用户体验要素中的战略层说的是用户需求和产品目标,那么将这两点转变成范围层则应该解释为产品应该提供给用户什么样的内容和功能
用户2025931
2019/03/19
5880
对用户体验最有用的诠释,重读《用户体验要素》
最近重读了《用户体验要素——以用户体验为中心的设计》。此书不愧是AJAX之父Jesse James Garrett的经典之作。整个知识结构简洁优雅,让我又有了新的感悟。难怪有知名出版媒体人称“这本书,始终是对于用户体验最有用的诠释”。本文将书中要点分享给大家。
九零后在互联网
2023/02/06
9020
体验设计五要素之四|产品
辛向阳教授提出:交互设计的本质是对行为的设计。交互设计的五要素分别是:用户、场景、目的、媒介、行为。详情请查看文章《交互设计的本质》。
晓吾
2022/03/30
5050
体验设计五要素之四|产品
用户体验要素:以用户为中心的产品设计
这本书是产品经理的入门书。全书分为了4部分共8章,第1部分介绍什么是用户体验,与产品设计有何不同。第2部分以网站为例,将产品设计划分为5层,然后概要描述了每个层面上包含的用户体验要素。第3部分,从底层到顶层,详细讲解了每一层。最后一部分则做了总结。
张子阳
2018/09/30
1.6K0
用户体验要素:以用户为中心的产品设计
《产品体验要素》读书笔记
无论是功能型产品还是信息型产品,战略层关注的内容都是一样的——来自企业外部的用户需求。我们必须了解这些观众想从我们这儿得到什么,还要知道他们想达到的这些目标将怎样满足他们所期待的其他目标。 雨用户需求相对应的,是我们对APP的期望目标。这些产品目标可以是商业目的,或是其它类型的目标。
nimomeng
2018/09/13
7030
《产品体验要素》读书笔记
发现产品机会点?试试用户分层
作者:viviheyfeng,腾讯无线安全产品部设计组 引言 随着产品发展和用户结构变化,我们的用户不再是一个简单的整体。对于同一功能的不同用户,甚至是同一用户的不同阶段,他们都可能会有不同的痛点和需求。此时如果继续用“一刀切”的普适策略,没有定位到具体用户具体问题,会导致付出资源成本的产品策略达不到预期效果。此时就需要我们进行用户分层。 什么是用户分层? 用户分层,就是区别对待不同的用户进行精细化设计。具体来说,是基于不同用户的行为特征划分为不同的用户群,设计不同的策略来满足其差异化需求,以充
腾讯大讲堂
2020/04/30
1.3K0
你为自己的产品做好战略规划了吗?(市场用户篇)
在我初入职场的第一份工作时,我的一位领导——当时公司的HRVP曾跟我分享的一个职业化经验,一直让我记忆犹新,获益匪浅:他告诉我,能不能做一名好的管理者,问问题的能力是其中一项很重要的能力。做事情、完成任务,能够正确的问出问题、问正确的问题是关键的那个“1”。做战略规划,更是这样。
用户6324559
2023/04/20
2720
你为自己的产品做好战略规划了吗?(市场用户篇)
从0到1,浅谈需求的模型转化
作者:张一弛,华中师大硕士毕业。曾就职于阿里巴巴移动事业群,负责UC浏览器海外版产品工作。2014年加入腾讯,先后在QQ群、QQ HD、PC QQ等产品线从事产品策划工作。现负责新产品TIM核心应用的产品相关工作。 需求从思维到概念的转化 产品设计流程中,在完成需求与市场分析之后,产品经理需要拆解需求场景抽离核心路径,梳理出大大小小的各类功能点,划分功能优先级最终得到版本需求列表,随着项目的行进,在设计师和工程师的协助下,以超出用户预期为目标实现产品满足用户需求。将产品由抽象的思维模型转换为逐步具象化的
腾讯大讲堂
2018/03/01
9930
从0到1,浅谈需求的模型转化
【用户体验要素】框架层
界面设计是我们熟知的关于“按钮、输入框和其他界面控件”的领域;导航设计是专用于呈现信息的一种界面形式;信息设计包含功能和信息两个方面,用于呈现有效的信息沟通
用户2025931
2019/03/19
9600
用户下沉还是走向高端?如何用竞争战略玩转消费升级?
农历新年后的一个月时间里,拼多多股价已经悄然上涨了约25%,3月5日当天一度涨超6%,目前股价已经创出IPO以来的新高。
曾响铃
2019/03/18
4410
用户下沉还是走向高端?如何用竞争战略玩转消费升级?
标签体系下的用户画像建设小指南
用户画像是指根据用户的属性、用户偏好、生活习惯、用户行为等信息而抽象出来的标签化用户模型。通俗说就是给用户打标签,而标签是通过对用户信息分析而来的高度精炼的特征标识。通过打标签可以利用一些高度概括、容易理解的特征来描述用户,可以让人更容易理解用户,并且可以方便计算机处理。
王知无-import_bigdata
2021/07/12
4.5K0
标签体系下的用户画像建设小指南
【用户体验要素】结构层
交互设计关注影响用户执行和完成任务的元素;信息架构关注如何将信息表达给用户的元素。整体结构层最本质的其实是要求如何去理解用户和用户可能发生的行为——思考用户的工作方式、行为和思考方式,这样可以为用户提高产品体验
用户2025931
2019/03/19
6290
【用户体验要素】结构层
全篇干货,10分钟带你读透《参与感》
专注和极致是产品目标,快是行动准则,口碑则是整个互联网思维的核心,因为用户主要是以口碑来选择产品的。
IT阅读排行榜
2018/08/14
6840
“爆款制造机”盒马,刷新网红美食的孵化路径
在没有明星代言、缺少前期沉淀的局面下,相关话题在微博上的阅读量超过4.2亿;尽管只有紫薯芋泥和大麦若叶两种口味,照旧在多个城市上演了上架即售罄的一幕……可能这样的数据尚未达到刷屏的程度,鉴于赛道的高度细分以及对特定人群的“量身打造”,仍然是可圈可点的经典案例。
Alter聊科技
2023/01/12
3210
数据产品面试题答题思路与逻辑解析
某美妆品牌(可以限定品类为面霜)计划推出一款新面霜产品,目标消费者为95后年轻女性,但不知道推出什么样的产品可能受欢迎;如果我们要做一个数据产品/工具,基于数据帮助品牌做出面霜产品决策,那我们的数据产品/工具应该包括什么样的功能
数据干饭人
2023/09/14
6410
数据产品面试题答题思路与逻辑解析
豆瓣网可用性测试报告
  豆瓣网简介:豆瓣(douban)是一个社区网站。网站由杨勃(网名“阿北”) 创立于2005年3月6日。该网站以书影音起家,提供关于书籍、电影、音乐等作品的信息,无论描述还是评论都由用户提供(User-generated content,UGC),是Web 2.0网站中具有特色的一个网站。网站还提供书影音推荐、线下同城活动、小组话题交流等多种服务功能,它更像一个集品味系统(读书、电影、音乐)、表达系统(我读、我看、我听)和交流系统(同城、小组、友邻)于一体的创新网络服务,一直致力于帮助都市人群发现生活中有用的事物。
顾翔
2019/12/12
1.2K0
豆瓣网可用性测试报告
这4种分析方法,大牛产品经理都在用
导读:设计产品有两种常见方式:一种是坐在办公室里拍脑袋设计;一种是先深入一线进行用户调研,然后基于调研结果来设计。
IT阅读排行榜
2022/04/14
5330
这4种分析方法,大牛产品经理都在用
车企新品牌、新车型层出,数据采集分析告诉你到底哪家强!
在之前的文章中,我们讨论到了汽车行业的现状、新能源汽车的崛起、汽车VoC系统的价值和如何搭建汽车VoC系统,今天我们来探讨一下汽车VoC在不同场景中的应用。
八爪鱼大数据
2021/11/29
6740
车企新品牌、新车型层出,数据采集分析告诉你到底哪家强!
用户体验,决定SaaS成败
来源:ToB老人家|作者:王戴明 ---- 腾讯SaaS加速器 三期40席项目招募 报名方式 腾讯SaaS加速器,作为腾讯产业加速器的重要组成部分,旨在搭建腾讯与SaaS相关企业的桥梁,通过资金、技术、资源、商机等生态层面的扶持,从战略到场景落地全方位加速企业成长,从而助力产业转型升级。 三期招募正式开始,扫描 二维码 立刻报名 (或点击文末  “阅读原文”,直达报名入口) 详情介绍:SaaS行业英雄集结令再发,腾讯SaaS加速器三期开启招募 在日常工作中,当我们说到SaaS产品的用户体验,往
腾讯SaaS加速器
2021/09/07
6370
推荐阅读
相关推荐
用户调研怎么做?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验