这里主要参考了使用SectionIndexer实现微信通讯录的效果
在这里做个记录
效果图
页面使用RelativeLayout,主要分为三个部分,match_parent的主listView,右边字母的SideBar,还有就是微信那种点击字母时浮动的一个TextView
布局:
fragment_contacts.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent"> 6 <ListView 7 android:layout_width="match_parent" 8 android:layout_height="match_parent" 9 android:listSelector="@android:color/transparent" 10 android:id="@+id/listFragmentContacts"/> 11 <TextView 12 android:id="@+id/textFragmentContacts" 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:layout_centerInParent="true" 16 android:padding="20dp" 17 android:textSize="40sp" 18 android:textColor="@android:color/white" 19 android:background="#33000000" 20 android:visibility="gone" 21 tools:visibility="visible" 22 tools:text="A"/> 23 <lee.example.com.testdj.customWeight.ContactsMySideBar 24 android:id="@+id/sideBarFragmentContacts" 25 android:layout_width="24dp" 26 android:layout_height="match_parent" 27 android:layout_alignParentRight="true"/> 28 </RelativeLayout>
layout_contacts_item.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:layout_width="match_parent" 5 android:layout_height="wrap_content" 6 android:orientation="vertical"> 7 8 <!--section,表示组--> 9 <TextView 10 android:id="@+id/tvSectionContactsItem" 11 android:layout_width="match_parent" 12 android:layout_height="wrap_content" 13 android:gravity="left" 14 android:textSize="22sp" 15 android:textColor="#666666" 16 tools:text="section"/> 17 18 <TextView 19 android:id="@+id/tvNormalContactsItem" 20 android:layout_width="match_parent" 21 android:layout_height="wrap_content" 22 android:padding="6dp" 23 android:textColor="@android:color/black" 24 android:background="@android:color/white" 25 android:textSize="20sp" 26 tools:text="text"/> 27 </LinearLayout>
实现:
首先,自定义View来实现SideBar,ContactsMySideBar.java
1 public class ContactsMySideBar extends View{ 2 3 private static final String TAG = "ContactsMySideBar"; 4 5 private SectionIndexer sectionIndexer; 6 private final char[] letters = new char[]{‘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‘}; 7 private Paint paint; //画笔用来绘制字母 8 private ListView listView; 9 private int focusedIndex = -1; //点击选中的索引 10 private int drawWidth, drawHeight; //绘制单个字母的宽高 11 12 public interface OnTouchChangedListener{ 13 void onTouchDown(char c); 14 void onTouchUp(); 15 } 16 17 private OnTouchChangedListener listener; 18 19 public void setOnTouchChangedListener(OnTouchChangedListener listener){ 20 this.listener = listener; 21 } 22 23 public void setListView(ListView listView){ 24 this.listView = listView; 25 sectionIndexer = (SectionIndexer) listView.getAdapter(); 26 } 27 28 public ContactsMySideBar(Context context) { 29 super(context); 30 init(); 31 } 32 33 public ContactsMySideBar(Context context, AttributeSet attrs) { 34 super(context, attrs); 35 init(); 36 } 37 38 public ContactsMySideBar(Context context, AttributeSet attrs, int defStyle) { 39 super(context, attrs, defStyle); 40 init(); 41 } 42 43 private void init(){ 44 //设置画笔属性 45 paint = new Paint(); 46 paint.setColor(Color.GRAY); 47 paint.setTextSize(18f); 48 paint.setTextAlign(Paint.Align.CENTER); 49 } 50 51 @Override 52 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 53 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 54 drawWidth = getMeasuredWidth()/2; //宽度为sideBar的一半 55 drawHeight = getMeasuredHeight()/letters.length; //设置高度 56 } 57 58 @Override 59 protected void onDraw(Canvas canvas) { 60 for (int i = 0; i < letters.length; i++){ 61 canvas.drawText(String.valueOf(letters[i]), drawWidth, drawHeight + (i * drawHeight), paint); 62 } 63 } 64 65 @Override 66 public boolean onTouchEvent(MotionEvent event) { 67 int pointerY = (int) event.getY(); 68 int selectedIndex = pointerY / drawHeight; 69 if (selectedIndex >= letters.length){ 70 selectedIndex = letters.length - 1; 71 }else if(selectedIndex < 0){ 72 selectedIndex = 0; 73 } 74 75 switch (event.getAction()){ 76 case MotionEvent.ACTION_DOWN: 77 //点击时设置为半透明 78 setBackgroundColor(Color.parseColor("#33000000")); 79 case MotionEvent.ACTION_MOVE: 80 if (sectionIndexer == null){ 81 sectionIndexer = (SectionIndexer) listView.getAdapter(); 82 } 83 // Log.d(TAG, letters[selectedIndex] + ""); 84 //根据数组中的元素获取对应的组位置 85 int position = sectionIndexer.getPositionForSection(letters[selectedIndex]); 86 Log.d(TAG, "" + position); 87 if (position == -1){ 88 return true; 89 } 90 if (selectedIndex != 0){ 91 if (position != 0){ 92 //改变当前listView所处位置 93 listView.setSelection(position); 94 } 95 }else { 96 listView.setSelection(position); 97 } 98 99 //重绘SideBar 100 invalidate(); 101 if (null != listener){ 102 listener.onTouchDown(letters[selectedIndex]); 103 } 104 break; 105 case MotionEvent.ACTION_UP: 106 case MotionEvent.ACTION_CANCEL: 107 //松开手指取消背景色 108 setBackgroundResource(android.R.color.transparent); 109 invalidate(); 110 if (null != listener){ 111 listener.onTouchUp(); 112 } 113 break; 114 } 115 return true; 116 } 117 }
这部分代码很简单,重写了OnMeasure和OnDraw来绘制SideBar,在OnTouchEvent中写出了SideBar的响应事件
定义listView的adapter
SideBarAdapter.java
1 public class SideBarAdapter extends BaseAdapter implements SectionIndexer{ 2 3 private static final String TAG = "SideBarAdapter"; 4 5 private Context context; 6 private List<ContactsModel> modelList; 7 private LayoutInflater layoutInflater; 8 9 public SideBarAdapter(Context context, List<ContactsModel> modelList){ 10 this.context = context; 11 this.modelList = modelList; 12 layoutInflater = LayoutInflater.from(context); 13 } 14 15 @Override 16 public int getCount() { 17 return modelList.size(); 18 } 19 20 @Override 21 public ContactsModel getItem(int i) { 22 return modelList.get(i); 23 } 24 25 @Override 26 public long getItemId(int i) { 27 return i; 28 } 29 30 @Override 31 public View getView(int i, View convertView, ViewGroup viewGroup) { 32 Holder holder; 33 if (convertView == null){ 34 convertView = layoutInflater.inflate(R.layout.layout_contacts_item, viewGroup, false); 35 holder = new Holder(); 36 holder.normalTv = (TextView) convertView.findViewById(R.id.tvNormalContactsItem); 37 holder.sectionTv = (TextView) convertView.findViewById(R.id.tvSectionContactsItem); 38 convertView.setTag(holder); 39 }else { 40 holder = (Holder) convertView.getTag(); 41 } 42 String text = modelList.get(i).getName(); 43 setSectionTv(i, holder, text); 44 setNormalTv(holder, text); 45 46 return convertView; 47 } 48 49 private void setNormalTv(Holder holder, String text){ 50 holder.normalTv.setText(text); 51 } 52 53 private void setSectionTv(int position, Holder holder, String text){ 54 //获取每个item字符串的头一个字符 55 // Log.d(TAG, ZhCharactersUtil.changeToSpell(text)); 56 char firstChar = ZhCharactersUtil.changeToSpell(text).toUpperCase().charAt(0); 57 //若为第一个位置直接设置组view就行 58 if (position == 0) { 59 holder.sectionTv.setVisibility(View.VISIBLE); 60 holder.sectionTv.setText((firstChar + "").toUpperCase()); 61 } 62 //若不是,需判断当前item首字母与上一个item首字母是否一致,再设置组view 63 else { 64 String preLabel = modelList.get(position - 1).getName(); 65 //获取上一个item的首字母 66 char preFirstChar = ZhCharactersUtil.changeToSpell(preLabel).toUpperCase().charAt(0); 67 if (firstChar != preFirstChar) { 68 holder.sectionTv.setVisibility(View.VISIBLE); 69 holder.sectionTv.setText((firstChar + "").toUpperCase()); 70 } else { 71 //若与上一个item首字母一致则不需要重复设置组view 72 holder.sectionTv.setVisibility(View.GONE); 73 } 74 } 75 } 76 77 @Override 78 public Object[] getSections() { 79 //获取组信息的数组,比如这里可以返回char[]{‘A‘,‘B‘,...} 80 return new Object[0]; 81 } 82 83 @Override 84 public int getPositionForSection(int section) { 85 //根据组信息获取索引 86 for (int i = 0; i < modelList.size(); i++) { 87 String str = modelList.get(i).getName(); 88 char firstChar = ZhCharactersUtil.changeToSpell(str).toUpperCase().charAt(0); 89 if (firstChar == section) { 90 return i; 91 } 92 } 93 return 0; 94 } 95 96 @Override 97 public int getSectionForPosition(int i) { 98 //根据索引获取组信息,这里不做处理 99 return 0; 100 } 101 102 private final class Holder { 103 TextView normalTv, sectionTv; 104 } 105 }
这里用到了一个第三方,jpinyin,是用来对汉字进行处理,转换成拼音等等,ZhCharactersUtil.java就是用来处理汉字的,我这里只需要取拼音的第一个字母
1 public class ZhCharactersUtil { 2 public static String changeToSpell(String string){ 3 char[] chars = PinyinHelper.getShortPinyin(string).toCharArray(); 4 return chars[0] + ""; 5 } 6 7 }
这里的ContactsModel里边只有两个属性,一个name,一个firstCharacter,其中firstCharacter是用来进行排序的,上边adapter的代码可以用modelList.get(i).getFirstCharacter()来简化一些,偷懒了,所以开始写好了就放在那没动,这里这个model就不记录了。
在Activity中使用,我这里是在fragment中使用,都是一样的
1 private ListView listView; 2 //悬浮的textView 3 private TextView maskText; 4 private ContactsMySideBar sideBar; 5 6 //然后对他们进行初始化
1 private void initData(){ 2 ContactsModel model1 = new ContactsModel("张一"); 3 ContactsModel model2 = new ContactsModel("张二"); 4 ContactsModel model3 = new ContactsModel("李一"); 5 ContactsModel model4 = new ContactsModel("李二"); 6 ContactsModel model5 = new ContactsModel("赵一"); 7 ContactsModel model6 = new ContactsModel("赵二"); 8 ContactsModel model7 = new ContactsModel("王三"); 9 ContactsModel model8 = new ContactsModel("王二"); 10 ContactsModel model9 = new ContactsModel("阿张"); 11 ContactsModel model10 = new ContactsModel("不行"); 12 ContactsModel model11 = new ContactsModel("特别"); 13 ContactsModel model12 = new ContactsModel("将就"); 14 ContactsModel model13 = new ContactsModel("公司"); 15 ContactsModel model14 = new ContactsModel("空是"); 16 ContactsModel model15 = new ContactsModel("好附件打开"); 17 ContactsModel model16 = new ContactsModel("现在"); 18 ContactsModel model17 = new ContactsModel("哦跑"); 19 20 ContactsModel model18 = new ContactsModel("aaasss"); 21 ContactsModel model19 = new ContactsModel("bbb"); 22 ContactsModel model20 = new ContactsModel("s"); 23 ContactsModel model21 = new ContactsModel("jjj"); 24 ContactsModel model22 = new ContactsModel("oot"); 25 26 modelList.add(model1); 27 modelList.add(model2); 28 modelList.add(model3); 29 modelList.add(model4); 30 modelList.add(model5); 31 modelList.add(model6); 32 modelList.add(model7); 33 modelList.add(model8); 34 modelList.add(model9); 35 modelList.add(model10); 36 modelList.add(model11); 37 modelList.add(model12); 38 modelList.add(model13); 39 modelList.add(model14); 40 modelList.add(model15); 41 modelList.add(model16); 42 modelList.add(model17); 43 // modelList.add(model18); 44 // modelList.add(model19); 45 // modelList.add(model20); 46 // modelList.add(model21); 47 // modelList.add(model22); 48 Collections.sort(modelList, new Comparator<ContactsModel>() { 49 @Override 50 public int compare(ContactsModel contactsModel, ContactsModel t1) { 51 return contactsModel.getFirstCharacter().compareTo(t1.getFirstCharacter()); 52 } 53 }); 54 55 adapter = new SideBarAdapter(getContext(), modelList); 56 listView.setAdapter(adapter); 57 sideBar.setListView(listView); 58 sideBar.setOnTouchChangedListener(new ContactsMySideBar.OnTouchChangedListener() { 59 @Override 60 public void onTouchDown(char c) { 61 maskText.setText(c + ""); 62 maskText.setVisibility(View.VISIBLE); 63 } 64 65 @Override 66 public void onTouchUp() { 67 maskText.setVisibility(View.GONE); 68 } 69 }); 70 }
这里添加了一些假数据,完成。