联系人列表字母排序索引(一)

原创 2016年05月31日 13:57:01

很久没有写博客了,对自己又放松了很多。这篇博客本来早就要写了,迟迟拖到现在。今天这里要说的是,联系人列表,分组带索引,即联系人按字母顺序排列并分组,右边还有索引条。先看下效果:



就是这么一个效果,想必大家都想知道是怎么实现的吧。其实很简单,接下来我会慢慢讲解这个效果的实现。


首先我们分析这个联系人列表的组成:

1.联系人列表 ----- ListView

2.右边索引-------自定义View

3.浮动窗口------WindowManager

4.获取名称的首字母----PinYin4j(已经将github上的封装成了依赖包)



下面我将按照我的实现顺序进行说明:

1.先自定义右边索引。

2.设置浮动窗口。

3.添加列表数据,对数据进行排序,对列表进行分组。

4.添加列表与索引间的监听。



一、自定义索引

这里的索引,我把它命名为IndexView,IndexView 包含26个英文字母和“#”,每个字母所占据的区域位置,IndexView按下时显示黑色背景,每个字母都有文字颜色,触摸时浮动窗口显示不同的字母。这里,我们就有概念了,于是可以开始写代码了:

1.先定义基本的成员变量

 /*字母表数组*/
    private String letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ#";
    private Paint mPaint;
    private int mTextColor;
    private int mTextSize;
    private int mPadding;
    private int mLetterWidth;
    private int mLetterHeight;
    //当被触摸时,绘制背景
    private boolean isTouched;
    private int mBackgroundColor;
    private int mTransparentColor;
    //除了用于绘制外,更涉及到触摸事件
    private ArrayList<Rect> mRects;
    private OnCharTouchEvent mListener;
    //上一次获取的字母
    private String mPreLetter;

2.然后开始写逻辑,首先,我们必须继承View,然后重写构造方法,在构造方法中都调用一个init()方法:

public class IndexView extends View
    public IndexView(Context context) {
        super(context);
        init(context);
    }

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

    public IndexView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

init里面完成了什么工作呢?主要是对一些成员变量的初始化,其中画笔用来绘制文字的:

  private void init(Context context) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mTextColor = Color.parseColor("#666666");
        mBackgroundColor =  Color.parseColor("#bbbbbb");
        mTransparentColor = Color.parseColor("#00000000");
        mRects = new ArrayList<>();
    }

3.设置每个字母的位置,每个字母的位置都是一个矩形区域,我们用Rect表示,Rects里面保存所有的字母位置,因此,重写onSizeChanged方法,onSizeChanged方法,是在measure之后调用的,因为在onMeasure里面,我们会得到每个字母的宽和高,

因此这里能够得到每个字母的矩形位置:

 @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mRects.clear();
        for (int i = 0; i < letter.length(); i++) {
            int x = 0;
            int y = mLetterHeight * i;
            Rect rect = new Rect(x, y, x + mLetterWidth, y + mLetterHeight);
            mRects.add(rect);
        }
    }

4.重写 onMeasure方法,调用于onSizeChanged之前,这里面我们要完成测量当前自定义View的宽和高,以及每个字母的宽和高,这里我们设置字母所占的宽和高是一样的,因此,当前自定义view的宽也就是字母的宽,然后按比例设置了字母的大小,间距等。

   @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //widthMeasureSpec是父类中,调用该类的measure方法设置的
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        mLetterWidth = mLetterHeight = heightSize / letter.length();
        mPadding = mLetterHeight / 5;
        mTextSize = mLetterHeight - mPadding * 2;
        setMeasuredDimension(mLetterWidth, heightSize);
    }


5.重写onDraw()方法,主要完成字母的绘制工作。先通过一个变量,判断当前是否是触摸状态,由此设置绘制背景;接着循环绘制每一个字母。

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(isTouched ? mBackgroundColor : mTransparentColor);
        for (int i = 0; i < letter.length(); i++) {
            char c = letter.charAt(i);
            String s = String.valueOf(c);
            mPaint.setColor(mTextColor);
            mPaint.setTextSize(mTextSize);
            mPaint.setTextAlign(Paint.Align.CENTER);
            drawTextCenter(canvas, mPaint, s, mRects.get(i));
        }

    }

   /**
     * 将文本绘制在居中位置
     *
     * @param canvas
     * @param paint
     * @param s
     * @param rect
     */
    private void drawTextCenter(Canvas canvas, Paint paint, String s, Rect rect) {
        Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
        int baseline = rect.top + (rect.bottom - rect.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
        paint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText(s, rect.centerX(), baseline, paint);
    }

drawTextCenter使用之前,需要设置画笔居中,这个方法是根据文本的绘制原理,来将文本绘制在居中位置的。不懂的可以自行百度,谷歌。


到这里,我们已经绘制完一个索引了。但是这是不够的,我们还需要为它添加触摸事件。


6.重写onTouchEvent方法。

/**
     * 接收到事件,进行处理,并消费掉
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                doDown(event);
                break;
            case MotionEvent.ACTION_MOVE:
                doMove(event);
                break;
            case MotionEvent.ACTION_UP:
                doCancelOrUp();
                break;
            case MotionEvent.ACTION_CANCEL:
                doCancelOrUp();
                break;
        }
        return true;
    }

直接返回了true,即所有的触摸事件到这里,都将被消费掉。然后针对不同的事件进行处理。

ActionDown 事件:

   private void doDown(MotionEvent event) {
        isTouched = true;
        int downX = (int) event.getX();
        int downY = (int) event.getY();
        String s = getTouchedLetter(downX, downY);
        Log.v("@s", s + "");
        if (mListener != null)
            mListener.onTouch(s);
        invalidate();
    }
记录位置,根据位置获取对应的字母,然后设置监听事件。(这里,先理解,getTouchedLetter为通过坐标位置获取字母,mListener为监听事件,用于回调浮动窗口的)。


ActionMove事件:

 /**
     * 移动时,判断选中的字母是否发生了变化,并进行回调
     *
     * @param event
     */
    private void doMove(MotionEvent event) {
        isTouched = true;
        int downX = (int) event.getX();
        int downY = (int) event.getY();
        String s = getTouchedLetter(downX, downY);
        if (s == null)
            return;
        if (mListener != null) {
            if (mPreLetter == null || (!mPreLetter.equalsIgnoreCase(s))) {
                mListener.onLetterChanged(mPreLetter, s);
                mPreLetter = s;
            }
        }

        invalidate();
    }
这里每次都要记录上一个的字母,如果字母发生变化,才回调。

ActionUp|ActionCancel事件:

   isTouched = false;
        if (mListener != null)
            mListener.onRelease();
        invalidate();
触摸标志位设置为false,并回调关闭浮动窗口


通过位置获取字母,循环判断坐标位置,包含在哪个字母的Rect矩形位置中,通过这个位置获取字母。

  private String getTouchedLetter(int x, int y) {
        for (int i = 0; i < mRects.size(); i++) {
            Rect rect = mRects.get(i);
            if (rect.contains(x, y)) {
                return String.valueOf(letter.charAt(i));
            }
        }

        return null;
    }


最后一个是监听接口:

  public interface OnCharTouchEvent {
        void onTouch(String s);

        void onLetterChanged(String preLetter, String letter);

        void onRelease();
    }


到这里,我们的效果是这样的:




今天先到这里,源码提前放出


源码



版权声明:本文为博主原创文章,未经博主允许不得转载。

仿微信联系人索引列表ListView

IM 模块中经常用到 字母索引 ListView 来做通讯录 或者称联系人列表, 今天跟大家分享一个仿微信联系人索引列表, 优点是轻量级,简单易懂. 不要任何依赖 , jar包等 效果预览工程结构使...

仿Android6.0联系人列表

最近因为项目需要,研究了Android联系人相关内容,包括联系人数据库,获取联系人数据,使用ListView展示联系人。我将按照以下几点记录:      Android存储联系人数据库表结构  ...

实现通讯录分组排序的一个方法

在APP开发中,我们经常会去做一个通讯录。所有做iOS开发的都知道这玩意儿应该用UITableView来做。但是,如何将数据分组排序总是一个比较麻烦的问题,这里提供我个人的解决方法供大家参考。 ...

城市列表-根据拼音首字母排序

今天我们就简单的实现一下城市的排序 读取我们城市的信息并通过listview展示 首先看一下我们的布局文件...
  • Mannver
  • Mannver
  • 2016年05月04日 15:47
  • 6632

Android 实现ListView的A-Z字母排序和过滤搜索功能,实现汉字转成拼音

前段时间因为换工作的缘故又恰巧碰到国庆节,所以有段时间自己没有更新博客了,过完国庆到新公司报道,感觉还不错,就是现在住的地方离新公司有点远,地铁20站,伤不起啊,我每天早上7点多就要起床,然后屁颠屁颠...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

联系人列表字母排序索引(三)

也是忙忙碌碌好几天,今天又有时间了,继续这个文章的编写。今天是这篇文章的最后一部分。主要内容包括以下几点: 1.将中文名字转化成拼音,并提取首字母,进行排序。 2.实现分组列表Adapter模板。 3...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

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

实际上字母索引表的效果,可以说在现在的众多APP中使用的非常流行,比如支付宝,微信中的联系人,还有购物,买票的APP中选择全国城市,切换城市的时候,这时候的城市也就是按照一个字母索引的顺序来显示,看起...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:联系人列表字母排序索引(一)
举报原因:
原因补充:

(最多只允许输入30个字)