具体实现过程如下:
先在AndroidManifest.xml文件中添加读取联系人的权限:
1
联系人列表对应的适配器:
1 package com.example.testcontacts.adapter; 2 3 import java.util.List; 4 5 import android.content.Context; 6 import android.view.View; 7 import android.view.ViewGroup; 8 import android.widget.ArrayAdapter; 9 import android.widget.LinearLayout; 10 import android.widget.SectionIndexer; 11 import android.widget.TextView; 12 13 import com.example.testcontacts.R; 14 import com.example.testcontacts.domain.Contact; 15 16 public class ContactAdapter extends ArrayAdapter{ 17 private int resource;// 需要渲染的item布局文件 18 private SectionIndexer mIndexer;// 字母表分组工具 19 20 public ContactAdapter(Context context, int textViewResourceId,List objects) { 21 super(context, textViewResourceId, objects); 22 resource = textViewResourceId; 23 } 24 25 @Override 26 public View getView(int position, View convertView, ViewGroup parent) { 27 Contact contact = getItem(position); 28 View view = convertView == null ? View.inflate(getContext(), R.layout.contact_item, null) : convertView; 29 LinearLayout ll_sort_key = (LinearLayout) view.findViewById(R.id.ll_sort_key); 30 TextView tv_sort_key = (TextView) view.findViewById(R.id.tv_sort_key); 31 TextView tv_name = (TextView) view.findViewById(R.id.tv_name); 32 TextView tv_number = (TextView) view.findViewById(R.id.tv_number); 33 34 tv_name.setText(contact.getName()); 35 tv_number.setText(contact.getNumber()); 36 37 int section = mIndexer.getSectionForPosition(position); 38 if (position == mIndexer.getPositionForSection(section)) { 39 tv_sort_key.setText(contact.getSortKey()); 40 ll_sort_key.setVisibility(View.VISIBLE); 41 }else{ 42 ll_sort_key.setVisibility(View.GONE); 43 } 44 return view; 45 } 46 47 /**给当前适配器传入一个分组工具*/ 48 public void setIndexer(SectionIndexer indexer) { 49 mIndexer = indexer; 50 } 51 }
实体类:
1 package com.example.testcontacts.domain;
2
3 public class Contact {
4 private String name;// 联系人姓名
5 private String sortKey;// 排序字母
6 private String number;
7
8 public String getName() {
9 return name;
10 }
11
12 public void setName(String name) {
13 this.name = name;
14 }
15
16 public String getSortKey() {
17 return sortKey;
18 }
19
20 public void setSortKey(String sortKey) {
21 this.sortKey = sortKey;
22 }
23
24 public String getNumber() {
25 return number;
26 }
27
28 public void setNumber(String number) {
29 this.number = number;
30 }
31 }
java类:
1 package com.example.testcontacts;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import android.app.Activity;
7 import android.database.Cursor;
8 import android.net.Uri;
9 import android.os.Bundle;
10 import android.provider.ContactsContract;
11 import android.view.MotionEvent;
12 import android.view.View;
13 import android.view.View.OnTouchListener;
14 import android.view.ViewGroup.MarginLayoutParams;
15 import android.widget.AbsListView;
16 import android.widget.AbsListView.OnScrollListener;
17 import android.widget.AlphabetIndexer;
18 import android.widget.Button;
19 import android.widget.LinearLayout;
20 import android.widget.ListView;
21 import android.widget.RelativeLayout;
22 import android.widget.TextView;
23
24 import com.example.testcontacts.adapter.ContactAdapter;
25 import com.example.testcontacts.domain.Contact;
26
27 public class MainActivity extends Activity {
28 private LinearLayout ll_title;// 分组的布局
29 private RelativeLayout rl_toast;
30 private Button btn_alphabet;
31 private TextView tv_title; // 分组上显示的字母
32 private TextView tv_toast;
33 private ListView lv_contacts;// 联系人ListView
34
35 private ContactAdapter adapter;// 联系人列表适配器
36 private AlphabetIndexer indexer;// 用于进行字母表分组
37 private List contacts = new ArrayList();// 存储所有手机中的联系人
38 private String alphabet = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";// 定义字母表的排序规则
39 private int lastFirstVisibleItem = -1;// 上次第一个可见元素,用于滚动时记录标识。
40
41 @Override
42 protected void onCreate(Bundle savedInstanceState) {
43 super.onCreate(savedInstanceState);
44 setContentView(R.layout.activity_main);
45
46 adapter = new ContactAdapter(this, R.layout.contact_item, contacts);
47 ll_title = (LinearLayout) findViewById(R.id.ll_title);
48 rl_toast = (RelativeLayout) findViewById(R.id.rl_section_toast);
49 tv_title = (TextView) findViewById(R.id.tv_title);
50 tv_toast = (TextView) findViewById(R.id.tv_section_toast);
51 btn_alphabet = (Button) findViewById(R.id.btn_alphabet);
52 lv_contacts = (ListView) findViewById(R.id.lv_contacts);
53
54 Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
55 Cursor cursor = getContentResolver().query(uri,
56 new String[] { "display_name", "sort_key", }, null, null,
57 "sort_key");
58 if (cursor.moveToFirst()) {
59 do {
60 String name = cursor.getString(0);
61 String sortKey = getSortKey(cursor.getString(1));
62 Contact contact = new Contact();
63 contact.setName(name);
64 contact.setSortKey(sortKey);
65 contacts.add(contact);
66 } while (cursor.moveToNext());
67 }
68 startManagingCursor(cursor);
69 indexer = new AlphabetIndexer(cursor, 1, alphabet);
70 adapter.setIndexer(indexer);
71 if (contacts.size() > 0) {
72 setListViewListener();
73 setAlpabetListener();
74 }
75 }
76
77 /** 根据当前的滑动状态来改变分组的显示位置,从而实现挤压动画的效果 */
78 private void setListViewListener() {
79 lv_contacts.setAdapter(adapter);
80 lv_contacts.setOnScrollListener(new OnScrollListener() {
81 @Override
82 public void onScrollStateChanged(AbsListView view, int scrollState) {
83 }
84
85 @Override
86 public void onScroll(AbsListView view, int firstVisibleItem,
87 int visibleItemCount, int totalItemCount) {
88 int section = indexer.getSectionForPosition(firstVisibleItem);//当前分组所在的位置
89 int nextSecPosition = indexer.getPositionForSection(section + 1);//找出当前位置所在的分组
90 if (firstVisibleItem != lastFirstVisibleItem) {
91 MarginLayoutParams params = (MarginLayoutParams) ll_title
92 .getLayoutParams();
93 params.topMargin = 0;
94 ll_title.setLayoutParams(params);
95 tv_title.setText(String.valueOf(alphabet.charAt(section)));
96 }
97 if (nextSecPosition == firstVisibleItem + 1) {
98 View childView = view.getChildAt(0);
99 if (childView != null) {
100 int titleHeight = ll_title.getHeight();
101 int bottom = childView.getBottom();
102 MarginLayoutParams params = (MarginLayoutParams) ll_title
103 .getLayoutParams();
104 if (bottom < titleHeight) {
105 float pushedDistance = bottom - titleHeight;
106 params.topMargin = (int) pushedDistance;
107 ll_title.setLayoutParams(params);
108 } else {
109 if (params.topMargin != 0) {
110 params.topMargin = 0;
111 ll_title.setLayoutParams(params);
112 }
113 }
114 }
115 }
116 lastFirstVisibleItem = firstVisibleItem;
117 }
118 });
119 }
120
121 /**
122 * 获取sort key的首个字符,如果是英文字母就直接返回,否则返回#。
123 * @param sortKeyString 数据库中读取出的sort key
124 * @return 英文字母或者#
125 */
126 private String getSortKey(String sortKeyString) {
127 String key = sortKeyString.substring(0, 1).toUpperCase();
128 if (key.matches("[A-Z]")) {
129 return key;
130 }
131 return "#";
132 }
133
134 /**
135 * 设置字母表上的触摸事件,根据当前触摸的位置结合字母表的高度,计算出当前触摸在哪个字母上。
136 * 当手指按在字母表上时,展示弹出式分组。手指离开字母表时,将弹出式分组隐藏。
137 */
138 private void setAlpabetListener() {
139 btn_alphabet.setOnTouchListener(new OnTouchListener() {
140 @Override
141 public boolean onTouch(View v, MotionEvent event) {
142 float alphabetHeight = btn_alphabet.getHeight();//获得字母表的总高度
143 float y = event.getY();//获取到目前手指在字母表上的纵坐标
144 int sectionPosition = (int) ((y/alphabetHeight)/(1f/27f));//当前手指所在位置(0表在#端,1表示在Z端)。
145 if (sectionPosition < 0) {
146 sectionPosition = 0;
147 } else if (sectionPosition > 26) {
148 sectionPosition = 26;
149 }
150 String sectionLetter = String.valueOf(alphabet.charAt(sectionPosition));
151 int position = indexer.getPositionForSection(sectionPosition);
152 switch (event.getAction()) {
153 case MotionEvent.ACTION_DOWN://在弹出式分组上显示当前手指所按的字母
154 btn_alphabet.setBackgroundResource(R.drawable.contact_list_scroll_pressed);
155 rl_toast.setVisibility(View.VISIBLE);
156 tv_toast.setText(sectionLetter);
157 lv_contacts.setSelection(position);//把列表滚动到相应的分组
158 break;
159 case MotionEvent.ACTION_MOVE://同ACTION_DOWN
160 tv_toast.setText(sectionLetter);
161 lv_contacts.setSelection(position);
162 break;
163 default://弹出式分组布局隐藏
164 btn_alphabet.setBackgroundResource(R.drawable.contact_list_scroll_normal);
165 rl_toast.setVisibility(View.GONE);
166 }
167 return true;
168 }
169 });
170 }
171 }
样式:
1 23 4 9 10 14 15 16 17
布局文件:
在res/layout中新建一个布局文件,起名为:activity.main.xml
16 7 16 17 1824 25 34 35 41 4233 49 50 58 5957
在res/layout中新建一个布局文件,起名为:contact_item.xml
15 6 11 12 22 2321 27 28 62 6335 36 41 42 6150 51 60