联系人快速索引
1.界面布局
<?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.contactquickindex.MainActivity">
<ListView
android:id="@+id/lv_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/tv_word"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerInParent="true"
android:background="#44000000"
android:gravity="center"
android:text="A"
android:textColor="#000000"
android:textSize="30sp"
android:visibility="gone" />
<com.contactquickindex.IndexView
android:id="@+id/iv_words"
android:layout_width="30dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="#ff0000" />
</RelativeLayout>
2.初始化显示字母列表
1.自定义IndexView继承于View
/**
*
* @author: Hashub.NG
* @description: 绘制快速索引的字母 1.把26个字母放入数组 2.在onMeasure计算每条的高itemHeight和宽itemWidth,
* 3.在onDraw和wordWidth,wordHeight,wordX,wordY
* <p/>
* 手指按下文字变色 1.重写onTouchEvent(),返回true,在down/move的过程中计算 int
* touchIndex = Y / itemHeight; 强制绘制
* <p/>
* 2.在onDraw()方法对于的下标设置画笔变色
* <p/>
* 3.在up的时候 touchIndex = -1; 强制绘制
* @update: 2018年8月1日 下午7:43:44
*/
public class IndexView extends View{}
2.定义变量
/**
* 每条的宽和高
*/
private int itemWidth;
private int itemHeight;
private 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" };
3.初始化字母位置
public IndexView(Context context, AttributeSet attrs)
{
super(context, attrs);
paint = new Paint();
paint.setColor(Color.WHITE);// 设置颜色
paint.setAntiAlias(true);// 设置抗锯齿
paint.setTypeface(Typeface.DEFAULT_BOLD);// 设置粗体字
}
/**
* 测量方法
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@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.WHITE);
}
String word = words[i];// A
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);
}
}
3.在按下和移动的时候使操作字母变色
实现步骤分析
.在按下和移动时候,使操作的字母变色
a.重写onTouchEvent(),返回true
b.在down/move时,计算出操作的下标,并且在onDraw(),设置不同颜色画笔,强制绘制
c.在up时,重置操作下标,强制重绘制
/**
* 字母的下标位置
*/
private int touchIndex = -1;
/**
* 手指按下文字变色
* 1.重写onTouchEvent(),返回true,在down/move的过程中计算
* int touchIndex = Y / itemHeight; 强制绘制
* <p/>
* 2.在onDraw()方法对于的下标设置画笔变色
* <p/>
* 3.在up的时候
* touchIndex = -1;
* 强制绘制
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event)
{
super.onTouchEvent(event);
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN://Down和Move都需要处理,不需要break
case MotionEvent.ACTION_MOVE:
float Y=event.getY();
int index=(int) (Y/itemHeight);//字母索引
if(index!=touchIndex)
{
touchIndex=index;
invalidate();//强制绘制onDraw();
if(onIndexChangeListener!=null && touchIndex<words.length)//判空及防止数组越界
{
onIndexChangeListener.onIndexChange(words[touchIndex]);
}
}
break;
case MotionEvent.ACTION_UP:
touchIndex=-1;
invalidate();
break;
}
return true;
}
4.在按下和移动时显示更新提示字母
使用接口,封装变化,供外界调用
/**
* 字母下标索引变化的监听器
*/
public interface OnIndexChangeListener
{
/**
* 当字母下标位置发生变化的时候回调
*
* @param word
* 字母(A~Z)
*/
void onIndexChange(String word);
}
/**
* 设置字母下标索引变化的监听
* 把接口写成类的成员变量,提供set方法
* @param onIndexChangeListener
*/
public void setOnIndexChangeListener(OnIndexChangeListener onIndexChangeListener)
{
this.onIndexChangeListener = onIndexChangeListener;
}
在MainActivity中使用接口
private ListView lv_main;
private TextView tv_word;
private IndexView iv_words;
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);
lv_main = (ListView) findViewById(R.id.lv_main);
tv_word = (TextView) findViewById(R.id.tv_word);
iv_words = (IndexView) findViewById(R.id.iv_words);
//设置监听字母下标索引的变化
iv_words.setOnIndexChangeListener(new MyOnIndexChangeListener());
//准备数据
initData();
//设置适配器
adapter = new IndexAdapter();
lv_main.setAdapter(adapter);
}
使用handler延迟隐藏
//显示
tv_word.setVisibility(View.VISIBLE);
tv_word.setText(word);
handler.removeCallbacksAndMessages(null);
handler.postDelayed(new Runnable() {
@Override
public void run() {
//也是运行在主线程
System.out.println(Thread.currentThread().getName()+"------------");
tv_word.setVisibility(View.GONE);
}
}, 3000);
5.列表显示联系人
导入汉字转拼音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;
}
}
Person bean类
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 + '\'' + '}';
}
}
初始化联系人列表数据
/**
* 初始化数据
*/
private void initData() {
persons = new ArrayList<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("阿三"));
persons.add(new Person("李博俊"));
联系人列表排序
//排序
Collections.sort(persons, new Comparator<Person>() {
@Override
public int compare(Person lhs, Person rhs) {
return lhs.getPinyin().compareTo(rhs.getPinyin());
}
});
item_main.xml布局
<?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="#44000000"
android:text="A"
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:textColor="#000000"
android:textSize="25sp" />
</LinearLayout>
listview填充数据显示
class IndexAdapter extends BaseAdapter{
@Override
public int getCount() {
return persons.size();
}
@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 = (TextView) convertView.findViewById(R.id.tv_word);
viewHolder.tv_name = (TextView) 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;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
}
static class ViewHolder{
TextView tv_word;
TextView tv_name;
}
6.在按下和移动是列表更新
更新列表
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中的位置
lv_main.setSelection(i);//定位到ListVeiw中的某个位置
return;
}
}
}
网友评论