如下是快速索引的效果图,是从网上下的实例。如图实现的难点1:是最右侧的索引是用自定义View来实现的,主要通过onDraw的方法将其画出;
难点2:是如何拿到每个名字的首字母用的是pinyin4j-2.5.0.jar 将汉字转化成拼音再去第一个字符;难点3:ListView的adapte不好实现
下图的布局是一个ListView右侧是一个自定义的View,中间是一个TextView点击的时候显示
如下是自定义的View来实现快速索引
1 package com.demo.sb.widget; 2 3 import android.content.Context; 4 import android.graphics.Canvas; 5 import android.graphics.Color; 6 import android.graphics.Paint; 7 import android.graphics.Rect; 8 import android.graphics.Typeface; 9 import android.util.AttributeSet; 10 import android.util.Log; 11 import android.view.MotionEvent; 12 import android.view.View; 13 14 /** 15 * 快速索引 用于根据字母快速定位联系人 也就是界面最右边的那个红色竖条 16 * 17 * @author Administrator 18 * 19 */ 20 public class QuickIndexBar extends View { 21 22 private static final String[] LETTERS = new String[] { "A", "B", "C", "D", 23 "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", 24 "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }; 25 26 private Paint mPaint; 27 28 private float cellHeight; 29 30 private int cellWidth; 31 32 /** 33 * 暴露一个字母的监听 34 */ 35 public interface OnLetterUpdateListener { 36 void onLetterUpdate(String letter); 37 } 38 39 private OnLetterUpdateListener listener; 40 41 public OnLetterUpdateListener getListener() { 42 return listener; 43 } 44 45 /** 46 * 设置字母更新的监听 47 */ 48 public void setListener(OnLetterUpdateListener listener) { 49 this.listener = listener; 50 } 51 52 public QuickIndexBar(Context context) { 53 this(context, null); 54 // TODO Auto-generated constructor stub 55 } 56 57 public QuickIndexBar(Context context, AttributeSet attrs) { 58 this(context, attrs, 0); 59 // TODO Auto-generated constructor stub 60 } 61 62 public QuickIndexBar(Context context, AttributeSet attrs, int defStyleAttr) { 63 super(context, attrs, defStyleAttr); 64 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 65 mPaint.setColor(Color.WHITE); 66 mPaint.setTypeface(Typeface.DEFAULT_BOLD); 67 } 68 69 @Override 70 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 71 // TODO Auto-generated method stub 72 super.onSizeChanged(w, h, oldw, oldh); 73 cellWidth = getMeasuredWidth(); 74 75 int mHeight = getMeasuredHeight(); 76 cellHeight = mHeight * 1.0f / LETTERS.length; 77 } 78 79 /** 80 * 绘制自定义最重要的一步重写onDraw方法 画什么,由Canvas处理 怎么画,由Paint处理 81 */ 82 @Override 83 protected void onDraw(Canvas canvas) { 84 // TODO Auto-generated method stub 85 for (int i = 0; i < LETTERS.length; i++) { 86 String text = LETTERS[i]; 87 // 计算坐标 88 int x = (int) (cellWidth / 2.0f - mPaint.measureText(text) / 2.0f); 89 // 获取文本的高度 90 Rect bounds = new Rect();// 矩形 91 mPaint.getTextBounds(text, 0, text.length(), bounds); 92 int textHeight = bounds.height(); 93 int y = (int) (cellHeight / 2.0f + textHeight / 2.0f + i 94 * cellHeight); 95 96 // 根据按下的字母,设置画笔颜色 97 mPaint.setColor(touchIndex == i ? Color.GRAY : Color.WHITE); 98 99 // 绘制文本A-Z 100 canvas.drawText(text, x, y, mPaint); 101 } 102 } 103 104 int touchIndex = -1; 105 106 @Override 107 public boolean onTouchEvent(MotionEvent event) { 108 // TODO Auto-generated method stub 109 int index = -1; 110 switch (event.getAction()) { 111 case MotionEvent.ACTION_DOWN: 112 // 获取当前触摸到的字母索引 113 index = (int) (event.getY() / cellHeight); 114 if (index >= 0 && index < LETTERS.length) { 115 // 判断是否跟上一次触摸到的一样 116 if (index != touchIndex) { 117 118 if (listener != null) { 119 listener.onLetterUpdate(LETTERS[index]); 120 } 121 122 Log.d("jiejie", "onTouchEvent: " + LETTERS[index]); 123 124 touchIndex = index; 125 } 126 } 127 break; 128 case MotionEvent.ACTION_MOVE: 129 130 index = (int) (event.getY() / cellHeight); 131 if (index >= 0 && index < LETTERS.length) { 132 // 判断是否跟上一次触摸到的一样 133 if (index != touchIndex) { 134 135 if (listener != null) { 136 listener.onLetterUpdate(LETTERS[index]); 137 } 138 139 Log.d("jiejie", "onTouchEvent : " + LETTERS[index]); 140 touchIndex = index; 141 } 142 } 143 break; 144 case MotionEvent.ACTION_UP: 145 touchIndex = -1; 146 break; 147 default: 148 break; 149 } 150 // 重新绘制 151 invalidate(); 152 return true; 153 } 154 }
主代码和布局文件
1 package com.demo.sb.main; 2 3 import java.util.ArrayList; 4 import java.util.Collections; 5 import com.demo.sb.entity.Cheeses; 6 import com.demo.sb.entity.HaoHanAdapter; 7 import com.demo.sb.entity.Person; 8 import com.demo.sb.utils.DensityUtil; 9 import com.demo.sb.widget.QuickIndexBar; 10 import com.demo.sb.widget.QuickIndexBar.OnLetterUpdateListener; 11 import com.demo.suibian.R; 12 import android.app.Activity; 13 import android.os.Bundle; 14 import android.os.Handler; 15 import android.text.TextUtils; 16 import android.view.View; 17 import android.widget.ListView; 18 import android.widget.TextView; 19 20 /** 21 * 快速索引 22 * 23 * @author Administrator 24 * 25 */ 26 public class Activity_Index extends Activity { 27 28 /**ListView用来展示数据的*/ 29 private ListView mListView; 30 31 /**贮存的数据*/ 32 private ArrayList<Person> persons; 33 34 /**展示所点的字母,相当于Toast*/ 35 private TextView tv_center; 36 37 @Override 38 protected void onCreate(Bundle savedInstanceState) { 39 // TODO Auto-generated method stub 40 super.onCreate(savedInstanceState); 41 setContentView(R.layout.mactivity_index); 42 43 QuickIndexBar bar = (QuickIndexBar) findViewById(R.id.bar); 44 bar.setListener(new OnLetterUpdateListener() { 45 46 @Override 47 public void onLetterUpdate(String letter) { 48 // TODO Auto-generated method stub 49 DensityUtil.showToast(Activity_Index.this, letter); 50 showLetter(letter); 51 // 根据字母定位ListView,找到集合中第一个以letter为拼音首字母的对象,得到索引 52 for (int i = 0; i < persons.size(); i++) { 53 Person p = persons.get(i); 54 String l = p.getPinyin().charAt(0) + ""; 55 if (TextUtils.equals(letter, l)) { 56 mListView.setSelection(i); 57 break; 58 } 59 } 60 } 61 }); 62 63 mListView = (ListView) findViewById(R.id.lv_index); 64 persons = new ArrayList<Person>(); 65 // 填充数据,排序 66 fillAndSortData(persons); 67 68 mListView.setAdapter(new HaoHanAdapter(Activity_Index.this, persons)); 69 70 tv_center = (TextView) findViewById(R.id.tv_center); 71 72 } 73 74 private Handler mHandler = new Handler(); 75 76 /** 77 * 显示字母 78 * 79 * @param letter 80 */ 81 protected void showLetter(String letter) { 82 // TODO Auto-generated method stub 83 tv_center.setVisibility(View.VISIBLE); 84 tv_center.setText(letter); 85 86 mHandler.removeCallbacksAndMessages(null); 87 mHandler.postDelayed(new Runnable() { 88 89 @Override 90 public void run() { 91 // TODO Auto-generated method stub 92 tv_center.setVisibility(View.GONE); 93 } 94 }, 2000); 95 } 96 97 private void fillAndSortData(ArrayList<Person> persons2) { 98 // TODO Auto-generated method stub 99 // 填充数据 100 for (int i = 0; i < Cheeses.NAMES.length; i++) { 101 String name = Cheeses.NAMES[i]; 102 persons2.add(new Person(name)); 103 } 104 105 // 进行排序 106 Collections.sort(persons2); 107 } 108 }
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" > 5 6 <ListView 7 android:id="@+id/lv_index" 8 android:layout_width="match_parent" 9 android:layout_height="match_parent" /> 10 11 <com.demo.sb.widget.QuickIndexBar 12 android:id="@+id/bar" 13 android:layout_width="30dp" 14 android:layout_height="match_parent" 15 android:layout_alignParentRight="true" 16 android:background="#f00" /> 17 18 <TextView 19 android:id="@+id/tv_center" 20 android:layout_width="160dp" 21 android:layout_height="100dp" 22 android:layout_centerInParent="true" 23 android:background="@drawable/bg_index" 24 android:gravity="center" 25 android:text="" 26 android:textColor="#fff" 27 android:textSize="32sp" 28 android:visibility="gone" /> 29 30 </RelativeLayout>
适配器
1 package com.demo.sb.entity; 2 3 import java.util.ArrayList; 4 5 import com.demo.sb.utils.DensityUtil; 6 import com.demo.suibian.R; 7 8 import android.content.Context; 9 import android.text.TextUtils; 10 import android.view.View; 11 import android.view.ViewGroup; 12 import android.widget.BaseAdapter; 13 import android.widget.TextView; 14 15 public class HaoHanAdapter extends BaseAdapter { 16 17 private Context mContext; 18 private ArrayList<Person> persons; 19 20 public HaoHanAdapter(Context mContext, ArrayList<Person> persons) { 21 this.mContext = mContext; 22 this.persons = persons; 23 } 24 25 @Override 26 public int getCount() { 27 // TODO Auto-generated method stub 28 return persons.size(); 29 } 30 31 @Override 32 public Object getItem(int arg0) { 33 // TODO Auto-generated method stub 34 return persons.get(arg0); 35 } 36 37 @Override 38 public long getItemId(int arg0) { 39 // TODO Auto-generated method stub 40 return arg0; 41 } 42 43 @Override 44 public View getView(final int arg0, View arg1, ViewGroup arg2) { 45 // TODO Auto-generated method stub 46 ViewHolder holder = null; 47 if (arg1 == null) { 48 arg1 = View.inflate(mContext, R.layout.item_haohanadapter, null); 49 holder = new ViewHolder(); 50 holder.mIndex = (TextView) arg1.findViewById(R.id.item_tv_index); 51 holder.mName = (TextView) arg1.findViewById(R.id.item_tv_name); 52 arg1.setTag(holder); 53 } else { 54 holder = (ViewHolder) arg1.getTag(); 55 } 56 57 final Person p = persons.get(arg0); 58 String string = null; 59 String currentLetter = p.getPinyin().charAt(0) + ""; 60 // 根据上一个首字母,决定当前是否显示字母 61 if (arg0 == 0) { 62 string = currentLetter; 63 } else { 64 // 上一个人的拼音的首字母 65 String preLetter = persons.get(arg0 - 1).getPinyin().charAt(0) + ""; 66 if (!TextUtils.equals(preLetter, currentLetter)) { 67 string = currentLetter; 68 } 69 } 70 71 // 根据string 是否为空,决定是否显示索引栏 72 holder.mIndex.setVisibility(string == null ? View.GONE : View.VISIBLE); 73 holder.mIndex.setText(currentLetter); 74 holder.mName.setText(p.getName()); 75 76 holder.mName.setOnClickListener(new View.OnClickListener() { 77 78 @Override 79 public void onClick(View view) { 80 // TODO Auto-generated method stub 81 DensityUtil.showToast(mContext, arg0 + " "+p.getName()); 82 } 83 }); 84 return arg1; 85 } 86 87 static class ViewHolder { 88 TextView mIndex; 89 TextView mName; 90 } 91 }
实体类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.demo.sb.entity; import com.demo.sb.utils.PinyinUtils; public class Person implements Comparable<Person>{ private String name; private String pinyin; public Person(String name){ super(); this.name = name; this.pinyin = PinyinUtils.getPinyin(name); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPinyin() { return pinyin; } public void setPinyin(String pinyin) { this.pinyin = pinyin; } @Override public int compareTo(Person another) { // TODO Auto-generated method stub return this.pinyin.compareTo(another.getPinyin()); } }
汉字转成拼音
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
package com.demo.sb.utils; import net.sourceforge.pinyin4j.PinyinHelper; import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; public class PinyinUtils { public static String getPinyin(String str) { HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); format.setCaseType(HanyuPinyinCaseType.UPPERCASE); format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); StringBuilder sb = new StringBuilder(); char[] charArray = str.toCharArray(); for (int i = 0; i < charArray.length; i++) { char c = charArray[i]; // 如果是空格, 跳过 if (Character.isWhitespace(c)) { continue; } if (c >= -127 && c < 128) { // 肯定不是汉字 sb.append(c); } else { String s = ""; try { // 通过char得到拼音集合. 单 -> dan, shan s = PinyinHelper.toHanyuPinyinStringArray(c, format)[0]; sb.append(s); } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); sb.append(s); } } } return sb.toString(); } }