效果图:
项目结构:
核心代码
自定义索引:
IndexView
package com.example.mytongxunlu4;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.orhanobut.logger.Logger;
/**
* 自定义通讯录
*/
public class IndexView extends View {
/**
* 每条的宽和高
*/
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"};
public IndexView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
//设置颜色
paint.setColor(Color.WHITE);
//设置抗锯齿
paint.setAntiAlias(true);
//设置粗体字
paint.setTypeface(Typeface.DEFAULT_BOLD);
// 设置字体大小
paint.setTextSize(DensityUtil.dip2px(context,20));
}
/**
* 测量方法
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
itemWidth = getMeasuredWidth();
itemHeight = getMeasuredHeight() / words.length;
Logger.t("111").d("onMeasure>>>");
}
/**
* 绘制
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Logger.t("111").d("onDraw>>>");
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);
}
}
/**
* 字母的下标位置
*/
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:
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();//强制绘制onDraw();
break;
}
return true;
}
/**
* 接口回调
* 字母下标索引变化的监听器
*/
public interface OnIndexChangeListener{
/**
* 当字母下标位置发生变化的时候回调
* @param word 字母(A~Z)
*/
void onIndexChange(String word);
}
private OnIndexChangeListener onIndexChangeListener;
/**
* 设置字母下标索引变化的监听
* @param onIndexChangeListener
*/
public void setOnIndexChangeListener(OnIndexChangeListener onIndexChangeListener) {
this.onIndexChangeListener = onIndexChangeListener;
}
}
工具类DensityUtil
package com.example.mytongxunlu4;
import android.content.Context;
/**
* 工具类
*/
public class DensityUtil {
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
}
适配器:
IndexAdapter
package com.example.mytongxunlu4;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.ArrayList;
/**
*ListView适配器
*/
class IndexAdapter extends BaseAdapter {
private Context context;
private ArrayList<Person> persons;
public IndexAdapter(Context context,ArrayList<Person> persons){
this.context=context;
this.persons=persons;
}
@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(context, 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;
}
}
MainActivity
package com.example.mytongxunlu4;
import android.app.Activity;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.common.api.GoogleApiClient;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
* 在下按和移动时,列表更新
*/
public class MainActivity extends Activity {
private ListView lv_main;
private TextView tv_word;
private IndexView iv_words;
ArrayList<Person> persons;
IndexAdapter adapter;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
// 关闭显示的字母提示
tv_word.setVisibility(View.GONE);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
myOnclick();
// 准备数据
initData();
//设置适配器
adapter = new IndexAdapter(MainActivity.this,persons);
lv_main.setAdapter(adapter);
}
private void myOnclick() {
// IndexView中回调出来的点击事件
iv_words.setOnIndexChangeListener(new IndexView.OnIndexChangeListener() {
@Override
public void onIndexChange(String word) {
updateWord(word);//提示框的字母显示和隐藏
updateListView(word);//A~Z 根据右边的索引listView定位到某个位置
}
});
}
/**
* 提示框的字母显示和隐藏
* @param word
*/
private void updateWord(String word) {
// 显示提示框
tv_word.setVisibility(View.VISIBLE);
tv_word.setText(word + "");
handler.removeCallbacksAndMessages(null);
handler.sendEmptyMessageDelayed(1, 3000);
}
/**
* 根据右边的索引listView定位到某个位置
* @param word
*/
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;
}
}
}
/**
* 初始化控件
*/
private void initView() {
lv_main = (ListView) findViewById(R.id.lv_main);
tv_word = (TextView) findViewById(R.id.tv_word);
iv_words = (IndexView) findViewById(R.id.iv_words);
}
/**
* 初始化数据
*/
private void initData() {
persons = new ArrayList<>();
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());
}
});
}
}
实体类:
package com.example.mytongxunlu4;
/**
* 作者:杨光福 on 2016/5/18 11:25
* 微信:yangguangfu520
* QQ号:541433511
* 作用:姓名:阿福
* 拼音:AFU
*/
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 + '\'' +
'}';
}
}
汉字转化 拼音工具类:(对应要用jar包 pinyin4j-2.5.0.jar)
PinYinUtils
package com.example.mytongxunlu4; 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; /** * 作者:杨光福 on 2016/4/14 13:57 * 微信:yangguangfu520 * QQ号:541433511 * 作用:把汉字转换成拼音 * 阿福 * AFU */ 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; } }
activity_main.xmlitem_main.xml<?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.example.mytongxunlu.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.example.mytongxunlu4.IndexView android:id="@+id/iv_words" android:layout_width="30dp" android:layout_height="match_parent" android:layout_alignParentRight="true" android:background="#ff0000" /> </RelativeLayout>
源码下载:<?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>
Myself ---- mytongxunlu4
http://download.csdn.net/download/zhaihaohao1/10111351
参考视频:
http://www.gulixueyuan.com/course/124/learn#lesson/1926