快速打造仿Android聯(lián)系人界面

博文出處:快速打造仿Android聯(lián)系人界面,歡迎大家關(guān)注我的博客,謝謝!

有段時(shí)間沒寫博客了,趁今天有空就寫了一篇。今天的主題就是仿聯(lián)系人界面。相信大家在平時(shí)都見過,就是可以實(shí)現(xiàn)快速索引的側(cè)邊欄。比如在美團(tuán)中選擇城市的界面:

美團(tuán)中選擇城市的界面

我們可以看到在右側(cè)有一個(gè)支持快速索引的欄。接下來,我們就要實(shí)現(xiàn)這種索引欄。

首先是attrs.xml,定義了三個(gè)自定義屬性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="QuickIndexBar">
        // 字體的顏色
        <attr name="font_color" format="color|reference"></attr>
        // 選中時(shí)字體的顏色
        <attr name="selected_font_color" format="color|reference"></attr>
        // 字體的大小
        <attr name="font_size" format="dimension|reference"></attr>
    </declare-styleable>
</resources>

之后我們創(chuàng)建一個(gè)類繼承自View,類名就叫QuickIndexBar

// 默認(rèn)字體顏色
private int defaultFontColor = Color.WHITE;
// 默認(rèn)選中字體顏色
private int defaultSelectedFontColor = Color.GRAY;
// 字體顏色
private int fontColor;
// 選中字體顏色
private int selectedFontColor;
// 字體大小
private float fontSize;
// 默認(rèn)字體大小
private float defaultfontSize = 12;
// 上次觸摸的字母單元格
int lastSelected = -1;
// 這次觸摸的字母單元格
int selected = -1;

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

public QuickIndexBar(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public QuickIndexBar(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.QuickIndexBar);
    fontColor = a.getColor(R.styleable.QuickIndexBar_font_color, defaultFontColor);
    selectedFontColor = a.getColor(R.styleable.QuickIndexBar_selected_font_color, defaultSelectedFontColor);
    fontSize = a.getDimension(R.styleable.QuickIndexBar_font_size,
            TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, defaultfontSize,
                    getContext().getResources().getDisplayMetrics()));
    a.recycle();
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint.setColor(fontColor);
    mPaint.setTypeface(Typeface.DEFAULT_BOLD);
    mPaint.setTextSize(fontSize);

}

上面的代碼就是在構(gòu)造器中初始化了自定義屬性,大家應(yīng)該都能看懂。

// 快速索引的字母
public static final String[] INDEX_ARRAYS = 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"};
// 控件的寬度
private int width;
// 控件的高度
private int height;
// 字母單元格的寬度
private float cellHeight;

/**
 * 得到控件的大小
 */
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    width = getMeasuredWidth();
    height = getMeasuredHeight();
    //  得到字母單元格的高度
    cellHeight = height * 1.0f / INDEX_ARRAYS.length;
}

然后在onSizeChanged(int w, int h, int oldw, int oldh)中獲取widthheight。還要計(jì)算cellHeight,也就是INDEX_ARRAYS中每個(gè)字符串所占用的高度,以便在onDraw(Canvas canvas)中使用。

我們來看看onDraw(Canvas canvas)

@Override
protected void onDraw(Canvas canvas) {
    // 遍歷畫出index
    for (int i = 0; i < INDEX_ARRAYS.length; i++) {
        // 測(cè)出字體的寬度
        float x = width / 2 - mPaint.measureText(INDEX_ARRAYS[i]) / 2;
        // 得到字體的高度
        Paint.FontMetrics fm = mPaint.getFontMetrics();
        double fontHeight = Math.ceil(fm.descent - fm.ascent);

        float y = (float) ((i + 1) * cellHeight - cellHeight / 2 + fontHeight / 2);
        if (i == selected) {
            mPaint.setColor(lastSelected == -1 ? fontColor : selectedFontColor);
        } else {
            mPaint.setColor(fontColor);
        }
        // 繪制索引的字母 (x,y)為字母左下角的坐標(biāo)
        canvas.drawText(INDEX_ARRAYS[i], x, y, mPaint);
    }

}

在代碼中去遍歷INDEX_ARRAYS,測(cè)量出字母的寬度和高度。這里要注意的是,canvas.drawText(String text, float x, float y, Paint paint)中的 x,y 指的是字母左下角的坐標(biāo),并不是“原點(diǎn)”。

別忘了我們還要對(duì)QuickIndexBar的觸摸事件作出處理。所以我們要重寫onTouchEvent(MotionEvent event):

/**
 * 設(shè)置當(dāng)索引改變的監(jiān)聽器
 */
public interface OnIndexChangeListener {
    /**
     * 當(dāng)索引改變
     *
     * @param selectIndex 索引值
     */
    void onIndexChange(int selectIndex);

    /**
     * 當(dāng)手指抬起
     */
    void onActionUp();
}

public void setOnIndexChangeListener(OnIndexChangeListener listener) {
    this.listener = listener;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    float y;
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
            y = event.getY();
            // 計(jì)算出觸摸的是哪個(gè)字母單元格
            selected = (int) (y / cellHeight);
            if (selected >= 0 && selected < INDEX_ARRAYS.length) {
                if (selected != lastSelected) {
                    if (listener != null) {
                        listener.onIndexChange(selected); // 回調(diào)監(jiān)聽器的方法
                    }
                    Log.i(TAG, INDEX_ARRAYS[selected]);
                }
                lastSelected = selected;
            }
            break;
        case MotionEvent.ACTION_UP:
            // 把上次的字母單元格重置
            lastSelected = -1;
            listener.onActionUp();
            break;
    }
    invalidate(); // 重繪視圖
    return true;
}

ACTION_DOWNACTION_MOVE計(jì)算出了觸摸的y值對(duì)應(yīng)的是索引中的哪個(gè)字母,然后回調(diào)了監(jiān)聽器;而在ACTION_UP中重置了lastSelected,回調(diào)了監(jiān)聽器。

這樣,我們就把QuickIndexBar寫好了,關(guān)于QuickIndexBar使用的代碼就不貼出來了,太長(zhǎng)了。如果有需要,可以下載下面的Demo,里面都有注釋。Demo的效果圖如下:

20160322211942.gif

好了,今天就到這里了。have fun!

源碼下載:

ContactPicker.rar

GitHub:

ContactPicker

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容