自定义通讯录字母索引

来源:互联网 发布:塑料卡扣连接技术 淘宝 编辑:程序博客网 时间:2024/06/01 12:03

1、先来看下布局的效果

布局的代码如下,其中LetterIndexView为我们将要自定义的控件,使用相对布局置于界面的右侧;

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.itemp.letterindexview.MainActivity">    <ListView        android:id="@+id/lvFriends"        android:layout_width="match_parent"        android:layout_height="match_parent"/>    <TextView        android:id="@+id/tvCurrentLetter"        android:layout_width="100dp"        android:layout_height="100dp"        android:background="@drawable/shape_letterindexview_bg_pressed"        android:gravity="center"        android:textSize="50sp"        android:layout_centerInParent="true"        android:textColor="@color/white"        android:textStyle="bold"        android:visibility="visible"        android:text="A" />    <com.itemp.letterindexview.LetterIndexView        android:id="@+id/liv"        android:layout_width="35dp"        android:layout_height="match_parent"        android:layout_margin="5dp"        android:layout_alignParentRight="true"        /></RelativeLayout>

2、继承于View并使用绘图法在画布上绘制字母:

public class LetterIndexView extends View

3、实现构造方法,在其中初始化画笔,并为控件设置背景图(shape资源制作的圆角矩形)

    public LetterIndexView(Context context) {        this(context, null);    }    public LetterIndexView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public LetterIndexView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        //setBackgroundResourece();        setBackgroundResource(R.drawable.shape_letterindexview_bg);        paint = new Paint();        paint.setAntiAlias(true);//抗锯齿    }

4、shape资源的定义代码:res/drawable/shape_letterindexview_bg.xml

<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android">    <solid android:color="#fff" />    <stroke        android:width="1dp"        android:color="#ddd" />    <corners android:radius="10dp" /></shape>

这是一个白色实心的圆角矩形,按下后将其变为黄色实心的圆角矩形,文件为res/drawable/shape_letterindexview_bg_pressed.xml:

<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android">    <solid android:color="#ff0" />    <stroke        android:width="1dp"        android:color="#ddd" />    <corners android:radius="10dp" /></shape>

5、定义字符串数组作为索引的文本:

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

6、覆写onDraw()方法,将字母纵向排列均匀地绘制在画布上:

@Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if (width == 0) {            width = getWidth();            height = getHeight();        }        //把字母画在控件上,【选中字母】用红色画笔,否则黑色        for (int i = 0; i < letters.length; i++) {            //计算startX,控件宽度的一半减去字母宽度的一半            String letter = letters[i];            float letterSize = paint.measureText(letter);            float startX = (width - letterSize) / 2;            //startY,上方所有单元格的高度之和+(单元格高度的一半+字母高度的一半)            float unitHeight = (height - 40) / 27f;            float startY = 20 + i * unitHeight + (unitHeight + letterSize) / 2;            //高亮字母为红色,否则为黑色            if(i == currentPosition){                paint.setColor(Color.RED);            }else {                paint.setColor(Color.BLACK);            }            paint.setTextSize(35);            paint.setStyle(Paint.Style.FILL_AND_STROKE);//使用加粗效果            canvas.drawText(letter, startX, startY, paint);//绘制字母        }    }

7、接下来覆写onTouchEvent()定义手指在控件上的滑动响应,逻辑为:
·手指按下,整个控件的背景色变为黄色,并根据手指按下的y的位置,确认哪个字母为选中字母,并重绘以将该字母高亮显示,并通知外界响应按下事件(比如显示小窗口见本文末尾GIF)
·手指滑动,动态改变选中字母,并重绘以将该字母高亮显示
·手指抬起,控件背景恢复为默认的白色,并通知外界响应(比如隐藏小窗口见文章末尾GIF)

    @Override    public boolean onTouchEvent(MotionEvent event) {        float y = event.getY();        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                //改变背景效果                setBackgroundResource(R.drawable.shape_letterindexview_bg_pressed);                //根据手指位置设置高亮字母并重绘                invalidateCurrentPosition(y);                //通知外界手指按下                if (callback != null) {                    callback.onFingerDown(true);                }                break;            case MotionEvent.ACTION_MOVE:                //根据手指位置设置高亮字母并重绘                invalidateCurrentPosition(y);                //通知外界字母变化                if (callback != null) {                    callback.onLetterChanged(letters[currentPosition]);                }                break;            case MotionEvent.ACTION_UP:                //恢复背景效果                setBackgroundResource(R.drawable.shape_letterindexview_bg);                //通知外界手指抬起                if (callback != null) {                    callback.onFingerDown(false);                }                break;        }        return true;    }    /**     * 根据手指位置动态设置高亮字母并重绘     * @param y     */    private void invalidateCurrentPosition(float y) {        currentPosition = (int) ((y / height) * letters.length);        if(currentPosition > 26){            currentPosition = 26;        }        invalidate();    }

11、以接口的方式通知外界:手指按下或抬起,高亮字母发生改变:

    LetterIndexCallback callback;    public void setCallback(LetterIndexCallback callback) {        this.callback = callback;    }    public interface LetterIndexCallback {        void onFingerDown(boolean down);        void onLetterChanged(String letter);    }

12、最后当外界ListView主动滚动时,字母索引的选中字母也随之变化,我们为外界提供公共方法,用于更新选中字母的位置:

    /**     * 供外界ListView滚动时通知到当前控件     * @param firstLetter     */    public void setCurrentLetter(String firstLetter) {        for (int i = 0; i < letters.length; i++) {            if(letters[i].equals(firstLetter)){                setCurrentPosition(i);                return;            }        }    }

13、Activity实现【索引控件】的回调接口,并将自身设置给【索引控件】:

public class MainActivity extends AppCompatActivity implements LetterIndexView.LetterIndexCallback 
@Override    public void onLetterChanged(String letter) {        tvCurrentLetter.setText(letter);    }    @Override    public void onFingerDown(boolean fingerDown) {        if(fingerDown){            tvCurrentLetter.setVisibility(View.VISIBLE);        }else {            tvCurrentLetter.setVisibility(View.GONE);        }    }
letterIndexView.setCallback(this);

效果如下:
这里写图片描述

0 0
原创粉丝点击