Android仿微信通讯录功能,好友排序+字母索引

一、效果图展示



二、功能特点

1.好友排序:按照拼音顺序对好友进行排序,兼容英文数字符号等

2.字母索引:右侧字母导航条,既可拖动也可点击,联动ListView滑动


三、实现

接下来就让我们一步步显示这个效果吧。

1.右侧字母索引的导航条

这个我们可以在网上找到很多类似的,你大可找一个自己喜欢的甚至自己写一个出来,这里我在网上找了一个带波浪效果的,看起来比较炫酷一点吧。

这是原地址:https://github.com/AlexLiuSheng/AnimSideBar

然后我把它导入到了我们项目中并修改了部分代码,以下是我项目中的SideBar.Java

[java]   view plain  copy
  1. package com.afei.indexlistview;  
  2.   
  3. import android.content.Context;  
  4. import android.content.res.TypedArray;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.Paint;  
  7. import android.util.AttributeSet;  
  8. import android.view.MotionEvent;  
  9. import android.widget.TextView;  
  10.   
  11. public class SideBar extends TextView {  
  12.     private String[] letters = new String[]{“A”“B”“C”“D”“E”“F”“G”“H”“I”,  
  13.             ”J”“K”“L”“M”“N”“O”“P”“Q”“R”“S”“T”“U”“V”,  
  14.             ”W”“X”“Y”“Z”“#”};  
  15.     private Paint textPaint;  
  16.     private Paint bigTextPaint;  
  17.     private Paint scaleTextPaint;  
  18.   
  19.     private Canvas canvas;  
  20.     private int itemH;  
  21.     private int w;  
  22.     private int h;  
  23.     /** 
  24.      * 普通情况下字体大小 
  25.      */  
  26.     float singleTextH;  
  27.     /** 
  28.      * 缩放离原始的宽度 
  29.      */  
  30.     private float scaleWidth;  
  31.     /** 
  32.      * 滑动的Y 
  33.      */  
  34.     private float eventY = 0;  
  35.     /** 
  36.      * 缩放的倍数 
  37.      */  
  38.     private int scaleSize = 1;  
  39.     /** 
  40.      * 缩放个数item,即开口大小 
  41.      */  
  42.     private int scaleItemCount = 6;  
  43.     private ISideBarSelectCallBack callBack;  
  44.   
  45.     public SideBar(Context context) {  
  46.         this(context, );  
  47.     }  
  48.   
  49.     public SideBar(Context context, AttributeSet attrs) {  
  50.         this(context, attrs, 0);  
  51.     }  
  52.   
  53.     public SideBar(Context context, AttributeSet attrs, int defStyleAttr) {  
  54.         super(context, attrs, defStyleAttr);  
  55.         init(attrs);  
  56.     }  
  57.   
  58.     private void init(AttributeSet attrs) {  
  59.         if (attrs != ) {  
  60.             TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SideBar);  
  61.             scaleSize = ta.getInteger(R.styleable.SideBar_scaleSize, 1);  
  62.             scaleItemCount = ta.getInteger(R.styleable.SideBar_scaleItemCount, 6);  
  63.             scaleWidth = ta.getDimensionPixelSize(R.styleable.SideBar_scaleWidth, dp(100));  
  64.             ta.recycle();  
  65.         }  
  66.         textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  67.         textPaint.setColor(getCurrentTextColor());  
  68.         textPaint.setTextSize(getTextSize());  
  69.         textPaint.setTextAlign(Paint.Align.CENTER);  
  70.         bigTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  71.         bigTextPaint.setColor(getCurrentTextColor());  
  72.         bigTextPaint.setTextSize(getTextSize() * (scaleSize + 3));  
  73.         bigTextPaint.setTextAlign(Paint.Align.CENTER);  
  74.         scaleTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  75.         scaleTextPaint.setColor(getCurrentTextColor());  
  76.         scaleTextPaint.setTextSize(getTextSize() * (scaleSize + 1));  
  77.         scaleTextPaint.setTextAlign(Paint.Align.CENTER);  
  78.     }  
  79.   
  80.     public void setDataResource(String[] data) {  
  81.         letters = data;  
  82.         invalidate();  
  83.     }  
  84.   
  85.     public void setOnStrSelectCallBack(ISideBarSelectCallBack callBack) {  
  86.         this.callBack = callBack;  
  87.     }  
  88.   
  89.     /** 
  90.      * 设置字体缩放比例 
  91.      * 
  92.      * @param scale 
  93.      */  
  94.     public void setScaleSize(int scale) {  
  95.         scaleSize = scale;  
  96.         invalidate();  
  97.     }  
  98.   
  99.     /** 
  100.      * 设置缩放字体的个数,即开口大小 
  101.      * 
  102.      * @param scaleItemCount 
  103.      */  
  104.     public void setScaleItemCount(int scaleItemCount) {  
  105.         this.scaleItemCount = scaleItemCount;  
  106.         invalidate();  
  107.     }  
  108.   
  109.     private int dp(int px) {  
  110.         final float scale = getContext().getResources().getDisplayMetrics().density;  
  111.         return (int) (px * scale + 0.5f);  
  112.     }  
  113.   
  114.     @Override  
  115.     public boolean onTouchEvent(MotionEvent event) {  
  116.         switch (event.getAction()) {  
  117.             case MotionEvent.ACTION_DOWN:  
  118.             case MotionEvent.ACTION_MOVE:  
  119.                 if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {  
  120.                     eventY = event.getY();  
  121.                     invalidate();  
  122.                     return true;  
  123.                 } else {  
  124.                     eventY = 0;  
  125.                     invalidate();  
  126.                     break;  
  127.                 }  
  128.             case MotionEvent.ACTION_CANCEL:  
  129.                 eventY = 0;  
  130.                 invalidate();  
  131.                 return true;  
  132.             case MotionEvent.ACTION_UP:  
  133.                 if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {  
  134.                     eventY = 0;  
  135.                     invalidate();  
  136.                     return true;  
  137.                 } else  
  138.                     break;  
  139.         }  
  140.         return super.onTouchEvent(event);  
  141.     }  
  142.   
  143.   
  144.     @Override  
  145.     protected void onDraw(Canvas canvas) {  
  146.         this.canvas = canvas;  
  147.         DrawView(eventY);  
  148.     }  
  149.   
  150.     private void DrawView(float y) {  
  151.         int currentSelectIndex = -1;  
  152.         if (y != 0) {  
  153.             for (int i = 0; i < letters.length; i++) {  
  154.                 float currentItemY = itemH * i;  
  155.                 float nextItemY = itemH * (i + 1);  
  156.                 if (y >= currentItemY && y < nextItemY) {  
  157.                     currentSelectIndex = i;  
  158.                     if (callBack != ) {  
  159.                         callBack.onSelectStr(currentSelectIndex, letters[i]);  
  160.                     }  
  161.                     //画大的字母  
  162.                     Paint.FontMetrics fontMetrics = bigTextPaint.getFontMetrics();  
  163.                     float bigTextSize = fontMetrics.descent - fontMetrics.ascent;  
  164.                     canvas.drawText(letters[i], w - getPaddingRight() - scaleWidth - bigTextSize, singleTextH + itemH * i, bigTextPaint);  
  165.                 }  
  166.             }  
  167.         }  
  168.         drawLetters(y, currentSelectIndex);  
  169.     }  
  170.   
  171.     private void drawLetters(float y, int index) {  
  172.         //第一次进来没有缩放情况,默认画原图  
  173.         if (index == -1) {  
  174.             w = getMeasuredWidth();  
  175.             h = getMeasuredHeight();  
  176.             itemH = h / letters.length;  
  177.             Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();  
  178.             singleTextH = fontMetrics.descent - fontMetrics.ascent;  
  179.             for (int i = 0; i < letters.length; i++) {  
  180.                 canvas.drawText(letters[i], w - getPaddingRight(), singleTextH + itemH * i, textPaint);  
  181.             }  
  182.             //触摸的时候画缩放图  
  183.         } else {  
  184.             //遍历所有字母  
  185.             for (int i = 0; i < letters.length; i++) {  
  186.                 //要画的字母的起始Y坐标  
  187.                 float currentItemToDrawY = singleTextH + itemH * i;  
  188.                 float centerItemToDrawY;  
  189.                 if (index < i)  
  190.                     centerItemToDrawY = singleTextH + itemH * (index + scaleItemCount);  
  191.                 else  
  192.                     centerItemToDrawY = singleTextH + itemH * (index - scaleItemCount);  
  193.                 float delta = 1 - Math.abs((y - currentItemToDrawY) / (centerItemToDrawY - currentItemToDrawY));  
  194.                 float maxRightX = w - getPaddingRight();  
  195.                 //如果大于0,表明在y坐标上方  
  196.                 scaleTextPaint.setTextSize(getTextSize() + getTextSize() * delta);  
  197.                 float drawX = maxRightX - scaleWidth * delta;  
  198.                 //超出边界直接花在边界上  
  199.                 if (drawX > maxRightX)  
  200.                     canvas.drawText(letters[i], maxRightX, singleTextH + itemH * i, textPaint);  
  201.                 else  
  202.                     canvas.drawText(letters[i], drawX, singleTextH + itemH * i, scaleTextPaint);  
  203.             }  
  204.         }  
  205.     }  
  206.   
  207.     public interface ISideBarSelectCallBack {  
  208.         void onSelectStr(int index, String selectStr);  
  209.     }  
  210.   
  211. }  


然后还有3个自定义的属性

[html]   view plain  copy
  1. <?xml version=“1.0” encoding=“utf-8”?>  
  2. <resources>  
  3.     <declare-styleable name=“SideBar”>  
  4.         <attr name=“scaleSize” format=“integer”/>  
  5.         <attr name=“scaleItemCount” format=“integer”/>  
  6.         <attr name=“scaleWidth” format=“dimension”/>  
  7.     </declare-styleable>  
  8. </resources>  


2.汉字转拼音工具类

我们知道,java中是没有提供接口和方法让我们直接将汉字转成拼音的。

这里,可以参见我的另一篇博客:Java/Android中汉字转拼音的两种方法,优劣比较
然后在此我选择了使用第三方jar包的方式,因为它体积不大而且更加准确。以下是我的Cn2Spell.java

[java]   view plain  copy
  1. package com.afei.indexlistview;  
  2.   
  3. import net.sourceforge.pinyin4j.PinyinHelper;  
  4. import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;  
  5. import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;  
  6. import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;  
  7.   
  8. /** 
  9.  * 汉字转换位汉语拼音,英文字符不变 
  10.  */  
  11. public class Cn2Spell {  
  12.   
  13.     public static StringBuffer sb = new StringBuffer();  
  14.   
  15.     /** 
  16.      * 获取汉字字符串的首字母,英文字符不变 
  17.      * 例如:阿飞→af 
  18.      */  
  19.     public static String getPinYinHeadChar(String chines) {  
  20.         sb.setLength(0);  
  21.         char[] chars = chines.toCharArray();  
  22.         HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();  
  23.         defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);  
  24.         defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);  
  25.         for (int i = 0; i < chars.length; i++) {  
  26.             if (chars[i] > 128) {  
  27.                 try {  
  28.                     sb.append(PinyinHelper.toHanyuPinyinStringArray(chars[i], defaultFormat)[0].charAt(0));  
  29.                 } catch (Exception e) {  
  30.                     e.printStackTrace();  
  31.                 }  
  32.             } else {  
  33.                 sb.append(chars[i]);  
  34.             }  
  35.         }  
  36.         return sb.toString();  
  37.     }  
  38.   
  39.     /** 
  40.      * 获取汉字字符串的第一个字母 
  41.      */  
  42.     public static String getPinYinFirstLetter(String str) {  
  43.         sb.setLength(0);  
  44.         char c = str.charAt(0);  
  45.         String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c);  
  46.         if (pinyinArray != ) {  
  47.             sb.append(pinyinArray[0].charAt(0));  
  48.         } else {  
  49.             sb.append(c);  
  50.         }  
  51.         return sb.toString();  
  52.     }  
  53.   
  54.     /** 
  55.      * 获取汉字字符串的汉语拼音,英文字符不变 
  56.      */  
  57.     public static String getPinYin(String chines) {  
  58.         sb.setLength(0);  
  59.         char[] nameChar = chines.toCharArray();  
  60.         HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();  
  61.         defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);  
  62.         defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);  
  63.         for (int i = 0; i < nameChar.length; i++) {  
  64.             if (nameChar[i] > 128) {  
  65.                 try {  
  66.                     sb.append(PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0]);  
  67.                 } catch (Exception e) {  
  68.                     e.printStackTrace();  
  69.                 }  
  70.             } else {  
  71.                 sb.append(nameChar[i]);  
  72.             }  
  73.         }  
  74.         return sb.toString();  
  75.     }  
  76.   
  77. }  



3.让你的好友可以根据拼音来排序

我们选择实现comparable接口,并重写comparaTo方法。以下是我的User.java

[java]   view plain  copy
  1. package com.afei.indexlistview;  
  2.   
  3. /** 
  4.  * Created by Administrator on 2016/5/25. 
  5.  */  
  6. public class User implements Comparable<User> {  
  7.   
  8.     private String name; // 姓名  
  9.     private String pinyin; // 姓名对应的拼音  
  10.     private String firstLetter; // 拼音的首字母  
  11.   
  12.     public User() {  
  13.     }  
  14.   
  15.     public User(String name) {  
  16.         this.name = name;  
  17.         pinyin = Cn2Spell.getPinYin(name); // 根据姓名获取拼音  
  18.         firstLetter = pinyin.substring(01).toUpperCase(); // 获取拼音首字母并转成大写  
  19.         if (!firstLetter.matches(“[A-Z]”)) { // 如果不在A-Z中则默认为“#”  
  20.             firstLetter = ”#”;  
  21.         }  
  22.     }  
  23.   
  24.     public String getName() {  
  25.         return name;  
  26.     }  
  27.   
  28.     public String getPinyin() {  
  29.         return pinyin;  
  30.     }  
  31.   
  32.     public String getFirstLetter() {  
  33.         return firstLetter;  
  34.     }  
  35.   
  36.   
  37.     @Override  
  38.     public int compareTo(User another) {  
  39.         if (firstLetter.equals(“#”) && !another.getFirstLetter().equals(“#”)) {  
  40.             return 1;  
  41.         } else if (!firstLetter.equals(“#”) && another.getFirstLetter().equals(“#”)){  
  42.             return -1;  
  43.         } else {  
  44.             return pinyin.compareToIgnoreCase(another.getPinyin());  
  45.         }  
  46.     }  
  47. }  

原理很简单,就是先根据首字母判断,首字母为“#”都放在最后,都为“#”或者都是字母时才根据拼音来比较排序


4.万事俱备只欠东风,接下来就是组装这些东西了

activity_main.xml布局文件

[html]   view plain  copy
  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.     tools:context=“com.afei.indexlistview.MainActivity”>  
  7.   
  8.     <ListView  
  9.         android:id=“@+id/listView”  
  10.         android:layout_width=“match_parent”  
  11.         android:layout_height=“match_parent” />  
  12.   
  13.     <com.afei.indexlistview.SideBar  
  14.         android:id=“@+id/side_bar”  
  15.         android:layout_width=“match_parent”  
  16.         android:layout_height=“match_parent”  
  17.         android:layout_alignParentRight=“true”  
  18.         android:paddingRight=“10dp”  
  19.         android:textColor=“@color/colorAccent”  
  20.         android:textSize=“15sp” />  
  21.   
  22. </RelativeLayout>  



MainActivity.java

[java]   view plain  copy
  1. package com.afei.indexlistview;  
  2.   
  3. import android.support.v7.app.AppCompatActivity;  
  4. import android.os.Bundle;  
  5. import android.widget.ListView;  
  6.   
  7. import java.util.ArrayList;  
  8. import java.util.Collections;  
  9.   
  10. public class MainActivity extends AppCompatActivity {  
  11.   
  12.     private ListView listView;  
  13.     private SideBar sideBar;  
  14.     private ArrayList<User> list;  
  15.   
  16.     @Override  
  17.     protected void onCreate(Bundle savedInstanceState) {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.activity_main);  
  20.         initView();  
  21.         initData();  
  22.     }  
  23.   
  24.     private void initView() {  
  25.         listView = (ListView) findViewById(R.id.listView);  
  26.         sideBar = (SideBar) findViewById(R.id.side_bar);  
  27.         sideBar.setOnStrSelectCallBack(new SideBar.ISideBarSelectCallBack() {  
  28.             @Override  
  29.             public void onSelectStr(int index, String selectStr) {  
  30.                 for (int i = 0; i < list.size(); i++) {  
  31.                     if (selectStr.equalsIgnoreCase(list.get(i).getFirstLetter())) {  
  32.                         listView.setSelection(i); // 选择到首字母出现的位置  
  33.                         return;  
  34.                     }  
  35.                 }  
  36.             }  
  37.         });  
  38.     }  
  39.   
  40.     private void initData() {  
  41.         list = new ArrayList<>();  
  42.         list.add(new User(“亳州”)); // 亳[bó]属于不常见的二级汉字  
  43.         list.add(new User(“大娃”));  
  44.         list.add(new User(“二娃”));  
  45.         list.add(new User(“三娃”));  
  46.         list.add(new User(“四娃”));  
  47.         list.add(new User(“五娃”));  
  48.         list.add(new User(“六娃”));  
  49.         list.add(new User(“七娃”));  
  50.         list.add(new User(“喜羊羊”));  
  51.         list.add(new User(“美羊羊”));  
  52.         list.add(new User(“懒羊羊”));  
  53.         list.add(new User(“沸羊羊”));  
  54.         list.add(new User(“暖羊羊”));  
  55.         list.add(new User(“慢羊羊”));  
  56.         list.add(new User(“灰太狼”));  
  57.         list.add(new User(“红太狼”));  
  58.         list.add(new User(“孙悟空”));  
  59.         list.add(new User(“黑猫警长”));  
  60.         list.add(new User(“舒克”));  
  61.         list.add(new User(“贝塔”));  
  62.         list.add(new User(“海尔”));  
  63.         list.add(new User(“阿凡提”));  
  64.         list.add(new User(“邋遢大王”));  
  65.         list.add(new User(“哪吒”));  
  66.         list.add(new User(“没头脑”));  
  67.         list.add(new User(“不高兴”));  
  68.         list.add(new User(“蓝皮鼠”));  
  69.         list.add(new User(“大脸猫”));  
  70.         list.add(new User(“大头儿子”));  
  71.         list.add(new User(“小头爸爸”));  
  72.         list.add(new User(“蓝猫”));  
  73.         list.add(new User(“淘气”));  
  74.         list.add(new User(“叶峰”));  
  75.         list.add(new User(“楚天歌”));  
  76.         list.add(new User(“江流儿”));  
  77.         list.add(new User(“Tom”));  
  78.         list.add(new User(“Jerry”));  
  79.         list.add(new User(“12345”));  
  80.         list.add(new User(“54321”));  
  81.         list.add(new User(“_(:з」∠)_”));  
  82.         list.add(new User(“……%¥#¥%#”));  
  83.         Collections.sort(list); // 对list进行排序,需要让User实现Comparable接口重写compareTo方法  
  84.         SortAdapter adapter = new SortAdapter(this, list);  
  85.         listView.setAdapter(adapter);  
  86.     }  
  87. }  


这里负责初始化UI和数据,并且实现滑动或选择字母索引时的回调接口。既然用到了ListView,我们就还需要一个适配器。

SortAdapter.java

[java]   view plain  copy
  1. package com.afei.indexlistview;  
  2.   
  3. import android.content.Context;  
  4. import android.view.LayoutInflater;  
  5. import android.view.View;  
  6. import android.view.ViewGroup;  
  7. import android.widget.BaseAdapter;  
  8. import android.widget.TextView;  
  9.   
  10. import java.util.List;  
  11.   
  12. public class SortAdapter extends BaseAdapter{  
  13.   
  14.     private List<User> list = ;  
  15.     private Context mContext;  
  16.   
  17.     public SortAdapter(Context mContext, List<User> list) {  
  18.         this.mContext = mContext;  
  19.         this.list = list;  
  20.     }  
  21.   
  22.     public int getCount() {  
  23.         return this.list.size();  
  24.     }  
  25.   
  26.     public Object getItem(int position) {  
  27.         return list.get(position);  
  28.     }  
  29.   
  30.     public long getItemId(int position) {  
  31.         return position;  
  32.     }  
  33.   
  34.     public View getView(final int position, View view, ViewGroup arg2) {  
  35.         ViewHolder viewHolder;  
  36.         final User user = list.get(position);  
  37.         if (view == ) {  
  38.             viewHolder = new ViewHolder();  
  39.             view = LayoutInflater.from(mContext).inflate(R.layout.item, );  
  40.             viewHolder.name = (TextView) view.findViewById(R.id.name);  
  41.             viewHolder.catalog = (TextView) view.findViewById(R.id.catalog);  
  42.             view.setTag(viewHolder);  
  43.         } else {  
  44.             viewHolder = (ViewHolder) view.getTag();  
  45.         }  
  46.   
  47.         //根据position获取首字母作为目录catalog  
  48.         String catalog = list.get(position).getFirstLetter();  
  49.   
  50.         //如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现  
  51.         if(position == getPositionForSection(catalog)){  
  52.             viewHolder.catalog.setVisibility(View.VISIBLE);  
  53.             viewHolder.catalog.setText(user.getFirstLetter().toUpperCase());  
  54.         }else{  
  55.             viewHolder.catalog.setVisibility(View.GONE);  
  56.         }  
  57.   
  58.         viewHolder.name.setText(this.list.get(position).getName());  
  59.   
  60.         return view;  
  61.   
  62.     }  
  63.   
  64.     final static class ViewHolder {  
  65.         TextView catalog;  
  66.         TextView name;  
  67.     }  
  68.   
  69.     /** 
  70.      * 获取catalog首次出现位置 
  71.      */  
  72.     public int getPositionForSection(String catalog) {  
  73.         for (int i = 0; i < getCount(); i++) {  
  74.             String sortStr = list.get(i).getFirstLetter();  
  75.             if (catalog.equalsIgnoreCase(sortStr)) {  
  76.                 return i;  
  77.             }  
  78.         }  
  79.         return -1;  
  80.     }  
  81.   
  82. }  


适配器还用到了一个布局,即


item.xml

[html]   view plain  copy
  1. <?xml version=“1.0” encoding=“utf-8”?>  
  2. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     android:layout_width=“match_parent”  
  4.     android:layout_height=“wrap_content”  
  5.     android:gravity=“center_vertical”  
  6.     android:orientation=“vertical” >  
  7.   
  8.     <TextView  
  9.         android:id=“@+id/catalog”  
  10.         android:layout_width=“match_parent”  
  11.         android:layout_height=“match_parent”  
  12.         android:background=“#E0E0E0”  
  13.         android:textColor=“#454545”  
  14.         android:textSize=“20sp”  
  15.         android:padding=“10dp”/>  
  16.   
  17.     <TextView  
  18.         android:id=“@+id/name”  
  19.         android:layout_width=“match_parent”  
  20.         android:layout_height=“match_parent”  
  21.         android:gravity=“center_vertical”  
  22.         android:textColor=“#336598”  
  23.         android:textSize=“16sp”  
  24.         android:padding=“10dp”/>  
  25.   
  26. </LinearLayout>  


布局有两部分,一个是目录,即A,B,C,D这样的索引,仅当该目录下的第一项出现时才显示;一个则是姓名

四、项目地址

Git地址:http://git.oschina.net/afei_/IndexListView



一、效果图展示



二、功能特点

1.好友排序:按照拼音顺序对好友进行排序,兼容英文数字符号等

2.字母索引:右侧字母导航条,既可拖动也可点击,联动ListView滑动


三、实现

接下来就让我们一步步显示这个效果吧。

1.右侧字母索引的导航条

这个我们可以在网上找到很多类似的,你大可找一个自己喜欢的甚至自己写一个出来,这里我在网上找了一个带波浪效果的,看起来比较炫酷一点吧。

这是原地址:https://github.com/AlexLiuSheng/AnimSideBar

然后我把它导入到了我们项目中并修改了部分代码,以下是我项目中的SideBar.Java

[java]   view plain  copy
  1. package com.afei.indexlistview;  
  2.   
  3. import android.content.Context;  
  4. import android.content.res.TypedArray;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.Paint;  
  7. import android.util.AttributeSet;  
  8. import android.view.MotionEvent;  
  9. import android.widget.TextView;  
  10.   
  11. public class SideBar extends TextView {  
  12.     private String[] letters = new String[]{“A”“B”“C”“D”“E”“F”“G”“H”“I”,  
  13.             ”J”“K”“L”“M”“N”“O”“P”“Q”“R”“S”“T”“U”“V”,  
  14.             ”W”“X”“Y”“Z”“#”};  
  15.     private Paint textPaint;  
  16.     private Paint bigTextPaint;  
  17.     private Paint scaleTextPaint;  
  18.   
  19.     private Canvas canvas;  
  20.     private int itemH;  
  21.     private int w;  
  22.     private int h;  
  23.     /** 
  24.      * 普通情况下字体大小 
  25.      */  
  26.     float singleTextH;  
  27.     /** 
  28.      * 缩放离原始的宽度 
  29.      */  
  30.     private float scaleWidth;  
  31.     /** 
  32.      * 滑动的Y 
  33.      */  
  34.     private float eventY = 0;  
  35.     /** 
  36.      * 缩放的倍数 
  37.      */  
  38.     private int scaleSize = 1;  
  39.     /** 
  40.      * 缩放个数item,即开口大小 
  41.      */  
  42.     private int scaleItemCount = 6;  
  43.     private ISideBarSelectCallBack callBack;  
  44.   
  45.     public SideBar(Context context) {  
  46.         this(context, );  
  47.     }  
  48.   
  49.     public SideBar(Context context, AttributeSet attrs) {  
  50.         this(context, attrs, 0);  
  51.     }  
  52.   
  53.     public SideBar(Context context, AttributeSet attrs, int defStyleAttr) {  
  54.         super(context, attrs, defStyleAttr);  
  55.         init(attrs);  
  56.     }  
  57.   
  58.     private void init(AttributeSet attrs) {  
  59.         if (attrs != ) {  
  60.             TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SideBar);  
  61.             scaleSize = ta.getInteger(R.styleable.SideBar_scaleSize, 1);  
  62.             scaleItemCount = ta.getInteger(R.styleable.SideBar_scaleItemCount, 6);  
  63.             scaleWidth = ta.getDimensionPixelSize(R.styleable.SideBar_scaleWidth, dp(100));  
  64.             ta.recycle();  
  65.         }  
  66.         textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  67.         textPaint.setColor(getCurrentTextColor());  
  68.         textPaint.setTextSize(getTextSize());  
  69.         textPaint.setTextAlign(Paint.Align.CENTER);  
  70.         bigTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  71.         bigTextPaint.setColor(getCurrentTextColor());  
  72.         bigTextPaint.setTextSize(getTextSize() * (scaleSize + 3));  
  73.         bigTextPaint.setTextAlign(Paint.Align.CENTER);  
  74.         scaleTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  75.         scaleTextPaint.setColor(getCurrentTextColor());  
  76.         scaleTextPaint.setTextSize(getTextSize() * (scaleSize + 1));  
  77.         scaleTextPaint.setTextAlign(Paint.Align.CENTER);  
  78.     }  
  79.   
  80.     public void setDataResource(String[] data) {  
  81.         letters = data;  
  82.         invalidate();  
  83.     }  
  84.   
  85.     public void setOnStrSelectCallBack(ISideBarSelectCallBack callBack) {  
  86.         this.callBack = callBack;  
  87.     }  
  88.   
  89.     /** 
  90.      * 设置字体缩放比例 
  91.      * 
  92.      * @param scale 
  93.      */  
  94.     public void setScaleSize(int scale) {  
  95.         scaleSize = scale;  
  96.         invalidate();  
  97.     }  
  98.   
  99.     /** 
  100.      * 设置缩放字体的个数,即开口大小 
  101.      * 
  102.      * @param scaleItemCount 
  103.      */  
  104.     public void setScaleItemCount(int scaleItemCount) {  
  105.         this.scaleItemCount = scaleItemCount;  
  106.         invalidate();  
  107.     }  
  108.   
  109.     private int dp(int px) {  
  110.         final float scale = getContext().getResources().getDisplayMetrics().density;  
  111.         return (int) (px * scale + 0.5f);  
  112.     }  
  113.   
  114.     @Override  
  115.     public boolean onTouchEvent(MotionEvent event) {  
  116.         switch (event.getAction()) {  
  117.             case MotionEvent.ACTION_DOWN:  
  118.             case MotionEvent.ACTION_MOVE:  
  119.                 if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {  
  120.                     eventY = event.getY();  
  121.                     invalidate();  
  122.                     return true;  
  123.                 } else {  
  124.                     eventY = 0;  
  125.                     invalidate();  
  126.                     break;  
  127.                 }  
  128.             case MotionEvent.ACTION_CANCEL:  
  129.                 eventY = 0;  
  130.                 invalidate();  
  131.                 return true;  
  132.             case MotionEvent.ACTION_UP:  
  133.                 if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {  
  134.                     eventY = 0;  
  135.                     invalidate();  
  136.                     return true;  
  137.                 } else  
  138.                     break;  
  139.         }  
  140.         return super.onTouchEvent(event);  
  141.     }  
  142.   
  143.   
  144.     @Override  
  145.     protected void onDraw(Canvas canvas) {  
  146.         this.canvas = canvas;  
  147.         DrawView(eventY);  
  148.     }  
  149.   
  150.     private void DrawView(float y) {  
  151.         int currentSelectIndex = -1;  
  152.         if (y != 0) {  
  153.             for (int i = 0; i < letters.length; i++) {  
  154.                 float currentItemY = itemH * i;  
  155.                 float nextItemY = itemH * (i + 1);  
  156.                 if (y >= currentItemY && y < nextItemY) {  
  157.                     currentSelectIndex = i;  
  158.                     if (callBack != ) {  
  159.                         callBack.onSelectStr(currentSelectIndex, letters[i]);  
  160.                     }  
  161.                     //画大的字母  
  162.                     Paint.FontMetrics fontMetrics = bigTextPaint.getFontMetrics();  
  163.                     float bigTextSize = fontMetrics.descent - fontMetrics.ascent;  
  164.                     canvas.drawText(letters[i], w - getPaddingRight() - scaleWidth - bigTextSize, singleTextH + itemH * i, bigTextPaint);  
  165.                 }  
  166.             }  
  167.         }  
  168.         drawLetters(y, currentSelectIndex);  
  169.     }  
  170.   
  171.     private void drawLetters(float y, int index) {  
  172.         //第一次进来没有缩放情况,默认画原图  
  173.         if (index == -1) {  
  174.             w = getMeasuredWidth();  
  175.             h = getMeasuredHeight();  
  176.             itemH = h / letters.length;  
  177.             Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();  
  178.             singleTextH = fontMetrics.descent - fontMetrics.ascent;  
  179.             for (int i = 0; i < letters.length; i++) {  
  180.                 canvas.drawText(letters[i], w - getPaddingRight(), singleTextH + itemH * i, textPaint);  
  181.             }  
  182.             //触摸的时候画缩放图  
  183.         } else {  
  184.             //遍历所有字母  
  185.             for (int i = 0; i < letters.length; i++) {  
  186.                 //要画的字母的起始Y坐标  
  187.                 float currentItemToDrawY = singleTextH + itemH * i;  
  188.                 float centerItemToDrawY;  
  189.                 if (index < i)  
  190.                     centerItemToDrawY = singleTextH + itemH * (index + scaleItemCount);  
  191.                 else  
  192.                     centerItemToDrawY = singleTextH + itemH * (index - scaleItemCount);  
  193.                 float delta = 1 - Math.abs((y - currentItemToDrawY) / (centerItemToDrawY - currentItemToDrawY));  
  194.                 float maxRightX = w - getPaddingRight();  
  195.                 //如果大于0,表明在y坐标上方  
  196.                 scaleTextPaint.setTextSize(getTextSize() + getTextSize() * delta);  
  197.                 float drawX = maxRightX - scaleWidth * delta;  
  198.                 //超出边界直接花在边界上  
  199.                 if (drawX > maxRightX)  
  200.                     canvas.drawText(letters[i], maxRightX, singleTextH + itemH * i, textPaint);  
  201.                 else  
  202.                     canvas.drawText(letters[i], drawX, singleTextH + itemH * i, scaleTextPaint);  
  203.             }  
  204.         }  
  205.     }  
  206.   
  207.     public interface ISideBarSelectCallBack {  
  208.         void onSelectStr(int index, String selectStr);  
  209.     }  
  210.   
  211. }  


然后还有3个自定义的属性

[html]   view plain  copy
  1. <?xml version=“1.0” encoding=“utf-8”?>  
  2. <resources>  
  3.     <declare-styleable name=“SideBar”>  
  4.         <attr name=“scaleSize” format=“integer”/>  
  5.         <attr name=“scaleItemCount” format=“integer”/>  
  6.         <attr name=“scaleWidth” format=“dimension”/>  
  7.     </declare-styleable>  
  8. </resources>  


2.汉字转拼音工具类

我们知道,java中是没有提供接口和方法让我们直接将汉字转成拼音的。

这里,可以参见我的另一篇博客:Java/Android中汉字转拼音的两种方法,优劣比较
然后在此我选择了使用第三方jar包的方式,因为它体积不大而且更加准确。以下是我的Cn2Spell.java

[java]   view plain  copy
  1. package com.afei.indexlistview;  
  2.   
  3. import net.sourceforge.pinyin4j.PinyinHelper;  
  4. import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;  
  5. import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;  
  6. import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;  
  7.   
  8. /** 
  9.  * 汉字转换位汉语拼音,英文字符不变 
  10.  */  
  11. public class Cn2Spell {  
  12.   
  13.     public static StringBuffer sb = new StringBuffer();  
  14.   
  15.     /** 
  16.      * 获取汉字字符串的首字母,英文字符不变 
  17.      * 例如:阿飞→af 
  18.      */  
  19.     public static String getPinYinHeadChar(String chines) {  
  20.         sb.setLength(0);  
  21.         char[] chars = chines.toCharArray();  
  22.         HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();  
  23.         defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);  
  24.         defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);  
  25.         for (int i = 0; i < chars.length; i++) {  
  26.             if (chars[i] > 128) {  
  27.                 try {  
  28.                     sb.append(PinyinHelper.toHanyuPinyinStringArray(chars[i], defaultFormat)[0].charAt(0));  
  29.                 } catch (Exception e) {  
  30.                     e.printStackTrace();  
  31.                 }  
  32.             } else {  
  33.                 sb.append(chars[i]);  
  34.             }  
  35.         }  
  36.         return sb.toString();  
  37.     }  
  38.   
  39.     /** 
  40.      * 获取汉字字符串的第一个字母 
  41.      */  
  42.     public static String getPinYinFirstLetter(String str) {  
  43.         sb.setLength(0);  
  44.         char c = str.charAt(0);  
  45.         String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c);  
  46.         if (pinyinArray != ) {  
  47.             sb.append(pinyinArray[0].charAt(0));  
  48.         } else {  
  49.             sb.append(c);  
  50.         }  
  51.         return sb.toString();  
  52.     }  
  53.   
  54.     /** 
  55.      * 获取汉字字符串的汉语拼音,英文字符不变 
  56.      */  
  57.     public static String getPinYin(String chines) {  
  58.         sb.setLength(0);  
  59.         char[] nameChar = chines.toCharArray();  
  60.         HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();  
  61.         defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);  
  62.         defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);  
  63.         for (int i = 0; i < nameChar.length; i++) {  
  64.             if (nameChar[i] > 128) {  
  65.                 try {  
  66.                     sb.append(PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0]);  
  67.                 } catch (Exception e) {  
  68.                     e.printStackTrace();  
  69.                 }  
  70.             } else {  
  71.                 sb.append(nameChar[i]);  
  72.             }  
  73.         }  
  74.         return sb.toString();  
  75.     }  
  76.   
  77. }  



3.让你的好友可以根据拼音来排序

我们选择实现comparable接口,并重写comparaTo方法。以下是我的User.java

[java]   view plain  copy
  1. package com.afei.indexlistview;  
  2.   
  3. /** 
  4.  * Created by Administrator on 2016/5/25. 
  5.  */  
  6. public class User implements Comparable<User> {  
  7.   
  8.     private String name; // 姓名  
  9.     private String pinyin; // 姓名对应的拼音  
  10.     private String firstLetter; // 拼音的首字母  
  11.   
  12.     public User() {  
  13.     }  
  14.   
  15.     public User(String name) {  
  16.         this.name = name;  
  17.         pinyin = Cn2Spell.getPinYin(name); // 根据姓名获取拼音  
  18.         firstLetter = pinyin.substring(01).toUpperCase(); // 获取拼音首字母并转成大写  
  19.         if (!firstLetter.matches(“[A-Z]”)) { // 如果不在A-Z中则默认为“#”  
  20.             firstLetter = ”#”;  
  21.         }  
  22.     }  
  23.   
  24.     public String getName() {  
  25.         return name;  
  26.     }  
  27.   
  28.     public String getPinyin() {  
  29.         return pinyin;  
  30.     }  
  31.   
  32.     public String getFirstLetter() {  
  33.         return firstLetter;  
  34.     }  
  35.   
  36.   
  37.     @Override  
  38.     public int compareTo(User another) {  
  39.         if (firstLetter.equals(“#”) && !another.getFirstLetter().equals(“#”)) {  
  40.             return 1;  
  41.         } else if (!firstLetter.equals(“#”) && another.getFirstLetter().equals(“#”)){  
  42.             return -1;  
  43.         } else {  
  44.             return pinyin.compareToIgnoreCase(another.getPinyin());  
  45.         }  
  46.     }  
  47. }  

原理很简单,就是先根据首字母判断,首字母为“#”都放在最后,都为“#”或者都是字母时才根据拼音来比较排序


4.万事俱备只欠东风,接下来就是组装这些东西了

activity_main.xml布局文件

[html]   view plain  copy
  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.     tools:context=“com.afei.indexlistview.MainActivity”>  
  7.   
  8.     <ListView  
  9.         android:id=“@+id/listView”  
  10.         android:layout_width=“match_parent”  
  11.         android:layout_height=“match_parent” />  
  12.   
  13.     <com.afei.indexlistview.SideBar  
  14.         android:id=“@+id/side_bar”  
  15.         android:layout_width=“match_parent”  
  16.         android:layout_height=“match_parent”  
  17.         android:layout_alignParentRight=“true”  
  18.         android:paddingRight=“10dp”  
  19.         android:textColor=“@color/colorAccent”  
  20.         android:textSize=“15sp” />  
  21.   
  22. </RelativeLayout>  



MainActivity.java

[java]   view plain  copy
  1. package com.afei.indexlistview;  
  2.   
  3. import android.support.v7.app.AppCompatActivity;  
  4. import android.os.Bundle;  
  5. import android.widget.ListView;  
  6.   
  7. import java.util.ArrayList;  
  8. import java.util.Collections;  
  9.   
  10. public class MainActivity extends AppCompatActivity {  
  11.   
  12.     private ListView listView;  
  13.     private SideBar sideBar;  
  14.     private ArrayList<User> list;  
  15.   
  16.     @Override  
  17.     protected void onCreate(Bundle savedInstanceState) {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.activity_main);  
  20.         initView();  
  21.         initData();  
  22.     }  
  23.   
  24.     private void initView() {  
  25.         listView = (ListView) findViewById(R.id.listView);  
  26.         sideBar = (SideBar) findViewById(R.id.side_bar);  
  27.         sideBar.setOnStrSelectCallBack(new SideBar.ISideBarSelectCallBack() {  
  28.             @Override  
  29.             public void onSelectStr(int index, String selectStr) {  
  30.                 for (int i = 0; i < list.size(); i++) {  
  31.                     if (selectStr.equalsIgnoreCase(list.get(i).getFirstLetter())) {  
  32.                         listView.setSelection(i); // 选择到首字母出现的位置  
  33.                         return;  
  34.                     }  
  35.                 }  
  36.             }  
  37.         });  
  38.     }  
  39.   
  40.     private void initData() {  
  41.         list = new ArrayList<>();  
  42.         list.add(new User(“亳州”)); // 亳[bó]属于不常见的二级汉字  
  43.         list.add(new User(“大娃”));  
  44.         list.add(new User(“二娃”));  
  45.         list.add(new User(“三娃”));  
  46.         list.add(new User(“四娃”));  
  47.         list.add(new User(“五娃”));  
  48.         list.add(new User(“六娃”));  
  49.         list.add(new User(“七娃”));  
  50.         list.add(new User(“喜羊羊”));  
  51.         list.add(new User(“美羊羊”));  
  52.         list.add(new User(“懒羊羊”));  
  53.         list.add(new User(“沸羊羊”));  
  54.         list.add(new User(“暖羊羊”));  
  55.         list.add(new User(“慢羊羊”));  
  56.         list.add(new User(“灰太狼”));  
  57.         list.add(new User(“红太狼”));  
  58.         list.add(new User(“孙悟空”));  
  59.         list.add(new User(“黑猫警长”));  
  60.         list.add(new User(“舒克”));  
  61.         list.add(new User(“贝塔”));  
  62.         list.add(new User(“海尔”));  
  63.         list.add(new User(“阿凡提”));  
  64.         list.add(new User(“邋遢大王”));  
  65.         list.add(new User(“哪吒”));  
  66.         list.add(new User(“没头脑”));  
  67.         list.add(new User(“不高兴”));  
  68.         list.add(new User(“蓝皮鼠”));  
  69.         list.add(new User(“大脸猫”));  
  70.         list.add(new User(“大头儿子”));  
  71.         list.add(new User(“小头爸爸”));  
  72.         list.add(new User(“蓝猫”));  
  73.         list.add(new User(“淘气”));  
  74.         list.add(new User(“叶峰”));  
  75.         list.add(new User(“楚天歌”));  
  76.         list.add(new User(“江流儿”));  
  77.         list.add(new User(“Tom”));  
  78.         list.add(new User(“Jerry”));  
  79.         list.add(new User(“12345”));  
  80.         list.add(new User(“54321”));  
  81.         list.add(new User(“_(:з」∠)_”));  
  82.         list.add(new User(“……%¥#¥%#”));  
  83.         Collections.sort(list); // 对list进行排序,需要让User实现Comparable接口重写compareTo方法  
  84.         SortAdapter adapter = new SortAdapter(this, list);  
  85.         listView.setAdapter(adapter);  
  86.     }  
  87. }  


这里负责初始化UI和数据,并且实现滑动或选择字母索引时的回调接口。既然用到了ListView,我们就还需要一个适配器。

SortAdapter.java

[java]   view plain  copy
  1. package com.afei.indexlistview;  
  2.   
  3. import android.content.Context;  
  4. import android.view.LayoutInflater;  
  5. import android.view.View;  
  6. import android.view.ViewGroup;  
  7. import android.widget.BaseAdapter;  
  8. import android.widget.TextView;  
  9.   
  10. import java.util.List;  
  11.   
  12. public class SortAdapter extends BaseAdapter{  
  13.   
  14.     private List<User> list = ;  
  15.     private Context mContext;  
  16.   
  17.     public SortAdapter(Context mContext, List<User> list) {  
  18.         this.mContext = mContext;  
  19.         this.list = list;  
  20.     }  
  21.   
  22.     public int getCount() {  
  23.         return this.list.size();  
  24.     }  
  25.   
  26.     public Object getItem(int position) {  
  27.         return list.get(position);  
  28.     }  
  29.   
  30.     public long getItemId(int position) {  
  31.         return position;  
  32.     }  
  33.   
  34.     public View getView(final int position, View view, ViewGroup arg2) {  
  35.         ViewHolder viewHolder;  
  36.         final User user = list.get(position);  
  37.         if (view == ) {  
  38.             viewHolder = new ViewHolder();  
  39.             view = LayoutInflater.from(mContext).inflate(R.layout.item, );  
  40.             viewHolder.name = (TextView) view.findViewById(R.id.name);  
  41.             viewHolder.catalog = (TextView) view.findViewById(R.id.catalog);  
  42.             view.setTag(viewHolder);  
  43.         } else {  
  44.             viewHolder = (ViewHolder) view.getTag();  
  45.         }  
  46.   
  47.         //根据position获取首字母作为目录catalog  
  48.         String catalog = list.get(position).getFirstLetter();  
  49.   
  50.         //如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现  
  51.         if(position == getPositionForSection(catalog)){  
  52.             viewHolder.catalog.setVisibility(View.VISIBLE);  
  53.             viewHolder.catalog.setText(user.getFirstLetter().toUpperCase());  
  54.         }else{  
  55.             viewHolder.catalog.setVisibility(View.GONE);  
  56.         }  
  57.   
  58.         viewHolder.name.setText(this.list.get(position).getName());  
  59.   
  60.         return view;  
  61.   
  62.     }  
  63.   
  64.     final static class ViewHolder {  
  65.         TextView catalog;  
  66.         TextView name;  
  67.     }  
  68.   
  69.     /** 
  70.      * 获取catalog首次出现位置 
  71.      */  
  72.     public int getPositionForSection(String catalog) {  
  73.         for (int i = 0; i < getCount(); i++) {  
  74.             String sortStr = list.get(i).getFirstLetter();  
  75.             if (catalog.equalsIgnoreCase(sortStr)) {  
  76.                 return i;  
  77.             }  
  78.         }  
  79.         return -1;  
  80.     }  
  81.   
  82. }  


适配器还用到了一个布局,即


item.xml

[html]   view plain  copy
  1. <?xml version=“1.0” encoding=“utf-8”?>  
  2. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     android:layout_width=“match_parent”  
  4.     android:layout_height=“wrap_content”  
  5.     android:gravity=“center_vertical”  
  6.     android:orientation=“vertical” >  
  7.   
  8.     <TextView  
  9.         android:id=“@+id/catalog”  
  10.         android:layout_width=“match_parent”  
  11.         android:layout_height=“match_parent”  
  12.         android:background=“#E0E0E0”  
  13.         android:textColor=“#454545”  
  14.         android:textSize=“20sp”  
  15.         android:padding=“10dp”/>  
  16.   
  17.     <TextView  
  18.         android:id=“@+id/name”  
  19.         android:layout_width=“match_parent”  
  20.         android:layout_height=“match_parent”  
  21.         android:gravity=“center_vertical”  
  22.         android:textColor=“#336598”  
  23.         android:textSize=“16sp”  
  24.         android:padding=“10dp”/>  
  25.   
  26. </LinearLayout>  


布局有两部分,一个是目录,即A,B,C,D这样的索引,仅当该目录下的第一项出现时才显示;一个则是姓名

四、项目地址

Git地址:http://git.oschina.net/afei_/IndexListView

猜你喜欢

转载自blog.csdn.net/yzj_0722/article/details/80217647