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介面以便進行聯絡人根據字母排序。還有讀取手機通訊錄聯絡人等沒什麼好講的,直接放到原始碼包中就好了。