一:简言
一个月没有写博客了,公司项目比较忙,最近发现公司用到一个知识点,所以抽时间通过博客的形式分享给大家,该知识点,模仿微信的好友列表,通过字母索引进行定位,该功能主要通过几个知识点实现。下面会一一讲解。
二:具体说下思路
- 1 :整体用到的控件,list view(recyclerView原理一样),自定义view,汉字转拼音的utils工具类.
- 2:创建一个自定义类继承view,实现侧面滑动字母显示字母索引数据,说下自定义View,首先自定义类继承View重写三个构造方法,重写
- onMeasure()的方法用于测量View的宽高,onSizeChanged()方法改变当前控件大小的时候调用,onDarw()方法绘制View,
- onTouchEvent()方法用于手势监听,自定义接口用于保存点击了那个字母的值。
- 3:创建一个person的实体bean,实现列表数据的展示,
- 4。通过将汉字通过拼音的形式进行分类,添加一个pinyinj-2.5.0.jar,实现转换,(稍后代码会详细介绍)。
- 5:将数据显示出来,实现效果;
三:实现效果
- 四:通过代码的形式介绍步骤:
1)首先实现实体bean(Person)
public class Person {
private String name;
private String pinyin;
public Person(String name){
this.name = name;
this.pinyin = PinYinUtils.getPinYin(name);
}
public String getPinyin() {
return pinyin;
}
public void setPinyin(String pinyin) {
this.pinyin = pinyin;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", pinyin='" + pinyin + '\'' +
'}';
}
}
该类没有讲解的必要,直接刀币下一个类:
2)拼音转换工具类(PinYinUtils)
首先添加这个jar包(稍后分享)
然后创建该类(代码如下)
public class PinYinUtils {
/**
* 得到指定汉字的拼音
* 注意:不应该被频繁调用,它消耗一定内存
* @param hanzi
* @return
*/
public static String getPinYin(String hanzi){
String pinyin = "";
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();//控制转换是否大小写,是否带音标
format.setCaseType(HanyuPinyinCaseType.UPPERCASE);//大写
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
//由于不能直接对多个汉字转换,只能对单个汉字转换
char[] arr = hanzi.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];
}else {
pinyin += arr[i];
}
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
//不是正确的汉字
pinyin += arr[i];
}
}else {
//不是汉字,
pinyin += arr[i];
}
}
return pinyin;
}
}
3)重点来了,主要实现的功能,自定义view(IndexView)
/**
* Created by wk先森
* 快速索引
* 绘制快速索引的字母
* 1.二十六个字母 放入集合中
* 2.在onMeaus中计算每条的宽和高 itemHeight itemWidth wordHeight wordX wordY
* <p>
* 手指按下 文字变色
* 重写onTouchEvent方法 返回true
* 在Down和move中 计算
* int touchIndex = Y/itemHeight 强制绘制
* <p>
* 2.在onDraw方法对于该下包画笔变色
* <p>
* 3。在Up的时候 touchIndex = -1;
* 强制绘制
*/
public class IndexView extends View {
/**
* 每条的宽和高
*/
private int itemWidth;
private int itemHeight;
Paint paint;
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 int touchIndex = -1;
public IndexView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView(context);
}
private void initView(Context context) {
paint = new Paint();
paint.setColor(Color.BLACK);//设置颜色
paint.setAntiAlias(true);
paint.setTypeface(Typeface.DEFAULT_BOLD);
}
/**
* 测量方法
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
itemWidth = getMeasuredWidth();
itemHeight = getMeasuredHeight() / words.length;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < words.length; i++) {
if(touchIndex == i){
//设置灰色
paint.setColor(Color.GRAY);
}else{
//设置白色
paint.setColor(Color.BLACK);
}
String word = words[i];
Rect rect = new Rect();
//0,1 取一个字母
paint.getTextBounds(word, 0, 1, rect);
int wordWidth = rect.width();
int wordHeight = rect.height();
//计算每个字母在视图上的坐标
float wordX = itemWidth / 2 - wordWidth / 2;
float wordY = itemHeight / 2 + wordHeight / 2 + i * itemHeight;
canvas.drawText(word, wordX, wordY, paint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
float Y= event.getY();
int index = (int) (Y/itemHeight);//字母的索引
if(index != touchIndex){
touchIndex = index;//强制绘制 onDraw();
invalidate();
if(onIndexChangeListener != null && touchIndex<words.length){
onIndexChangeListener.OnIndexChange(words[touchIndex]);
}
}
break;
case MotionEvent.ACTION_UP :
touchIndex = -1;
invalidate();
break;
}
return true;
}
/**
* 字母下标索引变化监听器
* */
public interface OnIndexChangeListener{
/**
* 挡字幕下标位置发生变化时候回调
* */
void OnIndexChange(String word);
}
private OnIndexChangeListener onIndexChangeListener;
public void setOnIndexChangeListener(OnIndexChangeListener onIndexChangeListener) {
this.onIndexChangeListener = onIndexChangeListener;
}
}
5)通过activity实现该功能(MainActivity)
该类需要关注的点:
Toast显示的时间
public class MainActivity extends AppCompatActivity {
private ListView lvMain;
private TextView tvWord;
private IndexView ivWords;
private Handler handler = new Handler();
/**
* 联系人的集合
*/
private ArrayList<Person> persons;
private IndexAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
lvMain = findViewById(R.id.lv_main);
tvWord = findViewById(R.id.tv_word);
ivWords = findViewById(R.id.iv_words);
//设置监听字母下标索引变化
ivWords.setOnIndexChangeListener(new IndexView.OnIndexChangeListener() {
/**
* 回传是字母
* */
@Override
public void OnIndexChange(String word) {
updateWord(word);
updateListView(word);//A~Z
}
});
initData();
adapter = new IndexAdapter();
lvMain.setAdapter(adapter);
}
private void updateListView(String word) {
for(int i=0;i<persons.size();i++){
String listWord = persons.get(i).getPinyin().substring(0,1);//YANGGUANGFU-->Y
if (word.equals(listWord)) {
//i是listView中的位置
lvMain.setSelection(i);//定位到ListVeiw中的某个位置
return;
}
}
}
private void updateWord(String word){
//显示
tvWord.setVisibility(View.VISIBLE);
tvWord.setText(word);
handler.removeCallbacksAndMessages(null);
handler.postDelayed(new Runnable() {
@Override
public void run() {
//也是运行在主线程中
tvWord.setVisibility(View.GONE);
}
},500);
}
/**
* 初始化数据
*/
private void initData() {
persons = new ArrayList<>();
persons.add(new Person("张晓飞"));
persons.add(new Person("杨光福"));
persons.add(new Person("胡继群"));
persons.add(new Person("刘畅"));
persons.add(new Person("钟泽兴"));
persons.add(new Person("尹革新"));
persons.add(new Person("安传鑫"));
persons.add(new Person("张骞壬"));
persons.add(new Person("温松"));
persons.add(new Person("李凤秋"));
persons.add(new Person("刘甫"));
persons.add(new Person("娄全超"));
persons.add(new Person("张猛"));
persons.add(new Person("王英杰"));
persons.add(new Person("李振南"));
persons.add(new Person("孙仁政"));
persons.add(new Person("唐春雷"));
persons.add(new Person("牛鹏伟"));
persons.add(new Person("姜宇航"));
persons.add(new Person("刘挺"));
persons.add(new Person("张洪瑞"));
persons.add(new Person("张建忠"));
persons.add(new Person("侯亚帅"));
persons.add(new Person("刘帅"));
persons.add(new Person("乔竞飞"));
persons.add(new Person("徐雨健"));
persons.add(new Person("吴亮"));
persons.add(new Person("王兆霖"));
persons.add(new Person("阿三"));
persons.add(new Person("李博俊"));
//排序
Collections.sort(persons, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getPinyin().compareTo(o2.getPinyin());
}
});
}
class IndexAdapter extends BaseAdapter{
@Override
public int getCount() {
return persons.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if(convertView == null){
convertView = View.inflate(MainActivity.this,R.layout.item_main,null);
viewHolder = new ViewHolder();
viewHolder.tv_word = convertView.findViewById(R.id.tv_word);
viewHolder.tv_name = convertView.findViewById(R.id.tv_name);
convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHolder) convertView.getTag();
}
String name = persons.get(position).getName();//阿福
String word = persons.get(position).getPinyin().substring(0,1);//AFU->A
viewHolder.tv_word.setText(word);
viewHolder.tv_name.setText(name);
if(position ==0){
viewHolder.tv_word.setVisibility(View.VISIBLE);
}else{
//得到前一个位置对应的字母,如果当前的字母和上一个相同,隐藏;否则就显示
String preWord = persons.get(position-1).getPinyin().substring(0,1);//A~Z
if(word.equals(preWord)){
viewHolder.tv_word.setVisibility(View.GONE);
}else{
viewHolder.tv_word.setVisibility(View.VISIBLE);
}
}
return convertView;
}
}
static class ViewHolder{
TextView tv_word;
TextView tv_name;
}
}
到这里功能可以说已经介绍完了。把剩下的xml文件赠送给大家
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.zzsy.quickindex.MainActivity">
<ListView
android:id="@+id/lv_main"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/tv_word"
android:visibility="gone"
android:layout_centerInParent="true"
android:layout_width="80dp"
android:layout_height="80dp"
android:gravity="center"
android:background="#44000000"
android:textColor="#000000"
android:text="A"
android:textSize="30sp"
android:textStyle="bold"
/>
<com.zzsy.quickindex.IndexView
android:id="@+id/iv_words"
android:layout_width="35dp"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:background="#10000000" />
</RelativeLayout>
<?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="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_word"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#22000000"
android:text="A"
android:paddingLeft="5dp"
android:textColor="#000000"
android:textSize="25sp" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="阿福"
android:padding="5dp"
android:textColor="#dd000000"
android:textSize="22sp" />
</LinearLayout>
上面的xml文件分别是main,item_main
五。jar包地址: