左边列表,右边索引条
- 对快速索引效果进行模块划分,分为左右2部分,每部分的功能如下:
o 右边字母View,需要绘制26个字母,并且能够获取当前所触摸的字母;
o 左边listview,需要对条目数据按照首字母进行分割,并且根据当前所触摸的字母将对应的条目放置到顶端;
下载拼音排序jar包
直接上码
public class QuickIndexBar extends View {
private static final String TAG = "QuickIndexBar";
private String[] letterArr = {"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 Paint paint;
public QuickIndexBar(Context context) {
this(context, null);
}
public QuickIndexBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public QuickIndexBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);//设置抗锯齿
paint.setColor(Color.WHITE);
int size = getResources().getDimensionPixelSize(R.dimen.qib_text_size);
paint.setTextSize(size);
//画笔绘制文字默认的起点是文字的左下角,android系统绘制文字是按照基准线来绘制的
paint.setTextAlign(Paint.Align.CENTER);//center是文字的底边的中心
}
float cellHeight;//格子的高度
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
cellHeight = getMeasuredHeight()*1f/letterArr.length;
}
@Override
protected void onDraw(Canvas canvas) {
//遍历数组绘制26个字母
for (int i = 0; i < letterArr.length; i++) {
String text = letterArr[i];
float x = getMeasuredWidth()/2;
//y:格子高度一半 + 文字高度一半 + i*格子高
float y = cellHeight/2 + getTextHeight(text)/2 + i*cellHeight;
paint.setColor(i==lastIndex?Color.BLACK:Color.WHITE);
canvas.drawText(text,x,y,paint);
}
}
int lastIndex = -1;//记录上次字母的索引
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int index = (int) (event.getY() / cellHeight);
if(lastIndex!=index){
//对索引进行安全性的判断
if(index>=0 && index<letterArr.length){
String letter = letterArr[index];
if(listener!=null){
listener.onLetterChange(letter);
}
}
}
lastIndex = index;
break;
case MotionEvent.ACTION_UP:
//抬起手指重置
lastIndex = -1;
if(listener!=null){
listener.onRelease();
}
break;
}
//刷新重绘
invalidate();
return true;
}
/**
* 获取文字的高度
* @param text
* @return
*/
private int getTextHeight(String text) {
Rect bounds = new Rect();
//方法一执行完,bounds就有值了
paint.getTextBounds(text,0,text.length(),bounds);
return bounds.height();
}
private OnLetterChangeListener listener;
public void setOnLetterChangeListener(OnLetterChangeListener listener){
this.listener = listener;
}
public interface OnLetterChangeListener{
void onLetterChange(String letter);
void onRelease();
}
}
布局
<?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.kailing.quickindex99.MainActivity">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.kailing.quickindex99.QuickIndexBar
android:layout_alignParentRight="true"
android:layout_width="30dp"
android:id="@+id/quickIndexBar"
android:background="#ff0000"
android:layout_height="match_parent" />
<TextView
android:id="@+id/tv_letter"
android:gravity="center"
android:textSize="45sp"
android:text="A"
android:visibility="gone"
android:layout_centerInParent="true"
android:textColor="#fff"
android:background="@drawable/bg"
android:layout_width="110dp"
android:layout_height="110dp"
/>
</RelativeLayout>
填充数据的bean类,使用pinyin排序
public class Friend implements Comparable<Friend>{
public String name;
public String pinyin;
public Friend(String name) {
this.name = name;
pinyin = PinYinUtil.getPinYin(name);
}
@Override
public int compareTo(@NonNull Friend o) {
return this.pinyin.compareTo(o.pinyin);
}
}
mainactivity
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
quickIndexBar.setOnLetterChangeListener(new QuickIndexBar.OnLetterChangeListener() {
@Override
public void onLetterChange(String letter) {
//从集合中找到首字母为letter的位置,然后将其置顶
for (int i = 0; i < friends.size(); i++) {
String s = friends.get(i).pinyin.charAt(0) + "";
if (s.equals(letter)) {
//说明找到了,将其置顶
listview.setSelection(i);
//找到就中断
break;
}
}
//显示当前触摸的字母
showLetter(letter);
}
@Override
public void onRelease() {
tvLetter.setVisibility(View.GONE);
}
});
//设置数据
prepareData();
//排序
Collections.sort(friends);
//设置adapter
listview.setAdapter(new FriendAdapter(friends));
}
/**
* 显示当前触摸的字母
* @param letter
*/
private void showLetter(String letter) {
tvLetter.setText(letter);
tvLetter.setVisibility(View.VISIBLE);
}
private void prepareData() {
friends.add(new Friend("李伟"));
friends.add(new Friend("张三"));
friends.add(new Friend("阿三"));
friends.add(new Friend("阿四"));
friends.add(new Friend("段誉"));
friends.add(new Friend("段正淳"));
friends.add(new Friend("张三丰"));
friends.add(new Friend("王二"));
friends.add(new Friend("王二b"));
friends.add(new Friend("赵四"));
friends.add(new Friend("杨坤"));
friends.add(new Friend("赵子龙"));
friends.add(new Friend("杨坤1"));
friends.add(new Friend("李伟1"));
friends.add(new Friend("宋江"));
friends.add(new Friend("宋江1"));
friends.add(new Friend("李伟3"));
}
}
adapter
public class FriendAdapter extends BaseAdapter {
ArrayList<Friend> friends;
public FriendAdapter(ArrayList<Friend> friends) {
this.friends = friends;
}
@Override
public int getCount() {
return friends.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) {
FriendHolder holder = null;
if (convertView == null) {
convertView = View.inflate(parent.getContext(), R.layout.adapter_friend, null);
holder = new FriendHolder(convertView);
convertView.setTag(holder);
}else {
holder = (FriendHolder) convertView.getTag();
}
//绑定数据
Friend friend = friends.get(position);
String letter = friend.pinyin.charAt(0)+"";
//获取上一个条目的首字母
if(position>0){
String lastLetter = friends.get(position-1).pinyin.charAt(0)+"";
if(letter.equals(lastLetter)){
//如果和上一个首字母相同,则隐藏当前的字母TextView
holder.tvLetter.setVisibility(View.GONE);
}else {
//如果不一样,就显示
holder.tvLetter.setVisibility(View.VISIBLE);
holder.tvLetter.setText(letter);
}
}else {
//说明当前是第0条
holder.tvLetter.setVisibility(View.VISIBLE);
holder.tvLetter.setText(letter);
}
holder.tvName.setText(friend.name);
return convertView;
}
static class FriendHolder {
@BindView(R.id.tv_letter)
TextView tvLetter;
@BindView(R.id.tv_name)
TextView tvName;
FriendHolder(View view) {
ButterKnife.bind(this, view);
}
}
}
pinyinutils
public class PinYinUtil {
/**
* 获取指定汉字的拼音
* @param chinese
* @return
*/
public static String getPinYin(String chinese){
if(TextUtils.isEmpty(chinese))return null;
//控制输出的拼音的格式,比如字母的大小写,需不需声调
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
format.setCaseType(HanyuPinyinCaseType.UPPERCASE);//大写字母
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);//不要声调
//由于只支持单个汉字的获取,因此要将字符串转为字符数组,对每个汉字获取,最后拼接
char[] charArr = chinese.toCharArray();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < charArr.length; i++) {
char c = charArr[i];
//1.过滤空格,选择忽略空格
if(Character.isWhitespace(c)){
continue;
}
//2.过滤非中文字符: &&a齐*天*大*圣a&&
//简单判断:由于中文在utf8中占据3个字节(-128~127),所以中文肯定大于127
if(c > 127){
//说明有可能是中文,获取它的拼音
try {
//由于多音字的存在,比如: 单:[dan, chan, shan]
String[] pinyinArr = PinyinHelper.toHanyuPinyinStringArray(c, format);
if(pinyinArr!=null){
//此处,我们取第0个,因为我们无能为力了。我们目前木办法去精准判断该字的读音。
//如果要实现精准读音,需要这样几个技术配合:分词算法,字库培养,服务器api支持.
builder.append(pinyinArr[0]);
}
} catch (Exception e) {
e.printStackTrace();
//说明获取失败,不用管
}
}else {
//肯定不是中文,一般是那些因为字母:abcd,直接拼接即可
builder.append(c);
}
}
return builder.toString();
}
}