[Android] 仿通讯录拼音索引与跳转以及Pinyin4j的使用(上)

[Android] 仿通讯录拼音索引与跳转以及Pinyin4j的使用(上)

提要:上周实现了一个通讯录的列表。大致要实现将列表中的数据按照拼音进行排序,并且实现拼音索引与跳转的一个功能

因为太长了,因此分为上下两篇。随缘加内容,没准会更多

分析一下实现思路:

  1. 首先引入pinyin工具类。时期能够获取列表第一个字符拼音的首字母。
  2. 实体类继承comparable接口,重写compareTo方法,实现拼音排序
  3. 列表的adapter中,在getView方法中,根据拼音首字母,设置背景颜色
  4. 在右边添加一个索引,能够实现滑动跳转、出现拼音气泡

既然如此,思路就可以打开了:

1.引入Pinyin工具类

这里使用的是Pinyin4j。直接在gradle中引入即可。

dependencies {
    
    implementation 'com.belerweb:pinyin4j:2.5.0'
    ...
}

点击sync,下载相关依赖。静静等待依赖下载完成。

使用Pinyin4j写相关util

在这里本来有更多功能的。但在这个例子里,我们只用找出拼音的首字母。不用其他的功能了。
因此只需要使用PinyinHelper.toHanyuPinyinStringArray()即可。

package com.example.pinyinindex.util;

import net.sourceforge.pinyin4j.PinyinHelper;

public class PinyinUtil {

    // 获取字符串第一个字符的拼音
    public static String getPinyin(String str) {
        String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(str.charAt(0));
        return pinyinArray[0];
    }

    // 获取字符串第一个字符的拼音首字母
    public static char getFirstLetter(String str) {
        String result = getPinyin(str);
        return result.charAt(0);
    }
}

冷知识

写到这里突然想说一嘴:其实Pinyin4j,读作:Pinyin for j, 也就是 拼音 for java
像这种还有2 读作 to。在看到这种依赖的时候,基本就能知道这个是为了什么场景使用的了。

如何使用pinyin4j?

在这个例子里,我们只需要使用PinyinHelper.toHanyuPinyinStringArray()。这个方法会返回一个数组,是这个汉字的所有读音(毕竟中文有多音字)。比如说“哦”,会返回[o,e]两个音。这个的具体使用就在过多介绍了。详细可以看Pinyin4j官网

2.编写实体类,实现相关接口

因为想要用更优雅的方式写比较排序方法 (主要是因为懒,不想写),在这里直接让相关实体类实现一下Comparable接口。这样子可以直接使用sort()方法进行排序。

package com.example.pinyinindex;

import com.example.pinyinindex.util.PinyinUtil;

public class Item implements Comparable<Item>{
    private String name;
    // 如果不知道tag是做什么的,可以忽略,后面会讲
    private String tag;

    public Item(String name, String tag) {
        this.name = name;
        this.tag = tag;
    }

    public String getName() {
        return name;
    }

    public String getTag() {
        return tag;
    }


    // 实现Comparable接口需要重写一下这个方法。
    @Override
    public int compareTo(Item item) {
        char otherLetter = PinyinUtil.getFirstLetter(item.name);
        char myLetter = PinyinUtil.getFirstLetter(this.name);

        return myLetter - otherLetter;
    }
}

3.进行排序

反正都已经实现Comparable接口了,直接使用sort()方法进行排序即可。但是,这里还有一个小问题。我想要在列表中,设置背景颜色。如果直接排序的话,列表怎么知道什么时候要设置背景呢?所以在这里还需要添加一个tag。

这个tag,就是用来记录当前这个item的拼音首字母。如果一个item没有名字,只有tag,则证明这个就是我们需要设置背景的元素啦。

创建对应数据

// 这个就是要传递给adapter的数据
private List<Item> itemList = new ArrayList<>();
private void initData() {
    // DataSource用来模拟数据
    List<Item> data = DataSource.getData();
    // 上一个item的拼音首字母
    final char[] lastTag = {' '};
    data.stream()
            .sorted()
            .forEach(item -> {
                char itemLetter = PinyinUtil.getFirstLetter(item.getName());
                // 如果当前item的拼音首字母和上一个的不同,则证明需要添加一个tag
                if (itemLetter != lastTag[0]) {
                    lastTag[0] = itemLetter;
                    itemList.add(new Item(null, String.valueOf(itemLetter)));
                }
                itemList.add(item);
            });
}

这里用到了stream流,和forEach。关于这点已经写过博客了,详情可以去看看Java8新特性之Stream流

对不清楚的小伙伴说一嘴:在这里我们对数据进行了排序,并且添加了tag。最后形成了一个数据数组,最后把这个数据数组传递给adapter就可以了

4.编写Adapter,实现关于背景颜色的设置

因为是用的RecyclerView,所以这里直接使用RecyclerView.Adapter。

package com.example.pinyinindex;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.myViewHolder> {
    private final List<Item> itemList;
    // 构造函数,获取数据
    public ItemAdapter(List<Item> itemList){
        this.itemList = itemList;
    }

    // 绑定布局
    @NonNull
    @Override
    public myViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_item_tag,parent,false);
        return new myViewHolder(v);
    }

    // 绑定数据
    @Override
    public void onBindViewHolder(@NonNull myViewHolder holder, int position) {
        // 进行初始化,防止在快速上下滚动的时候显示错乱
        holder.mainLayout.setVisibility(View.GONE);
        holder.tagTv.setVisibility(View.GONE);

        Item item = itemList.get(position);
        // 如果没有名字则证明是tag
        if (item.getName() == null) {
            holder.tagTv.setVisibility(View.VISIBLE);
            holder.tagTv.setText(item.getTag());
            return;
        }
        holder.mainLayout.setVisibility(View.VISIBLE);
        holder.nameTv.setText(item.getName());
    }

    @Override
    public int getItemCount() {
        return itemList.size();
    }

    // 创建ViewHolder
    class myViewHolder extends RecyclerView.ViewHolder{
        public TextView tagTv;
        public TextView nameTv;
        public LinearLayout mainLayout;

        public myViewHolder(View itemView){
            super(itemView);
            tagTv = itemView.findViewById(R.id.adapter_tag);
            nameTv = itemView.findViewById(R.id.adapter_name);
            mainLayout = itemView.findViewById(R.id.adapter_main);
        }
    }
}

这里偷懒,直接在布局文件中写了两个TextView,一个用来显示tag,一个用来显示名字。按照需要显示隐藏。

看一眼布局文件吧:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="A"
        android:id="@+id/adapter_tag"
        android:background="#999"
        android:textColor="@color/white"
        android:paddingLeft="10dp"/>
    <LinearLayout
        android:id="@+id/adapter_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="姓名"
            android:id="@+id/adapter_name"
            android:textSize="32sp"/>
    </LinearLayout>

</LinearLayout>

在这里插入图片描述

注意:在这个Adapter里,刚绑定数据的时候,会进行初始化,也就是让tag和name都隐藏,防止在快速上下滚动的时候显示错乱。如果不这么做,在快速上下滚动的时候,可能会出现tag和name同时显示的情况。

5.绑定数据

package com.example.pinyinindex;

import android.app.Activity;
import android.os.Bundle;

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 final List<Item> itemList = new ArrayList<>();

    @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);
                });
    }

    private void findViews() {
        rv = findViewById(R.id.recyclerView);
    }
}

6.全部代码&效果

DataSource.java

package com.example.pinyinindex;

import java.util.ArrayList;
import java.util.List;

public class DataSource {
    public static List<Item> getData() {
        ArrayList<Item> result = new ArrayList<>();
        result.add(new Item("李白", null));
        result.add(new Item("杜甫", null));
        result.add(new Item("李清照", null));
        result.add(new Item("王维", null));
        result.add(new Item("李商隐", null));
        result.add(new Item("张三", null));
        result.add(new Item("李四", null));
        result.add(new Item("王五", null));
        result.add(new Item("赵六", null));
        result.add(new Item("田七", null));
        result.add(new Item("张三丰", null));
        result.add(new Item("张无忌", null));
        result.add(new Item("马冬梅", null));
        result.add(new Item("张飞", null));
        result.add(new Item("李元芳", null));
        result.add(new Item("诸葛亮", null));
        result.add(new Item("姜子牙", null));
        result.add(new Item("岳云鹏", null));
        result.add(new Item("斐擒虎", null));
        result.add(new Item("孙悟空", null));
        result.add(new Item("猪八戒", null));
        result.add(new Item("沙和尚", null));
        result.add(new Item("白骨精", null));
        result.add(new Item("白龙马", null));
        result.add(new Item("唐僧", null));
        result.add(new Item("姬小满", null));
        result.add(new Item("典韦", null));
        result.add(new Item("曹操", null));
        result.add(new Item("刘备", null));
        result.add(new Item("关羽", null));
        result.add(new Item("张飞", null));
        result.add(new Item("赵云", null));
        result.add(new Item("黄忠", null));
        result.add(new Item("孙权", null));
        result.add(new Item("孙尚香", null));
        result.add(new Item("孙策", null));
        result.add(new Item("孙凯", null));
        result.add(new Item("黄月英", null));
        result.add(new Item("李典", null));
        result.add(new Item("孙坚", null));
        result.add(new Item("马超", null));
        result.add(new Item("张辽", null));
        result.add(new Item("司马懿", null));
        result.add(new Item("夏侯惇", null));
        result.add(new Item("许褚", null));
        result.add(new Item("郭嘉", null));
        result.add(new Item("吕蒙", null));
        result.add(new Item("黄盖", null));
        result.add(new Item("华佗", null));
        result.add(new Item("华雄", null));
        result.add(new Item("袁术", null));
        result.add(new Item("魏延", null));
        result.add(new Item("于吉", null));
        result.add(new Item("周泰", null));
        result.add(new Item("张角", null));
        result.add(new Item("曹丕", null));
        result.add(new Item("曹真", null));
        result.add(new Item("曹爽", null));

        return result;
    }

}

其他代码都在上面啦,就不贴了。
在这里插入图片描述

总结

实现这个效果实际上很简单,不用怎么复杂,只要按照我上面说的步骤,就可以实现这个效果。其实不用我说应该也能写出来

下一篇博客会介绍如何实现通讯录的拼音索引与跳转。应该会在不久写出来。如果对你有帮助,希望能给我点一个免费的赞,您的每一个点赞是我最大的动力。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飛_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值