浅谈android中手机联系人字母索引表的实现

实际上字母索引表的效果,可以说在现在的众多APP中使用的非常流行,比如支付宝,微信中的联系人,还有购物,买票的APP中选择全国城市,切换城市的时候,这时候的城市也就是按照一个字母索引的顺序来显示,看起来是很方便的.其实这种字母索引表的效果最开始是出现在微信的联系人中.因为觉得这种效果功能在今后的项目中可以说是非常常见,可能会用的上,所以准备来波博客讲述一下实现的原理,一来方便以后自己复习,二来如果能够帮助一些android路上奋斗小伙伴也是蛮有意义的.
下面我们先来看下效果图,


看完效果图后我们可以来分析一下这个看似很复杂的功能怎么分解成一个个小功能来实现.要想实现如下的demo效果,主要关注在四个大的方面:
        1,实现右侧浮动字母索引项的列表:
      问题分析:
                     右侧浮在表面的字母竖向排列的view的实现,并且点击view中相应的字母,会弹出一个自定义的对话框,并且对话框只有一个TextView用于显示过那个点击后的字母,并且仔细观察这个弹出的字母对话框还会延迟一段时间才会消失,还有一个很重要也是很明显的效果:就是点击相应右边的竖向字母列表中的字母的时候并与中间的显示联系人的ListView中的字母item中的字母相等的时候,才会弹出字母对话框并且会将联系人列表中的对应的字母item顶到界面的顶部显示,最后还有一点就是这个竖向的view实际上在上下滑动的时候会不断改变弹出的内容以及联系人列表中的字母项不断跳动.
      实现方法:
                     通过自定义一个view,来准确绘制出字母项索引,因为还需要实现当我们点击浮动字母列表时,弹出被点击的字母text,所以很容易就想到
                     在这个自定义的右侧浮动字母索引项的列表中应该还有一个我们自定义的监听器,监听字母索引表的点击和滑动事件,利用监听器中的回调方法中的参数返回我们的点击或者滑动到字母,如果我们点击或者滑动的字母正好与我们联系人列表中的字母索引相等,才会去弹出字母显示框


        2,实现联系人list列表效果:
        问题分析:
                        第二点就是联系人那种已经按照字母表排好序的列表ListView的实现,这种ListView是怎么实现的呢?实现这种相同首字母的联系人放在一起显示,并在这些相同首字母的联系人子列表的最前面加上一个字母索引项.
         解决办法:这个我是在我以前自己封装的CommonAdapter进行扩展的,很是方便,至于CommonAdapter(即打造ListView的通用适配器封装)的封装个人灵感和取经于android大神hyman,不过自己CommAdapter有点自己见解,最近一直在整理,希望出一期有关listView和GridView的博客
      
        3,获取联系人数据: 联系人数据从哪来?因为是读取手机中的联系人,所以很简单用我们非常熟悉的android中的四大组件之一ContentProvider
        来获得手机中自带应用中的数据库.很开心的就是拿数据的时候我们可以看到在raw_contact表中的phonebook_label字段中保存了联系人中文第一个字的首字母.所以我们就省去了取得每一个联系人的中文第一个字拼音的首字母,并且还得借助pinyin4.jar获得每个中文汉字的拼音,从而可以拿到首字拼音的首字母,然后将这些字母以及相应联系人的信息作为一个Bean类保存起来,最后可以使用一个Bean类的集合对象来存储手机联系人的信息.即使我们拿到相应的首字拼音的首字母 放入到相应的集合中去,此时的集合时不合格的,我们需要将集合的数据对象,按照其对应的首字拼音的首字母排序,从a-z
      排序主要用到了Collections.sort(list,compator)以及定义一个Compator比较对象.这样最后就取得拿到的数据并且按照联系人首字的拼音的首字母从a到z的排序好的集合.


      4,整合建立关系: 
      做到这里就是各个方面的工作都完成了,但是三件事貌似没什么关系,但是有一种关系千万别忽略了就是点击了竖向字母表中的字母的时候此时联系人列表中的字母索引项会跟着点击的字母变化而跳到顶部位置.所以需要建立关联就是通过一个viewpager.setSelection()方法就行了


    以上就是整个自定义view实现手机联系人的字母索引表的实现大致思路逻辑.


    那么我们就开始吧

第一,首先我们来解决自定义View实现浮动的字母索引项的列表.

package com.mikyou.contactdemo.myview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class MikyouLetterListView extends View {

	private OnTouchingLetterChangedListener listener;
	//定义了显示在最右边的浮动的索引项的列表,当然这个是固定的,所以可以直接初始化,如果需要变动的话则可以通过自定义属性来指定
	String[] b = {  "#","A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
			"L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
			"Y", "Z"};
	int choose = -1;//用于标记点击存放字母数组中的下标
	Paint paint = new Paint();
	boolean showBkg = false;//这个boolean变量主要是控制当我们点击索引列表中整个索引列表的背景有个变化,为false表示开始没点击背景为正常显示时候的背景

	public MikyouLetterListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public MikyouLetterListView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public MikyouLetterListView(Context context) {
		super(context);
	}


	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		if (showBkg) {//如果此时为true的话则表示需要改变整个canvas背景也即是索引项的背景
			canvas.drawColor(Color.parseColor("#10000000"));
		}
		/**
		 * 注意:在自定义view中的如果不需要设置wrap_content属性就不需要自己重写onMeasure方法
		 * 因为在onMeasure方法中系统默认会自动测量两种模式:一个是match_parent另一个则是自己指定明确尺寸大小
		 * 这两种方法对应着这一种MeasureSpec.AT_MOST测量模式,由于我们设置这个自定义浮动的字母索引表宽度是指定明确大小
		 * 高度是match_parent模式,所以这里就不要手动测量了直接通过getHeight和getWidth直接拿到系统自动测量好高度和宽度
		 * */
		int height = getHeight();
		int width = getWidth();
		//让整个显示的每个字母均分整个屏幕高度尺寸,这个singleHeight就是每个字母占据的高度
		int singleHeight = height / b.length;
		//遍历循环绘制每一个字母text
		for (int i = 0; i < b.length; i++) {

			//绘制字母text的颜色
			paint.setColor(Color.parseColor("#515151"));
			//绘制字母text的字体大小
			paint.setTextSize(25);
			//绘制字母text的字体样式
			paint.setTypeface(Typeface.DEFAULT_BOLD);
			//设置抗锯齿样式
			paint.setAntiAlias(true);
			if (i == choose) {//判断如果点击字母的下标等于i,那么就会设置绘制点击字母的样式用于高亮显示
				paint.setColor(Color.parseColor("#3399ff"));
				paint.setFakeBoldText(true);
			}
			/**
			 * 注意:canvas在绘制text的时候,他绘制的起点不是text的左上角而是它的左下角
			 * (xPos,yPos)表示每个字母左下角的位置的坐标
			 *xPos = width / 2 - paint.measureText(b[i]) / 2:意思很容易理解,就是用
			 * (总的view的宽度(可能还包括leftPadding和rightPadding的大小)-每个字母宽度)/2得到就是每个字母的左下角的X坐标,
			 * 仔细想下每个text的起点的x坐标都是一样的.paint.measureText(b[i])得到每一个字母宽度大小
			 * 由于是左下角,所以它们的Y坐标:应该如下设置 yPos = singleHeight * i + singleHeight;
			 * */
			float xPos = width / 2 - paint.measureText(b[i]) / 2;//得到绘制字母text的起点的X坐标
			float yPos = singleHeight * i + singleHeight;//得到绘制字母text的起点的Y坐标
  • 6
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熊喵先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值