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

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

如果你还没看上一篇博客,强烈建议你先看上一篇博客

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

在上次,我们已经完成了recyclerView的创建以及数据填充。并且为tag设置上了背景颜色。这次我们继续完成索引的创建以及滑动跳转的功能。

分析实现方法

  1. 创建一个LinearLayout,用来存放tag。也为触摸事件设置监听器。
  2. 维护一个List,用来去寻找tag的位置,为后续跳转做准备。
  3. 在获得焦点的时候,实时计算tag的高度,通过触摸点的Y轴坐标,计算出当前触摸点在tag列表中的位置。
  4. 通过RecyclerView获取到LinearLayoutManagerscrollToPositionWithOffset方法,跳转到指定位置。

具体实现

一样的,有了实现思路,便不难实现效果。

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

代码都已经发过了,这里就不再贴上去了。
效果如下:
在这里插入图片描述

如果对你有帮助,希望能给我点一个免费的赞,您的每一个点赞是我最大的动力。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

飛_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值