联系人快速索引,可以根据右边索引导航来定位具体拼音首字第九个寡妇小说母的数据,下面是效果图:
实现主要使用三个View:
1、ListView:负责展示联系人数据
2、右边的索引IndexView(自定义View):负责处理用户点击或滑动事件,根据事件坐标值定位相应的字母索引,并通知索引事件监听者
3、正中间的当前索引拼音首字母提示View
IndexView实现原理:
1、根据控件宽度和高度计算出每个字母所占的区域大小
2、通过Paint.getTextBounds()计算出每个字母本身的宽高
3、根据计算出的控件宽高,计算出每个字母需要绘制的左下角坐标(用于文字绘制)
4、计算出每个字母有效的点击/触摸区域坐标(用Rect保存,后面可以通过Rect.contains()方法来判断某个坐标是否在该区域内)
5、被点击/触目到的字母区域用另外一个Paint来绘制通过不同画笔颜色来区分
6、通过回调接口通知事件监听者索引的变化
【注意】要注意获取控件宽高的时机,这里是在onMeasure()方法被调用时获取。
LitView的定位原理:
1、数据需根据拼音进行排序
2、当IndexView索引发生变化,需要在回调函数里将所有数据的首字母拼音与回调的拼音进行比较,从而获取到具体的数据索引
3、通过ListView.tSelection(int index)方法来定位到具体数据索引的位置
索引拼音首字母提示View:
1、当IndexView索引发生变化,在回调函数里设置当前字母数据并控制View的显示和隐藏
下面开始贴代码,首先是布局文件:
contacts.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/lv_contacts" android:layout_width="match_parent" android:layout_height="match_parent"/> <TextView android:id="@+id/tv_abc" android:layout_width="80dp" android:layout_height="80dp" android:layout_centerInParent="true" android:textSize="18sp" android:gravity="center" android:textColor="#FFFFFF" android:background="#88333333" android:visibility="gone"/> <com.log.anotherapp.customView.IndexView android:id="@+id/index_words" android:layout_width="50dp" android:layout_height="match_parent" android:background="#88ff0000" android:layout_alignParentRight="true"/></RelativeLayout>
contacts_item.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="match_parent" android:orientation="vertical"> <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="50dp" android:background="#C3BCBB" android:gravity="center_vertical" android:paddingLeft="20dp" android:textStyle="bold" android:textSize="18sp" /> <TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center_vertical" android:paddingLeft="20dp" android:textSize="16sp" /></LinearLayout>
IndexView.java
package com.log.anotherapp.customView;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.graphics.Typeface;import android.util.AttributeSet;import android.util.Pair;import android.view.MotionEvent;import android.view.View;import androidx.annotation.Nullable;import com.log.anotherapp.util.DensityUtil;public class IndexView extends View { private String[] words = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; // 保存需绘制的文字宽高 private Pair<Integer, Integer>[] wordSizes = new Pair[words.length]; // 每个文字的坐下角坐标 private Pair<Float, Float>[] wordCoordinates = new Pair[words.length]; // 每个文字的有效区域(用于判断是否点击或滑动该文字区域,做高亮显示) private Rect[] wordRects = new Rect[words.length]; // 常态文字的paint private Paint paint; // 高亮文字的paint private Paint lectedPaint; // 每一个字母的宽度和高度 private float itemWidth; private float itemHeight; // 当前字母索引 private int currentIndex = -1; // 绘制的文本字体大小 private int textSize; private OnIndexListener listener; // 是否已经计算过字体大小和坐标,防止重复计算 private boolean isNotComputed = true; public IndexView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 系统调用这个方法后,本控件才测量出宽和高 itemWidth = getMeasuredWidth(); itemHeight = getMeasuredHeight() / words.length; } /** * 设置字体大小 * * @param textSize */ public void tTextSize(int textSize) { this.textSize = textSize; } private void init() { // 默认字体大小 if (textSize <= 0) { textSize = DensityUtil.sp2px(getContext(), 18); } paint = new Paint(); paint.tAntiAlias(true); paint.tTextSize(textSize); paint.tTypeface(Typeface.DEFAULT_BOLD); // 粗体字 paint.tColor(Color.WHITE); lectedPaint = new Paint(); lectedPaint.tAntiAlias(true); lectedPaint.tTextSize(textSize); lectedPaint.tTypeface(Typeface.DEFAULT_BOLD); // 粗体字 lectedPaint.tColor(Color.GRAY); } private void compute() { Rect rect = new Rect(); for (int i = 0; i < words.length; i++) { // 计算文字宽高 paint.getTextBounds(words[i], 0, 1, rect); Pair pair = new Pair(rect.width(), rect.height()); wordSizes[i] = pair; // 计算每个字的左下角x和y坐标 float x = (itemWidth - wordSizes[i].first) / 2.f; float y = (itemHeight + wordSizes[i].cond) / 2.f + itemHeight * i; wordCoordinates[i] = new Pair<>(x, y); // 计算文字点击/触摸有效区域 wordRects[i] = new Rect(0, (int) (i * itemHeight), (int) itemWidth, (int) ((i + 1) * itemHeight)); } isNotComputed = fal; } @Override protected void onDraw(Canvas canvas) { if (isNotComputed) { compute(); } // 高亮文字的画笔 Paint currentPaint; for (int i = 0; i < words.length; i++) { if (i == currentIndex) { currentPaint = lectedPaint; } el { currentPaint = paint; } canvas.drawText(words[i], wordCoordinates[i].first, wordCoordinates[i].cond, currentPaint); } } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { ca MotionEvent.ACTION_DOWN: ca MotionEvent.ACTION_MOVE: for (int i = 0; i < wordRects.length; i++) { // 判断点击坐标落在哪个字母上 if (wordRects[i].contains((int) x, (int) y)) { // 当前选中索引 currentIndex = i; invalidate(); if (listener != null) { listener.onIndexChange(words[i]); } break; } } 食堂合同 break; ca MotionEvent.ACTION_UP: ca MotionEvent.ACTION_CANCEL: currentIndex = -1; invalidate(); if (listener != null) { listener.onIndexRelea(); } break; } return true; } public void tOnIndexListener(OnIndexListener listener) { this.listener = listener; } public interface OnIndexListener { void onIndexChange(String text); void onIndexRelea(); }}
PinYinUtil.java
package com.log.anotherapp.util;import net.sourceforge.pinyin4j.PinyinHelper;import net.sourceforge.pinyin4j.format.HanyuPinyinCaType;import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;public class PinYinUtil 地理图例{ public static String getPinYin(String chineWord) { String pinyin = ""; HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();//控制转换是否大小写,是否带音标 format.tCaType(HanyuPinyinCaType.UPPERCASE);//大写 format.tToneType(HanyuPinyinToneType.WITHOUT_TONE); //由于不能直接对多个汉字转换,只能对单个汉字转换 char[] arr = chineWord.toCharArray(); for (int i = 0; i < arr.length; i++) { if (Character.isWhitespace(arr[i])) continue;//如果是空格,则不处理,进行下次遍历 //汉字是2个字节存储,肯定大于127,所以大于127就可以当为汉字转换 if (arr[i] > 127) { try { //由于多音字的存在,单 dan shan String[] pinyinArr = PinyinHelper.toHanyuPinyinStringArray(arr[i], format); if (pinyinArr != null) { pinyin += pinyinArr[0]; } el { pinyin += arr[i]; } } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); //不是正确的汉字 pinyin += arr[i]; } } el { //不是汉字, pinyin += arr[i]; } } return pinyin; }}
ContactsActivity.java
package com.log.anotherapp;import android.os.Bundle;import android.os.Handler;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaAdapter;import android.widget.ListView;import android.widget.TextView;import androidx.annotation.Nullable;import com.log.anotherapp.customView.IndexView;import com.log.anotherapp.model.Contacts;import com.log.anotherapp.util.PinYinUtil;import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.List;public class ContactsActivity extends BaActivity implements IndexView.OnIndexListener { private ListView listView; private IndexView indexView; private TextView textView; private Handler handler; private List<Contacts> contacts; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); tContentView(R.layout.contacts); initView(); } private void initView() { handler = new Handler(); textView = findViewById(R.id.tv_abc); listView = findViewById(R.id.lv_contacts); indexView = findViewById(R.id.index_words); indexView.tOnIndexListener(this); initData(); ContactsAdapter adapter = new ContactsAdapter(); listView.tAdapter(adapter); } private void initData() { contacts = new ArrayList<>(); contacts.add(new Contacts("阿一", PinYinUtil.getPinYin("阿一"))); contacts.add(new Contacts("奥利奥", PinYinUtil.getPinYin("奥利奥"))); contacts.add(new Contacts("布尔", PinYinUtil.getPinYin("布尔"))); contacts.add((new Contacts("曹操", PinYinUtil.getPinYin("曹操")))); contacts.add((new Contacts("曹尼玛", PinYinUtil.getPinYin("曹尼玛")))); contacts.add((new Contacts("董卿", PinYinUtil.getPinYin("董卿")))); contacts.add((new Contacts("厄尼", PinYinUtil.getPinYin("厄尼")))); contacts.add((new Contacts("凡妮莎", PinYinUtil.getPinYin("凡妮莎")))); contacts.add((new Contacts("高手", PinYinUtil.getPinYin("高手")))); contacts.add((new Contacts("韩红", PinYinUtil.getPinYin("韩红")))); contacts.add((new Contacts("花果", PinYinUtil.getPinYin("花果")))); contacts.add((new Contacts("InDon", PinYinUtil.getPinYin("InDon")))); contacts.add((new Contacts("节操", PinYinUtil.getPinYin("节操")))); contacts.add((new Contacts("康嘉", PinYinUtil.getPinYin("康嘉")))); contacts.add((new Contacts("岚岚", PinYinUtil.getPinYin("岚岚")))); contacts.add((new Contacts("玛尼", PinYinUtil.getPinYin("玛尼")))); contacts.add((new Contacts("能升", PinYinUtil.getPinYin("能升")))); contacts.add((竞聘演讲new Contacts("欧洋", PinYinUtil.getPinYin("欧洋")))); contacts.add((new Contacts("盼盼", PinYinUtil.getPinYin("盼盼")))); contacts.add((new Contacts("钱途", PinYinUtil.getPinYin("钱途")))); contacts.add((new Contacts("让龙", PinYinUtil.getPinYin("让龙")))); contacts.add((new Contacts("诗人", PinYinUtil.getPinYin("诗人")))); contacts.add((new Contacts("唐僧", PinYinUtil.getPinYin("唐僧")))); contacts.add((new Contacts("ULove", PinYinUtil.getPinYin("ULove")))); contacts.add((new Contacts("VLog", PinYinUtil.getPinYin("VLog")))); contacts.add((new Contacts("王帝", PinYinUtil.getPinYin("王帝")))); contacts.add((new Contacts("现金", PinYinUtil.getPinYin("现金")))); contacts.add((new Contacts("媛媛", PinYinUtil.getPinYin("媛媛")))); contacts.add((new Contacts("正宗", PinYinUtil.getPinYin("正宗")))); // 按照拼音排序 Collections.sort(contacts, new Comparator<Contacts>() { @Override public int compare(Contacts o1, Contacts o2) { return o1.getPinyin().compareTo(o2.getPinyin()); } }); } @Override public void onIndexChange(String text) { textView.tText(text); textView.tVisibility(View.VISIBLE); // 查找指定的拼音首字母在联系人数组的哪个索引 // 由于数组已经排序,只要找到第一个匹配的就可以了 int index = -1; for (int i = 0; i < contacts.size(); i++) { if (contacts.get(i).getPinyin().substring(0,1).equalsIgnoreCa(text)) { index = i; break; } } // 跳转到ListView指定位置 if (index >= 0) { listView.tSelection(index); } } @Override public void onIndexRelea() { // 延迟一秒再隐藏 handler.postDelayed(new Runnable() { @Override public void run() { textView.tVisibility(View.GONE); } }, 1000); } class ContactsAdapter extends BaAdapter { @Override public int getCount() { return contacts == null ? 0 : contacts.size(); } @Override public Object getItem(int position) { return contacts == null ? null : contacts.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(getContext()).inflate(R.layout.contacts_item, null); holder.title = convertView.findViewById(R.id.tv_title); holder.content = convertView.findViewById(R.id.tv_content); convertView.tTag(holder); } el { holder = (ViewHolder) convertView.getTag(); } Contacts data = contacts.get(position); if (position == 0) { holder.title.tText(data.getPinyin().substring(0, 1)); holder.title.tVisibility(View.VISIBLE); } el { // 比较上一个是否与当前的拼音首字母是否一样 if (data.getPinyin().charAt(0) == contacts.get(position - 1).getPinyin().charAt(0)) { holder.title.tVisibility(View.GONE); } e国家指导思想l { holder.title.tText(data.getPinyin().substring(0, 1)); holder.title.tVisibility(View.VISIBLE); } } holder.content.tText(data.getName()); return convertView; } class ViewHolder { TextView title; TextView content; } }}
因为用到了Pinyin4j,需要在build.gradle里添加依赖:
// https://mvnrepository.com/artifact/org.clojars.cbilson/pinyin4j implementation group: 'org.clojars.cbilson', name: 'pinyin4j', version: '2.5.0'
本文地址:https://blog.csdn.net/lognic10/article/details/108979041
本文发布于:2023-04-04 05:37:46,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/0d72196b94db0eb67cd27454264e5285.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:Android造轮子—联系人快速索引.doc
本文 PDF 下载地址:Android造轮子—联系人快速索引.pdf
留言与评论(共有 0 条评论) |