先看下效果圖
在這里我們先分析下效果,左邊是一個按照字母排序的并有子列表卧蜓,右邊是直接一個字母的排列帐要,屏幕中間有一個顯示你點擊了右邊的那個字母的顯示。
在這里我們先去處理右邊的字母列表弥奸。我們先去自定義一個view榨惠,把26個字母和“#”放到一個數(shù)組里面,再用畫筆在ondraw()里面畫出來其爵。
@Override
protected void onDraw(Canvas canvas) {
// 畫26個字母
int itemHeight = (getHeight() - getPaddingTop() - getPaddingBottom()) / mLetters.length;
for (int i = 0; i < mLetters.length; i++) {
// 知道每個字母的中心位置 1 字母的高度一半 2 字母高度一般+前面字符的高度
int letterCenterY = i * itemHeight + itemHeight / 2 + getPaddingTop();
// 基線冒冬,基于中心位置, 知道中心位置還不會基線,看一下之前的視頻
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
int dy = (int) ((fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);
int baseLine = letterCenterY + dy;
// x 繪制在最中間 = 寬度/2 - 文字/2
int textWidth = (int) mPaint.measureText(mLetters[i]);
int x = getWidth() / 2 - textWidth / 2;
if (!isUp){
// 當前字母 高亮 用兩個畫筆(最好) 改變顏色
if (mLetters[i].equals(mCurrentTouchLetter)) {
mPaint.setColor(Color.RED);
canvas.drawText(mLetters[i], x, baseLine, mPaint);
} else {
mPaint.setColor(Color.BLUE);
canvas.drawText(mLetters[i], x, baseLine, mPaint);
}
}else {
mPaint.setColor(Color.BLUE);
canvas.drawText(mLetters[i], x, baseLine, mPaint);
}
}
}
當我們在這個字母列表上面滑動的時候摩渺,被點擊到的字母會變色简烤,這就需要有兩個顏色的畫筆去分別畫不同的字母。
我們可以在onTouchEvent方法里面拿到按下和滑動的事件摇幻,在MotionEvent.ACTION_DOWN和MotionEvent.ACTION_MOVE的時候處理字母的變色横侦。我們首先得那個我們點擊的是哪個字母挥萌,我們可以拿到當前點擊位置的y方向的距離去除以每個字母的高度,然后取整就知道那個字母被點擊了枉侧,這時候調(diào)用invalidate方法去重新繪制字母列表引瀑,這樣就可以實現(xiàn)當那個字母被點擊或者是滑動時被覆蓋的字母變色的效果。然后在這里我們寫一個回調(diào)榨馁,去讓頁面中間的那個textview知道如何去顯示那個字母被點擊了憨栽。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
// 計算出當前觸摸字母 獲取當前的位置
float currentMoveY = event.getY();
// 位置 = currentMoveY / 字母高度 , 通過位置獲取字母 優(yōu)化翼虫?
int itemHeight = (getHeight() - getPaddingTop() - getPaddingBottom()) / mLetters.length;
currentPosition = (int) (currentMoveY / itemHeight);
if (currentPosition < 0)
currentPosition = 0;
if (currentPosition > mLetters.length - 1)
currentPosition = mLetters.length - 1;
// 要判斷 屑柔?
mCurrentTouchLetter = mLetters[currentPosition];
if (mListener != null) {
mListener.touch(mCurrentTouchLetter, true, currentPosition);
}
isUp = false;
// 重新繪制
invalidate();
break;
case MotionEvent.ACTION_UP:
if (mListener != null) {
mListener.touch(mCurrentTouchLetter, false, currentPosition);
}
isUp = true;
// 重新繪制
invalidate();
break;
}
return true;
}
private LetterTouchListener mListener;
public void setOnLetterTouchListener(LetterTouchListener listener) {
this.mListener = listener;
}
// 接口回掉其他View會不會使用?
public interface LetterTouchListener {
void touch(CharSequence letter, boolean isTouch,int currentPosition);
}
我們ManActivity的布局文件是這樣的
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_letter"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/letter_tv"
android:layout_width="wrap_content"
android:layout_centerInParent="true"
android:text="A"
android:visibility="gone"
android:textSize="16sp"
android:textColor="#FF0000"
android:layout_height="wrap_content" />
<com.example.letterlist.LetterSideBar
android:id="@+id/letter_side_bar"
android:layout_width="wrap_content"
android:layout_alignParentRight="true"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_height="match_parent" />
</RelativeLayout>
</FrameLayout>
在ManActivity里面我們給RecyclerView添加數(shù)據(jù)珍剑,先給RecyclerView添加字母列表掸宛,在RecyclerView的布局文件在添加一個子RecyclerView。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_text"
android:textSize="15sp"
android:background="#9999"
android:textColor="#fff"
android:layout_width="match_parent"
android:layout_height="50dp" />
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_child"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
給子RecyclerView添加數(shù)據(jù)之后招拙,在會掉里面唧瘾,當右邊的列表那個被惦記了,就將父RecyclerView的那個對應(yīng)位置的條目滾動到頂部别凤,這樣效果就實現(xiàn)了
@Override
public void touch(CharSequence letter,boolean isTouch,int current) {
if(isTouch) {
mLetterTv.setVisibility(View.VISIBLE);
mLetterTv.setText(letter);
rvLetter.scrollToPosition(current);
//讓RecyclerView滾動到頂部
if (current != -1) {
rvLetter.scrollToPosition(current);
LinearLayoutManager mLayoutManager =
(LinearLayoutManager) rvLetter.getLayoutManager();
mLayoutManager.scrollToPositionWithOffset(current, 0);
}
}else{
mLetterTv.setVisibility(View.GONE);
}
}
項目已經(jīng)放到github上面:https://github.com/chenzhikaizg/LetterListView