app只要涉及到联系人的界面,几乎都是按照字母排序以及导航栏的方式。既然这个需求这么火,于是开始学习相关内容,此篇文章是我通过参考网上资料独立编写和总结的,希望多多少少对大家有所帮助,写的不好,还请各位朋友指教。
效果图如下:
实现这个效果,需要三个知识点 :
1:将字符串 进行拼音分类
2:expandablelistview 二级扩展列表
3:右边字母分类view
我们先一个一个来了解解决方案,再上代码。
实现字母分类:
字母分类又分为三个小要点:一个是将中文转化为拼音,一个是实现按照字母的顺序排序,另一个是字母只显示在具有相同首字母中文的第一个前面。
1、将中文转化为拼音,这里使用了一个工具包,即pinyin4j-2.5.0.jar。官网地址:http://pinyin4j.sourceforge.net/
点击下载,导入项目即可。(至于教程,网上很多)
在这里我们只需要使用将中文转换成拼音的代码即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
pinyinutils.java
public static string getpingyin(string inputstring) {
hanyupinyinoutputformat format = new hanyupinyinoutputformat();
format.setcasetype(hanyupinyincasetype.lowercase);
format.settonetype(hanyupinyintonetype.without_tone);
format.setvchartype(hanyupinyinvchartype.with_v);
char [] input = inputstring.trim().tochararray();
string output = "" ;
try {
for ( char curchar : input) {
if (java.lang.character.tostring(curchar).matches( "[\\u4e00-\\u9fa5]+" )) {
string[] temp = pinyinhelper.tohanyupinyinstringarray(curchar, format);
output += temp[ 0 ];
} else
output += java.lang.character.tostring(curchar);
}
} catch (badhanyupinyinoutputformatcombination e) {
e.printstacktrace();
}
return output;
}
|
2、实现按照字母的顺序排序,使用的是java自带的comparator接口,利用之前获取到的中文拼音,得到首字母并根据ascii值来实现排序。
1
2
3
4
5
6
7
8
9
10
11
12
|
private int sort(personbean lhs, personbean rhs) {
// 获取ascii值
int lhs_ascii = lhs.getfirstpinyin().touppercase().charat( 0 );
int rhs_ascii = rhs.getfirstpinyin().touppercase().charat( 0 );
// 判断若不是字母,则排在字母之后
if (lhs_ascii < 65 || lhs_ascii > 90 )
return 1 ;
else if (rhs_ascii < 65 || rhs_ascii > 90 )
return - 1 ;
else
return lhs.getpinyin().compareto(rhs.getpinyin());
}
|
3、字母只显示在具有相同首字母中文的第一个前面。这里算是一个小技巧,这里字母显示的布局与中文名字的布局都是存放在listview的item的布局中的。
item的布局如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
<?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就是显示字母的 -->
<textview
android:id= "@+id/tv_lv_item_tag"
android:layout_width= "match_parent"
android:layout_height= "20dp"
android:background= "#e6e6e6"
android:gravity= "center_vertical"
android:paddingleft= "10dip"
android:text= "z"
android:visibility= "visible" />
<relativelayout
android:layout_width= "match_parent"
android:layout_height= "wrap_content" >
<view
android:id= "@+id/view_lv_item_line"
android:layout_width= "match_parent"
android:layout_height= "0.5dip"
android:background= "#174465"
android:layout_marginleft= "10dip"
android:layout_marginright= "20dip"
/>
<imageview
android:id= "@+id/iv_lv_item_head"
android:layout_width= "wrap_content"
android:layout_height= "wrap_content"
android:src= "@drawable/ic_launcher"
android:layout_below= "@id/view_lv_item_line"
android:layout_marginleft= "5dp" />
<textview
android:id= "@+id/tv_lv_item_name"
android:layout_width= "wrap_content"
android:layout_height= "wrap_content"
android:layout_centervertical= "true"
android:layout_torightof= "@id/iv_lv_item_head"
android:layout_marginleft= "5dip"
android:text= "周华健" />
</relativelayout>
</linearlayout>
|
而判断是否需要显示字母,是通过判断当前item的position是否等于第一个出现item对应的中文首字母的索引。
如果相等,则说明是第一次出现,便需要显示字母,否则不显示字母。而这样的判断,有一个前提,那就是中文拼音的排序必须是按照字母顺序排序的,这就是我们在上一步排序的必要。
实现右侧的字母导航:
右侧的字母导航,其本质就是一个自定义view。除了绘制界面之外,需要注意的就是触摸事件的处理,还有回调机制(这个很多地方都会用到)的使用。这个比较重要,直接上代码吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
package com.suse.contact;
import android.content.context;
import android.graphics.canvas;
import android.graphics.color;
import android.graphics.paint;
import android.graphics.typeface;
import android.graphics.drawable.colordrawable;
import android.util.attributeset;
import android.view.motionevent;
import android.view.view;
import android.widget.textview;
public class sidebar extends view {
// 触摸事件
private ontouchingletterchangedlistener ontouchingletterchangedlistener;
// 26个字母
public static string[] a_z = { "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 int choose = - 1 ; // 选中
private paint paint = new paint();
private textview mtextdialog;
/**
* 为sidebar设置显示字母的textview
* @param mtextdialog
*/
public void settextview(textview mtextdialog) {
this .mtextdialog = mtextdialog;
}
public sidebar(context context, attributeset attrs, int defstyle) {
super (context, attrs, defstyle);
}
public sidebar(context context, attributeset attrs) {
super (context, attrs);
}
public sidebar(context context) {
super (context);
}
/**
* 重写这个方法
*/
protected void ondraw(canvas canvas) {
super .ondraw(canvas);
// 获取焦点改变背景颜色.
int height = getheight(); // 获取对应高度
int width = getwidth(); // 获取对应宽度
int singleheight = height / a_z.length- 2 ; // 获取每一个字母的高度 (这里-2仅仅是为了好看而已)
for ( int i = 0 ; i < a_z.length; i++) {
paint.setcolor(color.rgb( 33 , 65 , 98 )); //设置字体颜色
paint.settypeface(typeface.default_bold); //设置字体
paint.setantialias( true ); //设置抗锯齿
paint.settextsize( 30 ); //设置字母字体大小
// 选中的状态
if (i == choose) {
paint.setcolor(color.parsecolor( "#3399ff" )); //选中的字母改变颜色
paint.setfakeboldtext( true ); //设置字体为粗体
}
// x坐标等于中间-字符串宽度的一半.
float xpos = width / 2 - paint.measuretext(a_z[i]) / 2 ;
float ypos = singleheight * i + singleheight;
canvas.drawtext(a_z[i], xpos, ypos, paint); //绘制所有的字母
paint.reset(); // 重置画笔
}
}
@override
public boolean dispatchtouchevent(motionevent event) {
final int action = event.getaction();
final float y = event.gety(); // 点击y坐标
final int oldchoose = choose;
final ontouchingletterchangedlistener listener = ontouchingletterchangedlistener;
final int c = ( int ) (y / getheight() * a_z.length); // 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数.
switch (action) {
case motionevent.action_up:
setbackgrounddrawable( new colordrawable( 0x00000000 ));
choose = - 1 ; //
invalidate();
if (mtextdialog != null ) {
mtextdialog.setvisibility(view.invisible);
}
break ;
default :
setbackgroundresource(r.drawable.sidebar_background);
if (oldchoose != c) { //判断选中字母是否发生改变
if (c >= 0 && c < a_z.length) {
if (listener != null ) {
listener.ontouchingletterchanged(a_z[c]);
}
if (mtextdialog != null ) {
mtextdialog.settext(a_z[c]);
mtextdialog.setvisibility(view.visible);
}
choose = c;
invalidate();
}
}
break ;
}
return true ;
}
/**
* 向外公开的方法
*
* @param ontouchingletterchangedlistener
*/
public void setontouchingletterchangedlistener(
ontouchingletterchangedlistener ontouchingletterchangedlistener) {
this .ontouchingletterchangedlistener = ontouchingletterchangedlistener;
}
/**
* 接口
*
* @author coder
*
*/
public interface ontouchingletterchangedlistener {
public void ontouchingletterchanged(string s);
}
}
|
接下来就是mainactivity和sortadapter的代码了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
mainactivity.java:
package com.suse.contact;
import java.util.arraylist;
import java.util.collections;
import java.util.list;
import android.app.activity;
import android.os.bundle;
import android.widget.listview;
import android.widget.textview;
import com.suse.contact.sidebar.ontouchingletterchangedlistener;
/**
*
* @classname: mainactivity
* @description: todo(这里用一句话描述这个类的作用)
* @author 银色的流星 欢迎批评、指导、交流 qq:962455668
*/
public class mainactivity extends activity {
private listview listview;
private sortadapter sortadapter;
private list<personbean> data;
private sidebar sidebar;
private textview dialog;
@override
protected void oncreate(bundle savedinstancestate) {
super .oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
init();
}
private list<personbean> getdata(string[] data) {
list<personbean> listarray = new arraylist<personbean>();
for ( int i = 0 ; i < data.length; i++) {
string pinyin = pinyinutils.getpingyin(data[i]);
string fpinyin = pinyin.substring( 0 , 1 ).touppercase();
personbean person = new personbean();
person.setname(data[i]);
person.setpinyin(pinyin);
// 正则表达式,判断首字母是否是英文字母
if (fpinyin.matches( "[a-z]" )) {
person.setfirstpinyin(fpinyin);
} else {
person.setfirstpinyin( "#" );
}
listarray.add(person);
}
return listarray;
}
private void init() {
// todo auto-generated method stub
sidebar = (sidebar) findviewbyid(r.id.sidebar);
listview = (listview) findviewbyid(r.id.listview);
dialog = (textview) findviewbyid(r.id.dialog);
sidebar.settextview(dialog);
// 设置字母导航触摸监听
sidebar.setontouchingletterchangedlistener( new ontouchingletterchangedlistener() {
@override
public void ontouchingletterchanged(string s) {
// todo auto-generated method stub
// 该字母首次出现的位置
int position = sortadapter.getpositionforselection(s.charat( 0 ));
if (position != - 1 ) {
listview.setselection(position);
}
}
});
data = getdata(getresources().getstringarray(r.array.listpersons));
// 数据在放在adapter之前需要排序
collections.sort(data, new pinyincomparator());
sortadapter = new sortadapter( this , data);
listview.setadapter(sortadapter);
}
}
|
sortadapter.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
package com.suse.contact;
import java.util.list;
import android.content.context;
import android.view.layoutinflater;
import android.view.view;
import android.view.viewgroup;
import android.widget.baseadapter;
import android.widget.textview;
public class sortadapter extends baseadapter {
private context context;
private list<personbean> persons;
private layoutinflater inflater;
public sortadapter(context context, list<personbean> persons) {
this .context = context;
this .persons = persons;
this .inflater = layoutinflater.from(context);
}
@override
public int getcount() {
// todo auto-generated method stub
return persons.size();
}
@override
public object getitem( int position) {
// todo auto-generated method stub
return persons.get(position);
}
@override
public long getitemid( int position) {
// todo auto-generated method stub
return position;
}
@override
public view getview( int position, view convertview, viewgroup parent) {
viewholder viewholder = null ;
personbean person = persons.get(position);
if (convertview == null ) {
viewholder = new viewholder();
convertview = inflater.inflate(r.layout.list_item, null );
viewholder.tv_tag = (textview) convertview
.findviewbyid(r.id.tv_lv_item_tag);
viewholder.tv_name = (textview) convertview
.findviewbyid(r.id.tv_lv_item_name);
convertview.settag(viewholder);
} else {
viewholder = (viewholder) convertview.gettag();
}
// 获取首字母的assii值
int selection = person.getfirstpinyin().charat( 0 );
// 通过首字母的assii值来判断是否显示字母
int positionforselection = getpositionforselection(selection);
if (position == positionforselection) { // 相等说明需要显示字母
viewholder.tv_tag.setvisibility(view.visible);
viewholder.tv_tag.settext(person.getfirstpinyin());
} else {
viewholder.tv_tag.setvisibility(view.gone);
}
viewholder.tv_name.settext(person.getname());
return convertview;
}
public int getpositionforselection( int selection) {
for ( int i = 0 ; i < persons.size(); i++) {
string fpinyin = persons.get(i).getfirstpinyin();
char first = fpinyin.touppercase().charat( 0 );
if (first == selection) {
return i;
}
}
return - 1 ;
}
class viewholder {
textview tv_tag;
textview tv_name;
}
}
|
虽然不全,但比较重要的代码都已经贴上去了,希望对大家有所帮助