Android 带字母索引的侧边栏

之前已经用自定义View做出如下这样一个效果了

这里写图片描述

这两天需要重新拿来使用,发现效果虽然做出来了,不过思路不太对,就重新参考写了一个,用法也更为简单了

首要的自然是需要继承View绘制出侧边栏,并向外提供一个监听字母索引变化的方法

/**
 * 作者:叶应是叶
 * 时间:2017/8/20 11:38
 * 描述:
 */
public class LetterIndexView extends View {

    public interface OnTouchingLetterChangedListener {

        void onHit(String letter);

        void onCancel();
    }

    private OnTouchingLetterChangedListener touchingLetterChangedListener;

    private Paint paint;

    private boolean hit;

    private final String[] letters = {"↑", "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", "#"};

    private final int DEFAULT_WIDTH;

    public LetterIndexView(Context context) {
        this(context, null);
    }

    public LetterIndexView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LetterIndexView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setColor(Color.parseColor("#565656"));
        DEFAULT_WIDTH = dpToPx(context, 24);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getWidthSize(widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

    private int getWidthSize(int widthMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        switch (widthMode) {
            case MeasureSpec.AT_MOST: {
                if (widthSize >= DEFAULT_WIDTH) {
                    return DEFAULT_WIDTH;
                } else {
                    return widthSize;
                }
            }
            case MeasureSpec.EXACTLY: {
                return widthSize;
            }
            case MeasureSpec.UNSPECIFIED:
            default:
                return DEFAULT_WIDTH;
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                hit = true;
                onHit(event.getY());
                break;
            case MotionEvent.ACTION_MOVE:
                onHit(event.getY());
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                hit = false;
                if (touchingLetterChangedListener != null) {
                    touchingLetterChangedListener.onCancel();
                }
                break;
        }
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (hit) {
            //字母索引条背景色
            canvas.drawColor(Color.parseColor("#bababa"));
        }
        float letterHeight = ((float) getHeight()) / letters.length;
        float width = getWidth();
        float textSize = letterHeight * 5 / 7;
        paint.setTextSize(textSize);
        for (int i = 0; i < letters.length; i++) {
            canvas.drawText(letters[i], width / 2, letterHeight * i + textSize, paint);
        }
    }

    private void onHit(float offset) {
        if (hit && touchingLetterChangedListener != null) {
            int index = (int) (offset / getHeight() * letters.length);
            index = Math.max(index, 0);
            index = Math.min(index, letters.length - 1);
            touchingLetterChangedListener.onHit(letters[index]);
        }
    }

    public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
        this.touchingLetterChangedListener = onTouchingLetterChangedListener;
    }

    private int dpToPx(Context context, float dpValue) {
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

}

在侧边栏时,中间会显示当前滑动指向的字母,这其实是一个TextView,在主布局文件中添加,通过IndexControl来控制TextView的可见性,并指示ListView滑动到指定项

/**
 * 作者:叶应是叶
 * 时间:2017/8/20 11:39
 * 描述:
 */
public class IndexControl {

    private final ListView listView;

    private final TextView tv_hint;

    private final Map<String, Integer> letterMap;

    public IndexControl(ListView contactsListView, LetterIndexView letterIndexView,
                        TextView tv_hint, Map<String, Integer> letterMap) {
        this.listView = contactsListView;
        this.tv_hint = tv_hint;
        this.letterMap = letterMap;
        letterIndexView.setOnTouchingLetterChangedListener(new LetterChangedListener());
    }

    private class LetterChangedListener implements LetterIndexView.OnTouchingLetterChangedListener {

        @Override
        public void onHit(String letter) {
            tv_hint.setVisibility(View.VISIBLE);
            tv_hint.setText(letter);
            int index = -1;
            if ("↑".equals(letter)) {
                index = 0;
            } else if (letterMap.containsKey(letter)) {
                index = letterMap.get(letter);
            }
            if (index < 0) {
                return;
            }
            index += listView.getHeaderViewsCount();
            if (index >= 0 && index < listView.getCount()) {
                listView.setSelectionFromTop(index, 0);
            }
        }

        @Override
        public void onCancel() {
            tv_hint.setVisibility(View.INVISIBLE);
        }
    }

}

这里也提供代码下载:LetterIndexView

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏跟着阿笨一起玩NET

c# 使用timer定时器操作,上次定时到了以后,下次还未执行完怎么处理

------解决方案-------------------------------------------------------- 开始的时候,禁用定时器,你...

2661
来自专栏Jerry的SAP技术分享

How to update BOL entity property value via ABAP code

Suppose I have one product with ID I042416 which could be found in CRM WebClient...

510
来自专栏草根专栏

适合在Markdown里面使用的emoji

因为Markdown里面加颜色需要写html style, 所以对于一些标题, 还是用一下emoji吧: RED APPLE (&#x1F34E;): ? GR...

2714
来自专栏MelonTeam专栏

Bitmap 源码阅读笔记

导语: Android 系统上的图片的处理,跟Bitmap 这个类脱不了关系,我们有必要去深入阅读里面的源码,以便在工作中能更好的处理Bitmap相关的问题...

2518
来自专栏一个会写诗的程序员的博客

java.base.jmod

/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/jmods$ jmod list java....

1112
来自专栏Ryan Miao

ehcache报错

jfinal2.0+tomcat7+ehcache2.6.11+Linux Linux version 2.6.18-164.el5 (mockbuild@x8...

3729
来自专栏搞前端的李蚊子

Html5模拟通讯录人员排序(sen.js)

// JavaScript Document  var PY_Json_Str = ""; var PY_Str_1 = ""; var PY_Str_...

5956
来自专栏前端儿

Web 前端颜色值--字体--使用,整理整理

颜色值 CSS 颜色使用组合了红绿蓝颜色值 (RGB) 的十六进制 (hex) 表示法进行定义。对光源进行设置的最低值可以是 0(十六进制 00)。最高值是 2...

2322
来自专栏WOLFRAM

向日葵中的数学之美

1843
来自专栏我和未来有约会

简练的视图模型 ViewModel

patterns & practices Developer Center 发布了 Unity Application Block 1.2 for Silver...

2189

扫码关注云+社区