首先先看一下效果图再说吧:
具体实现过程如下:
先在AndroidManifest.xml文件中添加读取联系人的权限:
1 <uses-permission android:name="android.permission.READ_CONTACTS" />
联系人列表对应的适配器:
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<Contact> { 17 private int resource;// 需要渲染的item布局文件 18 private SectionIndexer mIndexer;// 字母表分组工具 19 20 public ContactAdapter(Context context, int textViewResourceId,List<Contact> 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<Contact> contacts = new ArrayList<Contact>();// 存储所有手机中的联系人 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 <?xml version="1.0" encoding="utf-8"?> 2 <shape xmlns:android="http://schemas.android.com/apk/res/android" > 3 4 <corners 5 android:bottomLeftRadius="0dp" 6 android:bottomRightRadius="0dp" 7 android:topLeftRadius="0dp" 8 android:topRightRadius="0dp" /> 9 10 <gradient 11 android:angle="90" 12 android:endColor="#8BC4DF" 13 android:startColor="#8BC4DF" /> 14 15 <stroke android:width="1dp" /> 16 17 </shape>
布局文件:
在res/layout中新建一个布局文件,起名为:activity.main.xml
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 <ListView 8 android:id="@+id/lv_contacts" 9 android:layout_width="fill_parent" 10 android:layout_height="wrap_content" 11 android:layout_alignParentTop="true" 12 android:divider="@drawable/divider1" 13 android:dividerHeight="1dp" 14 android:fadingEdge="none" 15 android:scrollbars="none" > 16 </ListView> 17 18 <LinearLayout 19 android:id="@+id/ll_title" 20 android:layout_width="fill_parent" 21 android:layout_height="18dp" 22 android:layout_alignParentTop="true" 23 android:background="@drawable/itembg" > 24 25 <TextView 26 android:id="@+id/tv_title" 27 android:layout_width="wrap_content" 28 android:layout_height="wrap_content" 29 android:layout_gravity="center_horizontal" 30 android:layout_marginLeft="10dp" 31 android:textColor="#000000" 32 android:textSize="14sp" /> 33 </LinearLayout> 34 35 <Button 36 android:id="@+id/btn_alphabet" 37 android:layout_width="wrap_content" 38 android:layout_height="fill_parent" 39 android:layout_alignParentRight="true" 40 android:background="@drawable/contact_list_scroll_normal" /> 41 42 <RelativeLayout 43 android:id="@+id/rl_section_toast" 44 android:layout_width="70dp" 45 android:layout_height="70dp" 46 android:layout_centerInParent="true" 47 android:background="@drawable/section_toast" 48 android:visibility="gone" > 49 50 <TextView 51 android:id="@+id/tv_section_toast" 52 android:layout_width="wrap_content" 53 android:layout_height="wrap_content" 54 android:layout_centerInParent="true" 55 android:textColor="#000" 56 android:textSize="30sp" /> 57 </RelativeLayout> 58 59 </RelativeLayout>
在res/layout中新建一个布局文件,起名为:contact_item.xml
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical" > 5 6 <LinearLayout 7 android:id="@+id/ll_sort_key" 8 android:layout_width="fill_parent" 9 android:layout_height="18dip" 10 android:background="@drawable/itembg" > 11 12 <TextView 13 android:id="@+id/tv_sort_key" 14 android:layout_width="wrap_content" 15 android:layout_height="wrap_content" 16 android:layout_gravity="center_horizontal" 17 android:layout_marginLeft="10dip" 18 android:text="A" 19 android:textColor="#000000" 20 android:textSize="14sp" /> 21 </LinearLayout> 22 23 <LinearLayout 24 android:id="@+id/ll_name" 25 android:layout_width="fill_parent" 26 android:layout_height="50dip" > 27 28 <ImageView 29 android:layout_width="40dp" 30 android:layout_height="40dp" 31 android:layout_gravity="center_vertical" 32 android:layout_marginLeft="10dip" 33 android:layout_marginRight="10dip" 34 android:src="@drawable/icon" /> 35 36 <LinearLayout 37 android:layout_width="fill_parent" 38 android:layout_height="fill_parent" 39 android:layout_gravity="top" 40 android:orientation="vertical" > 41 42 <TextView 43 android:id="@+id/tv_name" 44 android:layout_width="wrap_content" 45 android:layout_height="wrap_content" 46 android:layout_gravity="center_vertical" 47 android:text="张三" 48 android:textColor="#000000" 49 android:textSize="18sp" /> 50 51 <TextView 52 android:id="@+id/tv_number" 53 android:layout_width="wrap_content" 54 android:layout_height="wrap_content" 55 android:layout_gravity="center_vertical" 56 android:layout_marginTop="10dp" 57 android:text="186057986" 58 android:textColor="#88000000" 59 android:textSize="12sp" /> 60 </LinearLayout> 61 </LinearLayout> 62 63 </LinearLayout>