[Android] 仿通讯录拼音索引与跳转以及Pinyin4j的使用(下)
如果你还没看上一篇博客,强烈建议你先看上一篇博客
传送门:[Android] 仿通讯录拼音索引与跳转以及Pinyin4j的使用(上)
在上次,我们已经完成了recyclerView的创建以及数据填充。并且为tag设置上了背景颜色。这次我们继续完成索引的创建以及滑动跳转的功能。
分析实现方法
- 创建一个LinearLayout,用来存放tag。也为触摸事件设置监听器。
- 维护一个List,用来去寻找tag的位置,为后续跳转做准备。
- 在获得焦点的时候,实时计算tag的高度,通过触摸点的Y轴坐标,计算出当前触摸点在tag列表中的位置。
- 通过RecyclerView获取到
LinearLayoutManager
的scrollToPositionWithOffset
方法,跳转到指定位置。
具体实现
一样的,有了实现思路,便不难实现效果。
1. 创建LinearLayout
这一步没什么特殊的,只要在xml中创建一个LinearLayout,并设置其属性即可。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/recyclerView"/>
<LinearLayout
android:orientation="vertical"
android:id="@+id/tagLayout"
android:layout_width="24sp"
android:layout_height="match_parent"
android:background="#E4E4E4"
android:layout_alignParentEnd="true"
android:layout_marginEnd="12sp"
android:layout_marginVertical="24sp"/>
</RelativeLayout>
当然啦,也要为tag创建一个xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0sp"
android:layout_weight="1"
android:orientation="vertical"
>
<TextView
android:layout_gravity="center"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="0sp"
android:layout_weight="1"
android:text="A"
android:id="@+id/tag_tv"/>
</LinearLayout>
2. 创建一个特殊的Adapter,用来维护列表
表面上说是Adapter,实际上就是一个简单的类,来通过数据读取出列表来维护,并且通过这个列表将数据放在LinearLayout中。
package com.example.pinyinindex;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class TagAdapter {
public List<String> tagList = new ArrayList<>();
public List<Integer> indexList = new ArrayList<>();
private final ViewGroup listView;
public TagAdapter(List<Item> data, ViewGroup listView) {
this.listView = listView;
for (int i = 0; i < data.size(); i++) {
if (data.get(i).getName() == null) {
// 此时为tag,需要添加tag
tagList.add(data.get(i).getTag());
indexList.add(i);
}
}
onBold();
}
private void onBold() {
for (String str : tagList) {
View v = View.inflate(listView.getContext(), R.layout.item_base_tag, null);
// 设置view的weight=1
ViewGroup.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT,1f);
v.setLayoutParams(lp);
TextView tagTv = v.findViewById(R.id.tag_tv);
tagTv.setText(str);
listView.addView(v);
}
}
}
注意: 在这里多维护了一个TagList,如果有更深一步的需求可以通过这个List来获取tag的名字。
这个构造函数接收的是上个博客中,传递给Adapter的数据,通过遍历这个数据,将tag添加到tagList、indexList中。
3. 为tagLayout设置触摸事件
重点来了。因为在这一步我们需要实时计算tag的高度,通过触摸点的Y轴坐标,计算出当前触摸点在tag列表中的位置。我们可以将高度保存成一个全局变量,这样子在第二次触摸的时候,就不需要重新计算了。
但是! 如果直接调用View.getHeight()
,就会发现获取到的是0。因为生命周期的问题,这个view还没有被绘制出来,此时并不能获取到它的高度。因此,我们需要再窗口获得焦点的时候(此时view已经全部绘制完毕)再获取他的高度。
具体来说,就是在onWindowFocusChanged
中调用getHeight()
。并且为tagLayout设置一个触摸事件。
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (!isLoaded) {
// isLoaded用来判断是否已经加载过了
if (!itemList.isEmpty()) {
tagAdapter = new TagAdapter(itemList, tagLayout);
// 可以在这里计算控件的高度,减少计算。但我偷懒了。。。
setOnTouchListener(tagAdapter);
isLoaded = true;
}
}
}
@SuppressLint("ClickableViewAccessibility")
private void setOnTouchListener(TagAdapter adapter) {
List<Integer> indexList = adapter.indexList;
tagLayout.setOnTouchListener((v, e) -> {
// 获取高度
int height = v.getHeight();
// 获取顶部y坐标
int top = v.getTop();
// 获取触摸点y坐标(屏幕坐标)
int y = (int) e.getRawY();
// 列表大小
int size = indexList.size();
// 计算当前触摸的是第几个
int index = (y - top) / (height / size);
if (index >= 0 && index < size) {
// 执行跳转方法
scrollToPosition(indexList.get(index));
}
return true;
});
}
private void scrollToPosition(int position) {
if (position < 0 || position >= itemList.size()) {
return;
}
RecyclerView.LayoutManager layoutManager = rv.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(position, 0);
}
}
总结:
MainActivity.java
package com.example.pinyinindex;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.example.pinyinindex.util.PinyinUtil;
import java.util.ArrayList;
import java.util.List;
import kotlin.collections.ArrayDeque;
public class MainActivity extends Activity {
private RecyclerView rv;
private LinearLayout tagLayout;
private final List<Item> itemList = new ArrayList<>();
private TagAdapter tagAdapter;
private boolean isLoaded = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViews();
initData();
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(new ItemAdapter(itemList));
}
private void initData() {
List<Item> data = DataSource.getData();
final char[] lastTag = {' '};
data.stream()
.sorted()
.forEach(item -> {
char itemLetter = PinyinUtil.getFirstLetter(item.getName());
if (itemLetter != lastTag[0]) {
lastTag[0] = itemLetter;
itemList.add(new Item(null, String.valueOf(itemLetter)));
}
itemList.add(item);
});
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (!isLoaded) {
if (!itemList.isEmpty()) {
tagAdapter = new TagAdapter(itemList, tagLayout);
setOnTouchListener(tagAdapter);
isLoaded = true;
}
}
}
@SuppressLint("ClickableViewAccessibility")
private void setOnTouchListener(TagAdapter adapter) {
List<Integer> indexList = adapter.indexList;
tagLayout.setOnTouchListener((v, e) -> {
// 获取高度
int height = v.getHeight();
// 获取顶部y坐标
int top = v.getTop();
// 获取触摸点y坐标(屏幕坐标)
int y = (int) e.getRawY();
// 列表大小
int size = indexList.size();
// 计算当前触摸的是第几个
int index = (y - top) / (height / size);
if (index >= 0 && index < size) {
// 执行跳转方法
scrollToPosition(indexList.get(index));
}
return true;
});
}
private void scrollToPosition(int position) {
if (position < 0 || position >= itemList.size()) {
return;
}
RecyclerView.LayoutManager layoutManager = rv.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(position, 0);
}
}
private void findViews() {
rv = findViewById(R.id.recyclerView);
tagLayout = findViewById(R.id.tagLayout);
}
}
代码都已经发过了,这里就不再贴上去了。
效果如下:
如果对你有帮助,希望能给我点一个免费的赞,您的每一个点赞是我最大的动力。