先看下效果图
Gif_20180425_103334.gif
在这里我们先分析下效果,左边是一个按照字母排序的并有子列表,右边是直接一个字母的排列,屏幕中间有一个显示你点击了右边的那个字母的显示。
在这里我们先去处理右边的字母列表。我们先去自定义一个view,把26个字母和“#”放到一个数组里面,再用画笔在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方向的距离去除以每个字母的高度,然后取整就知道那个字母被点击了,这时候调用invalidate方法去重新绘制字母列表,这样就可以实现当那个字母被点击或者是滑动时被覆盖的字母变色的效果。然后在这里我们写一个回调,去让页面中间的那个textview知道如何去显示那个字母被点击了。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
// 计算出当前触摸字母 获取当前的位置
float currentMoveY = event.getY();
// 位置 = currentMoveY / 字母高度 , 通过位置获取字母 优化?
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添加数据,先给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添加数据之后,在会掉里面,当右边的列表那个被惦记了,就将父RecyclerView的那个对应位置的条目滚动到顶部,这样效果就实现了
@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);
}
}
项目已经放到github上面:https://github.com/chenzhikaizg/LetterListView
网友评论