1. 程式人生 > >Android快速索引:實現微信通訊錄效果

Android快速索引:實現微信通訊錄效果

最近在練習自定義view,找了一些資料,模仿了一下微信通訊錄的實現效果,首先看一下效果圖:
這裡寫圖片描述

記錄下來當做筆記以備後用。
第一步:是繪製26個字母的view:
在每個構造方法中呼叫下面的init()方法實現畫筆的初始化

private void init() {
        //去掉字母鋸齒的引數
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.WHITE);
        // mPaint.setTypeface(Typeface.DEFAULT_BOLD);
        mPaint.setTextSize(40
);//設定字母大小 }

然後是onDraw()方法進行繪製:

@Override
    protected void onDraw(Canvas canvas) {
        //這裡用來繪製背景,當字母被觸控的時候會變成灰色
        if (mTouched) {
            canvas.drawColor(0x30000000);
        }

        for (int i = 0; i < letterLength; i++) {
            String text = LETTERS[i];
            // 計算座標
            int
x = (int) (cellWidth / 2.0f - mPaint.measureText(text) / 2.0f); // 獲取文字的高度 Rect bounds = new Rect();// 矩形 mPaint.getTextBounds(text, 0, text.length(), bounds); int textHeight = bounds.height(); int y = (int) (cellHeight / 2.0f + textHeight / 2.0f + i* cellHeight); // 設定文字顏色
mPaint.setColor(Color.BLACK); // 繪製文字A-Z canvas.drawText(text, x, y, mPaint); } }

根據註釋可以清楚的知道繪製的原理,就是計算26個字母的x和y座標,x左邊不變的,y座標規律的增加。

接下來重寫onTouchEvent()方法

boolean mTouched = false;
int touchIndex = -1;

    @Override
public boolean onTouchEvent(MotionEvent event) {
        int index = -1;
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
            // 獲取當前觸控到的字母索引
            index = (int) (event.getY() / cellHeight);
            if (index >= 0 && index < LETTERS.length) {
                // 判斷是否跟上一次觸控到的一樣
            if (index != touchIndex) {

                if (listener != null) {
                                    listener.onLetterUpdate(LETTERS[index]);
                    }

                    touchIndex = index;
                }
            }
            mTouched = true;
            break;
        case MotionEvent.ACTION_UP:
            if (listener != null) {
                listener.onFinished();
            }
            touchIndex = -1;
            mTouched = false;
            break;

        default:
            break;
        }
        invalidate();

        return true;
    }

原理就是根據兩個索引值,即當前按下字母的索引是否和上次的一樣,如果不一樣才會呼叫介面把字母暴露給呼叫方,否則如果不進行判斷的話,就會導致當手指在快速索引條上移動的時候總是會彈出相同的字母。還有就是記錄了一個按下的mTouched值,如果按下或者移動設定為true,否則為false。這樣就可以根據是否按下來繪製背景改變顏色了。再看一下ACTION_UP事件中暴露onFinished()方法給呼叫方,主要是為了將彈出的字母隱藏掉。這樣當手指按下的時候會彈出當前的字母,當手指擡起就會立即消失。

當view的大小發生變化時獲取單元格的寬和高

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 獲取單元格的寬和高

        cellWidth = getMeasuredWidth();

        int mHeight = getMeasuredHeight();
        cellHeight = mHeight * 1.0f / LETTERS.length;

    }

接下來就是回撥介面:

    /**
     * 暴露一個字母的監聽
     */
    public interface OnLetterUpdateListener {
        void onLetterUpdate(String letter);

        void onFinished();
    }

    private OnLetterUpdateListener listener;

    /**
     * 設定字母更新監聽
     * 
     * @param listener
     */
    public void setListener(OnLetterUpdateListener listener) {
        this.listener = listener;
    }

以上就是快速索引條的繪製過程。

接下來看一下MainActivity.java的主要程式碼:

// 設定監聽
bar.setListener(new OnLetterUpdateListener() {
            @Override
    public void onLetterUpdate(String letter) {
        showLetter(letter);
        // 根據字母定位ListView, 找到集合中第一個以letter為拼音首字母的物件,得到索引
        for (int i = 0; i < persons.size(); i++) {
            Person person = persons.get(i);
            String l = person.getPinyin().charAt(0) + "";
            if (TextUtils.equals(letter, l)) {
                // 匹配成功
                mMainList.setSelection(i);
                break;
            }
        }
    }

    @Override
    public void onFinished() {
        tv_center.setVisibility(View.GONE);
    }
});

這就是實現回撥方法,彈出顯示字母框和隱藏字母的功能。
還有一個就是對ListView中聯絡人拼音的第一個字母和手指此刻按下的字母進行比較,如果相等的話,就會將ListView位置定位到這個字母,也就是把該字母顯示在第一個位置。

這個是顯示字母的程式碼:

/**
* 顯示字母
*/
protected void showLetter(String letter) {
    tv_center.setVisibility(View.VISIBLE);
    tv_center.setText(letter);
}

這個是對通訊錄進行排序的方法:

private void fillAndSortData(ArrayList<Person> persons) {
    List<String> nameList = PhoneContactsUtils.getContactsName(this);
    // 填充資料
    for (int i = 0; i < nameList.size(); i++) {
        String name = nameList.get(i);
        persons.add(new Person(name));
    }

    // 進行排序
    Collections.sort(persons);
    }

以上就是主要的程式碼,還有工具類、實體類和介面卡的程式碼沒有貼出來,感覺太多了貼出來不方便閱讀。實體類就是一個名字和拼音兩個成員變數,並且要實現Comparable介面以便進行聯絡人根據字母排序。還有讀取手機通訊錄聯絡人等沒什麼好講的,直接放到原始碼包中就好了。