实现的功能:主界面分为右侧的自定义控件,中间为一个ListView,如下图:
点击主界面右侧的A-Z字符,快递定位到ListView的item中汉字以该字母开头的数据。
使用到第三方jar包,使用该jar包的由字符串得到拼音的功能。
实现的过程:
1.创建自定义控件然后实现主界面右侧A-Z的绘画
2.处理A-Z的onTouch事件
3.在自定义控件中提供点击事件的监听
4.使用jar实现将字符串转换成汉字
5.进行分组排序展示
6.实现点击自定义控件对listView的选择
1.创建自定义控件:
自定义QUICKIndexBar控件,然后继承View,在初始化方法中定义一个画笔然后设置其颜色和类型:
public void setListener(OnLetterUpdateListener listener) {
this.listener = listener;
}
public QuickIndexBar(Context context) {
this(context, null);
}
public QuickIndexBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public QuickIndexBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //消除锯齿
mPaint.setColor(Color.WHITE);//设置颜色
mPaint.setTypeface(Typeface.DEFAULT_BOLD);//设置黑体字体类型
mPaint.setTextSize(40);//设置字体大小
}
画笔定义了之后就是对A-Z进行绘画,重写onDraw方法可实现对界面的绘画,绘画的难点是是确定A-Z的坐标,A-Z的横向位置都是一样的,所有可以首先拿到存放A-Z单元格的宽高,然后得到A-Z的X坐标,经过画图可以很直接的观察到,A-Z每个相邻的字母Y坐标的距离都是又规律的,然后就可以很轻松的拿到每一个字符的Y坐标,也正是有这个规律可以从Y坐标求出对应的字符。
x = (单元格宽度-字符宽度)/2
y = (单元格高度+字符高度)/2+单元格高度*单元格个数
/**
* 获取到单元格的宽高
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 获取单元格的宽和高
cellWidth = getMeasuredWidth();
int mHeight = getMeasuredHeight();
cellHeight = mHeight * 1.0f / LETTERS.length;
}
/**
* 绘制A_Z
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < LETTERS.length; i++) {
String text = LETTERS[i];
// 计算坐标
int x = (int) (cellWidth / 2.0f - mPaint.measureText(text) / 2.0f);
// 获取文本的高度
Rect bounds = new Rect();// 矩形
mPaint.getTextBounds(text, 0, text.length(), bounds);
int textHeight = bounds.height();
int y = (int) (cellHeight / 2.0f + textHeight / 2.0f + i * cellHeight);
// 根据按下的字母, 设置画笔颜色
mPaint.setColor(touchIndex == i ? Color.GRAY : Color.WHITE);
// 绘制文本A-Z
canvas.drawText(text, x, y, mPaint);
}
2.处理onTouch事件:
首先根据用户手指按下,拿到Y坐标,然后用Y坐标/单元格高度,求出按下的是哪个单元格,然后求出是哪个字符,再用Toast进行提示,处理了DOWN事件后,进行MOVE事件的处理,由于要多次处理滑动事件,所有要reture true让该空间处理触摸事件,滑动事件判断哪个字符和DOWN事件一样,代码如下:
/**
* 处理触摸事件
*/
int touchIndex = -1;
@Override
public boolean onTouchEvent(MotionEvent event) {
int index = -1;
switch (MotionEventCompat.getActionMasked(event)) {
case MotionEvent.ACTION_DOWN:
// 获取当前触摸到的字母索引
index = (int) (event.getY() / cellHeight);
if(index >= 0 && index < LETTERS.length){
// 判断是否跟上一次触摸到的一样
if(index != touchIndex) {
if(listener != null){
listener.onLetterUpdate(LETTERS[index]);
}
Log.d(TAG, "onTouchEvent: " + LETTERS[index]);
touchIndex = index;
}
}
break;
case MotionEvent.ACTION_MOVE:
index = (int) (event.getY() / cellHeight);
if(index >= 0 && index < LETTERS.length){
// 判断是否跟上一次触摸到的一样
if(index != touchIndex){
if(listener != null){
listener.onLetterUpdate(LETTERS[index]);
}
Log.d(TAG, "onTouchEvent: " + LETTERS[index]);
touchIndex = index;
}
}
break;
case MotionEvent.ACTION_UP:
touchIndex = -1;
break;
default:
break;
}
invalidate();
return true;
}
3.在自定义控件中提供一个
接口供主界面调用
定义一个接口和方法供主界面找到ListView的索引,这个接口中的方法在触摸事件中调用。
/**
* 暴露一个字母的监听
*/
public interface OnLetterUpdateListener{
void onLetterUpdate(String letter);
}
private OnLetterUpdateListener listener;
public OnLetterUpdateListener getListener() {
return listener;
}
/**
* 设置字母更新监听
* @param listener
*/
public void setListener(OnLetterUpdateListener listener) {
this.listener = listener;
}
4.使用jar首先一个工具方法,这个工具方法实现了输入字符串得到汉语拼音的回调
public class PinyinUtils {
/**
* 根据传入的字符串(包含汉字),得到拼音
* @param str 字符串
* @return
*/
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();
}
}
首先创建一个人名类,在该类中有两个变量name和pinyin变量,在构造方法中只传入人名然后使用工具类得到拼音,然后创建一个类在类中写入所有人名的数据,然后提供给listView,然后在MainAcitivity中,拿到该数据并排好序后,使用ListView进行展示(具体代码看附件)。
/**
* 根据输入的集合对集合中的数据进行排序
* @param persons
*/
private void fillAndSortData(ArrayList persons) {
for (int i = 0; i < Cheeses.NAMES.length; i++) {
String name = Cheeses.NAMES[i];
persons.add(new Person(name));
}
//进行排序
Collections.sort(persons);
}
//存储人名和拼音
mPersons = new ArrayList<>();
// 填充数据 , 排序
fillAndSortData(mPersons);
HaoHanAdapter adapter = new HaoHanAdapter(this, mPersons);
list.setAdapter(adapter);
将自定义控件为ListView进行绑定需要用到自定义控件中改变字母监听的接口,该接口中点击A-Z可以带到A-Z的值,然后将A-Z的值与ListView数据中的拼音进行比较,若相同,则跳转该处,这样就实现了点击A-Z进行对ListView中数据的索引。
private void initData() {
/**
* 实现索引
*/
bar.setListener(new QuickIndexBar.OnLetterUpdateListener() {
@Override
public void onLetterUpdate(String letter) {
// Utils.showToast(getApplicationContext(), letter);
showLetter(letter);
// 根据字母定位ListView, 找到集合中第一个以letter为拼音首字母的对象,得到索引
for (int i = 0; i //显示字母
private void showLetter(String letter) {
tv_centent.setVisibility(View.VISIBLE);
tv_centent.setText(letter);
mHandler.removeCallbacksAndMessages(null);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
tv_centent.setVisibility(View.GONE);
}
},2000 );
}
源码下载地址:链接:http://pan.baidu.com/s/1dFDKGnf 密码:6ed0