相信大家对这个列表字母索引已经不陌生了,在很多app中也随处可见,像没团的城市地址选择,微信联系人列表,手机通讯录…等等。既然是个这么nb这么实用的功能我们怎么能不Get到来呢,下面就让我们一起造一个出来吧
一:我们可以大致将他分成3小块,右边的字母列表、中央的当前字母提示、ListView列表。ok分析好了那我们就一步步来编码实现吧
二:首先来实现右边的字母列表
1. 在画这个字母列表之前,先画张图来大致计算一下字母的坐标,如下图:
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", "#"};
private Paint wordsPaint;
private Paint bgPaint;
private int itemWidth;
private int itemHeight;
private int touchIndex = 0;
private onWordsChangeListener listener;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
itemWidth = getMeasuredWidth();
int height = getMeasuredHeight() - 10;
itemHeight = height / 27;
}
2. 开始绘制A~Z~#的字符,先绘制字母背景,在绘制文字
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < words.length; i++) {
if (touchIndex == i) {
canvas.drawCircle(itemWidth / 2, itemHeight / 2 + i * itemHeight, 23, bgPaint);
wordsPaint.setColor(Color.WHITE);
} else {
wordsPaint.setColor(Color.GRAY);
}
Rect rect = new Rect();
wordsPaint.getTextBounds(words[i], 0, 1, rect);
int wordWidth = rect.width();
float wordX = itemWidth / 2 - wordWidth / 2;
float wordY = itemWidth / 2 + i * itemHeight;
canvas.drawText(words[i], wordX, wordY, wordsPaint);
}
}
3.现在效果就是这个样子了
4. 现在来实现手指滑动或者点击字母列表的时候来改变当前选中的字母和在屏幕中央进行显示。这里怎么实现呢?很容易就想到这里肯定是在onTouchEvent
中做处理,在使用接口回调来在屏幕中央显示当前字母
/**
* 当手指触摸按下的时候改变字母背景颜色
*/
@Override
public boolean onTouchEvent(MotionEvent 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;
if (listener != null && 0 <= touchIndex && touchIndex <= words.length - 1) {
listener.wordsChange(words[touchIndex]);
}
invalidate();
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
public interface onWordsChangeListener {
void wordsChange(String words);
}
public void setOnWordsChangeListener(onWordsChangeListener listener) {
this.listener = listener;
}
5.接口都写好了就可以在主界面中来显示了,activity_main
的布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none" />
<com.zsy.words.view.WordsNavigation
android:id="@+id/words"
android:layout_width="30dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true" />
<TextView
android:id="@+id/tv"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="@drawable/tvstyle"
android:gravity="center"
android:textSize="40sp"
android:visibility="gone" />
RelativeLayout
6.MainActivity
中设置字母改变监听
tv = (TextView) findViewById(R.id.tv);
word = (WordsNavigation) findViewById(R.id.words);
word.setOnWordsChangeListener(this);
@Override
public void wordsChange(String words) {
updateWord(words);
}
/**
* 更新中央的字母提示
*
* @param words 首字母
*/
private void updateWord(String words) {
tv.setText(words);
tv.setVisibility(View.VISIBLE);
handler.removeCallbacksAndMessages(null);
handler.postDelayed(new Runnable() {
@Override
public void run() {
tv.setVisibility(View.GONE);
}
}, 500);
}
三:现在就只剩下ListView列表,和滑动列表来改变字母的背景了同时滑动列表来改变我们listView的数据显示
1.为ListView数据添加一个实体类Person
,这里我们需要对我们的姓名转化成拼音,我这里使用的是pinyin4j-2.5.0.jar
链接文章末尾给出
public class Person {
private String name;
private String pinyin;
private String headerWord;
public Person(String name) {
this.name = name;
this.pinyin = PinYinUtils.getPinyin(name);
headerWord = pinyin.substring(0, 1);
}
public String getPinyin() {
return pinyin;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHeaderWord() {
return headerWord;
}
}
2.PinYinUtils将汉子转拼音
public class PinYinUtils {
/**
* 将hanzi转成拼音
*
* @param hanzi 汉字或字母
* @return 拼音
*/
public static String getPinyin(String hanzi) {
StringBuilder sb = new StringBuilder();
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;
}
try {
String[] pinyinArr = PinyinHelper.toHanyuPinyinStringArray(arr[i], format);
if (pinyinArr != null) {
sb.append(pinyinArr[0]);
} else {
sb.append(arr[i]);
}
} catch (Exception e) {
e.printStackTrace();
sb.append(arr[i]);
}
}
return sb.toString();
}
}
3.现在我们就来撸ListView的Item布局了list_item.xml
<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_word"
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="#ebebeb"
android:gravity="center_vertical"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:textAppearance="?android:attr/textAppearanceListItemSmall" />
LinearLayout>
5.为我们的列表添加测试数据,并对数据进行排序
/**
* 初始化联系人列表信息
*/
private void initData() {
list = new ArrayList<>();
list.add(new Person("Dave"));
list.add(new Person("阿钟"));
list.add(new Person("胡继群"));
list.add(new Person("隔壁老王"));
list.add(new Person("姜宇航"));
Collections.sort(list, new Comparator() {
@Override
public int compare(Person lhs, Person rhs) {
return lhs.getPinyin().compareTo(rhs.getPinyin());
}
});
}
6.撸ListView的列表适配器
public class MyAdapter extends BaseAdapter {
private List list;
private LayoutInflater inflater;
public MyAdapter(Context context, List list) {
inflater = LayoutInflater.from(context);
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.list_item, null);
holder.tv_word = (TextView) convertView.findViewById(R.id.tv_word);
holder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
String word = list.get(position).getHeaderWord();
holder.tv_word.setText(word);
holder.tv_name.setText(list.get(position).getName());
if (position == 0) {
holder.tv_word.setVisibility(View.VISIBLE);
} else {
String headerWord = list.get(position - 1).getHeaderWord();
if (word.equals(headerWord)) {
holder.tv_word.setVisibility(View.GONE);
} else {
holder.tv_word.setVisibility(View.VISIBLE);
}
}
return convertView;
}
private class ViewHolder {
private TextView tv_word;
private TextView tv_name;
}
}
7.现在我们需要来实现,当滑动列表的时候需要更新ListView的显示,在wordsChange
函数中进行出理,同时滑动列表我们也需要更新右侧字母列表的状态,ok逻辑理清了就好办事了。
1.在wordsChange
调用此函数updateListView
改变ListView的显示
/**
* @param words 首字母
*/
private void updateListView(String words) {
for (int i = 0; i < list.size(); i++) {
String headerWord = list.get(i).getHeaderWord();
if (words.equals(headerWord)) {
listView.setSelection(i);
return;
}
}
}
2.为ListView设置滑动监听,来改变右侧字母列表的状态
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
word.setTouchIndex(list.get(firstVisibleItem).getHeaderWord());
}
3.自定义字母列表View中的函数,这样当我们滑动列表的时候字母状态也就可以时时同步了
public void setTouchIndex(String word) {
for (int i = 0; i < words.length; i++) {
if (words[i].equals(word)) {
touchIndex = i;
invalidate();
return;
}
}
}
ok大功告成,一个简单的联系字母索引列表就实现了。Demo下载地址,汉子转拼音jar包下载如果有什么疑问可以联系我哦