android快速索引的实现
2016-06-14 11:36
507 查看
从图中可以看出,这种快速索引在客户端的运用非常多,例如:快速检索城市,快速检索联系人等。接下来就来看看如何实现这种效果吧。
画字母
要实现这个效果, 先得把右侧的字母条画出来, 这里我们写个类, 继承自 View, 由于其内部不需要包含其他布局, 所以继承 View 即可, 无需继承 ViewGroup.public class QuickIndexBar extends View {
private static final String TAG = "TAG";
private static final String[] LETTERS = new String[]{
"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 mPaint;
public interface OnLetterChangeListener{
void OnLetterChange(String letter);
}
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);
// 初始化画笔
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.WHITE);
mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, getResources().getDisplayMetrics()));
mPaint.setTypeface(Typeface.DEFAULT_BOLD);
}
}
既然要画字母, 就要有画笔, 这里在构造方法里完成画笔的初始化, 创建一个抗锯齿, 颜色为白色, 大小12sp, 粗体的画笔. 有了画笔, 就要开始画了, 画法如图所示。
这里面要注意的是, 使用 Canvas 画文字的时候, 是从左下角开始的.
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mHeight = getMeasuredHeight();
mCellHeight = mHeight * 1.0f / LETTERS.length;
mCellWidth = getMeasuredWidth();
}
@Override
protected void onDraw(Canvas canvas) {
// 绘制字母
for (int i = 0; i < LETTERS.length; i++) {
String text = LETTERS[i];
// 求x坐标
int x = (int) (mCellWidth / 2.0f - mPaint.measureText(text) / 2.0f);
// 求y坐标
// 格子高度的一半 + 文字高度的一半 + 其上边所有格子高度
Rect bounds = new Rect();
mPaint.getTextBounds(text, 0, text.length(), bounds);
int y = (int) (mCellHeight / 2.0f + bounds.height() / 2.0f + mCellHeight * i);
canvas.drawText(text, x, y, mPaint);
}
}
这样一来, 字母就画出来了, 如果想要更自由一些的话, 可以使用自定义属性传入字体颜色.
触摸事件和回调
界面效果有了, 接下来就是处理触摸事件以及回调了, 处理触摸事件肯定是重写 onTouchEvent 方法了, 回调的话, 定义一个回调接口, 提供 get/set 方法, 在 onTouchEvent 相应的位置调用. onTouchEvent 代码如下:@Override
public boolean onTouchEvent(MotionEvent event) {
switch (MotionEventCompat.getActionMasked(event)) {
case MotionEvent.ACTION_DOWN:
// 根据y值获取当前触摸到的字母
float y = event.getY();
int index = (int) (y / mCellHeight);
// 如果字母索引发生变化
if(index != touchIndex){
if(index >= 0 && index < LETTERS.length){
Log.d(TAG, LETTERS[index]);
if(mLetterChangeListener != null){
// 执行回调
mLetterChangeListener.OnLetterChange(LETTERS[index]);
}
}
touchIndex = index;
}
break;
case MotionEvent.ACTION_MOVE:
// 根据y值获取当前触摸到的字母
int i = (int) (event.getY() / mCellHeight);
// 如果字母索引发生变化
if(i != touchIndex){
if(i >= 0 && i < LETTERS.length){
Log.d(TAG, LETTERS[i]);
if(mLetterChangeListener != null){
mLetterChangeListener.OnLetterChange(LETTERS[i]);
}
}
touchIndex = i;
}
break;
case MotionEvent.ACTION_UP:
// 恢复默认索引值
touchIndex = -1;
break;
default:
break;
}
invalidate();
return true;
}
回调接口:
public interface OnLetterChangeListener{ void OnLetterChange(String letter); } // 字母变化监听 private OnLetterChangeListener mLetterChangeListener; public OnLetterChangeListener getLetterChangeListener() { return mLetterChangeListener; } public void setLetterChangeListener( OnLetterChangeListener mLetterChangeListener) { this.mLetterChangeListener = mLetterChangeListener; }
ListView 的处理
首先我们要获取一个所有名字的集合, 并且对它按照拼音顺序排序.
private void fillAndSort(ArrayList<Friend> names) { for (int i = 0; i < NAMES.length; i++) { names.add(new Friend(NAMES[i])); } Collections.sort(names); } Friend 类如下: public class Friend implements Comparable<Friend>{ private String name; private String pinyin; public Friend(String name) { super(); this.name = name; // 获取拼音 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(Friend another) { return pinyin.compareTo(another.getPinyin()); } }那么接下来就是 ListView 的 Adapter 了, 这里要注意一点就是, 相同字母开头的名字, 只有第一个显示, 其他的不显示.
@Override public View getView(int position, View convertView, ViewGroup parent) { View view = convertView; if(convertView == null){ view = View.inflate(mContext, R.layout.item_list, null); } ViewHolder mViewHolder = ViewHolder.getHolder(view); Friend friend = names.get(position); // 跟上一个进行比较,如果不同,则显示。 String letter = null; String currentLetter = friend.getPinyin().charAt(0) + ""; if(position == 0){ // 第一个人直接显示 letter = currentLetter; }else { // 获取上一个人的拼音 String preLetter = names.get(position - 1).getPinyin().charAt(0) + ""; if(!TextUtils.equals(preLetter, currentLetter)){ letter = currentLetter; } } mViewHolder.mIndex.setVisibility(letter == null ? View.GONE : View.VISIBLE); if(letter != null){ mViewHolder.mIndex.setText(letter); } mViewHolder.mName.setText(friend.getName()); return view; }
每个 item 的布局如下:<?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_index"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#555555"
android:gravity="center_vertical"
android:paddingLeft="20dp"
android:visibility="gone"
android:text="A"
android:textColor="#ffffff"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center_vertical"
android:paddingLeft="20dp"
android:text="宋江"
android:textColor="#000000"
android:textSize="22sp" />
</LinearLayout> 也就是说, 其实每个条目都是有个字母索引, 有个名字, 只是首字母相同的名字, 只有第一个显示索引。最后就是在索引条上滑动的时候移动到 ListView 相应的位置了, 这个就是实现它提供的回调:
QuickIndexBar mQuickIndexBar = (QuickIndexBar) findViewById(R.id.quickIndex); mQuickIndexBar.setLetterChangeListener(new OnLetterChangeListener() { @Override public void OnLetterChange(String letter) { Utils.showToast(getApplicationContext(), letter); // 执行ListView的定位方法 for (int i = 0; i < names.size(); i++) { Friend friend = names.get(i); String l =friend.getPinyin().charAt(0) + ""; if(TextUtils.equals(letter, l)){ // 中断循环,快速定位 mListView.setSelection(i); break; } } } });
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories