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

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

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

美團中選擇城市的界面

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

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

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

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

// 默認字體顏色
private int defaultFontColor = Color.WHITE;
// 默認選中字體顏色
private int defaultSelectedFontColor = Color.GRAY;
// 字體顏色
private int fontColor;
// 選中字體顏色
private int selectedFontColor;
// 字體大小
private float fontSize;
// 默認字體大小
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);

}

上面的代碼就是在構造器中初始化了自定義屬性废赞,大家應該都能看懂。

// 快速索引的字母
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叮姑。還要計算cellHeight,也就是INDEX_ARRAYS中每個字符串所占用的高度唉地,以便在onDraw(Canvas canvas)中使用。

我們來看看onDraw(Canvas canvas)

@Override
protected void onDraw(Canvas canvas) {
    // 遍歷畫出index
    for (int i = 0; i < INDEX_ARRAYS.length; i++) {
        // 測出字體的寬度
        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)為字母左下角的坐標
        canvas.drawText(INDEX_ARRAYS[i], x, y, mPaint);
    }

}

在代碼中去遍歷INDEX_ARRAYS,測量出字母的寬度和高度耘沼。這里要注意的是极颓,canvas.drawText(String text, float x, float y, Paint paint)中的 x,y 指的是字母左下角的坐標,并不是“原點”群嗤。

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

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

    /**
     * 當手指抬起
     */
    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();
            // 計算出觸摸的是哪個字母單元格
            selected = (int) (y / cellHeight);
            if (selected >= 0 && selected < INDEX_ARRAYS.length) {
                if (selected != lastSelected) {
                    if (listener != null) {
                        listener.onIndexChange(selected); // 回調監(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計算出了觸摸的y值對應的是索引中的哪個字母,然后回調了監(jiān)聽器骚烧;而在ACTION_UP中重置了lastSelected,回調了監(jiān)聽器闰围。

這樣赃绊,我們就把QuickIndexBar寫好了,關于QuickIndexBar使用的代碼就不貼出來了羡榴,太長了碧查。如果有需要,可以下載下面的Demo校仑,里面都有注釋忠售。Demo的效果圖如下:

20160322211942.gif

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

源碼下載:

ContactPicker.rar

GitHub:

ContactPicker

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末稻扬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子羊瘩,更是在濱河造成了極大的恐慌泰佳,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尘吗,死亡現(xiàn)場離奇詭異逝她,居然都是意外死亡,警方通過查閱死者的電腦和手機睬捶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門黔宛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人擒贸,你說我怎么就攤上這事臀晃。” “怎么了酗宋?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵积仗,是天一觀的道長。 經常有香客問我蜕猫,道長寂曹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮隆圆,結果婚禮上漱挚,老公的妹妹穿的比我還像新娘。我一直安慰自己渺氧,他們只是感情好旨涝,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侣背,像睡著了一般白华。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贩耐,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天弧腥,我揣著相機與錄音,去河邊找鬼潮太。 笑死管搪,一個胖子當著我的面吹牛,可吹牛的內容都是我干的铡买。 我是一名探鬼主播更鲁,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼奇钞!你這毒婦竟也來了澡为?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤景埃,失蹤者是張志新(化名)和其女友劉穎缀壤,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體纠亚,經...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡塘慕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蒂胞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片图呢。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖骗随,靈堂內的尸體忽然破棺而出蛤织,到底是詐尸還是另有隱情,我是刑警寧澤鸿染,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布指蚜,位于F島的核電站,受9級特大地震影響涨椒,放射性物質發(fā)生泄漏摊鸡。R本人自食惡果不足惜绽媒,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望免猾。 院中可真熱鬧是辕,春花似錦、人聲如沸猎提。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锨苏。三九已至疙教,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伞租,已是汗流浹背松逊。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肯夏,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓犀暑,卻偏偏與公主長得像驯击,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子耐亏,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內容