也是忙忙碌碌好几天,今天又有时间了,继续这个文章的编写。今天是这篇文章的最后一部分。主要内容包括以下几点:
1.将中文名字转化成拼音,并提取首字母,进行排序。
2.实现分组列表Adapter模板。
3.将列表与索引结合在一起。
pinyin4j是一个将中文转化成拼音的高效工具,我的源码中带了这个依赖包。通过这个工具,可以先获取一个中文的拼音。
public static String getLetter(String name) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < name.length(); i++) { char c = name.charAt(i); String[] str = PinyinHelper.toHanyuPinyinStringArray(c); if (str != null && str.length >= 1) { sb.append(str[0].charAt(0)); } else { sb.append(c); } } return sb.toString(); }
PinyinHelper.tohanyuPinyinStringArray(),将每个char转化成拼音,我这里只提取每个汉字的第一个拼音字母,最后返回的结果是字符串的拼音简写。比如:张三丰---zsf
上面的步骤,已经可以将中文转化成拼音简写了,下面,要做的是什么呢?
下面的思路是这样的:
1.先对列表数据按字母顺序排序。
2.adapter的每一个itemView都是带字母分组头和内容的。只是,只有在一组中文开头的首个位置才显示。也就是说,张三丰,张君宝等,排在第一个的显示头部,也就是z,其他的隐藏头部。所以这个位置要计算出来。
3.因为每一个item包含了头部,所以,点击事件需要在真实内容区域,不在头部。因此,ListView的点击事件需要禁用,把事件写在adapter的内容控件上。
1.1.先对字母排序,排序需要知道排序的内容,先定义一个接口:
package com.mjc.contactlistdemo.sort_by_letter; /** * Created by mjc on 2016/5/24. */ public interface ISort { String getSortName(); }
之后,我们需要使用的数据,只要继承他,就可以使用我的带字母索引列表。
1.2.自定义排序方法:
排序的方法,需要我们借助Collections的sorts方法来排序:
/** * 按照字母排序 * * @param list * @return */ public static <T extends ISort> void sortByLetter(ArrayList<T> list) { Collections.sort(list, new Comparator<T>() { @Override public int compare(T lhs, T rhs) { String l = getLetter(lhs.getSortName()); String r = getLetter(rhs.getSortName()); int minLength = Math.min(l.length(), r.length()); int result = 0; for (int i = 0; i < minLength; i++) { if (l.charAt(i) < r.charAt(i)) { result = -1; break; } else if (l.charAt(i) > r.charAt(i)) { result = 1; break; } else { result = 0; continue; } } if (result == 0) { return l.length() > r.length() ? 1 : -1; } return result; } }); }
用的是泛型,只要实现了Isort的实体类,都可以用它来排序。排序的过程,主要是compare方法,这个方法的返回值,决定了你的排序。每次比较两个数值,结果由返回值决定。具体的说,就是lhs要排在rhs左边,返回值就要小于0,;反之,一样。
2.1.Adapter的实现。
我将计算都提取到了一个BaseSortByLetterAdapter里面,但是在子Adapter里面,需要调用BaseSortByLetterAdapter的方法,得到第一个分组的位置。
package com.mjc.contactlistdemo.sort_by_letter; import android.widget.BaseAdapter; import android.widget.SectionIndexer; import java.util.ArrayList; /** * Created by mjc on 2016/5/24. */ public abstract class BaseSortByLetterAdapter<T> extends BaseAdapter implements SectionIndexer { protected String[] sections; protected ArrayList<T> datas; public BaseSortByLetterAdapter(ArrayList<T> datas) { sections = new String[]{ "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","#"}; this.datas = datas; } @Override public String[] getSections() { return sections; } //需要进行排序的字符串 protected abstract String getSortString(T bean); //通过section位置,获取首个position位置 @Override public int getPositionForSection(int sectionIndex) { String section = sections[sectionIndex]; //todo ListView的数据要按照字母顺序排列 for (int i = 0; i < getCount(); i++) { T bean = datas.get(i); String headerLetter = SortUtil.getLetter(getSortString(bean)); if (String.valueOf(headerLetter.charAt(0)).equalsIgnoreCase(section)) { return i; } else if (sectionIndex == 0) { return 0; } } return -1; } //通过位置获取sectionIndex位置 @Override public int getSectionForPosition(int position) { T bean = datas.get(position); String name = getSortString(bean); String letter = SortUtil.getLetter(name); String header = String.valueOf(letter.charAt(0)); for (int i = 0; i < sections.length; i++) { if (sections[i].equalsIgnoreCase(header)) { return i; } } return 0; } public int getSectionIndex(String section) { for (int i = 0; i < sections.length; i++) { if (section.equalsIgnoreCase(sections[i])) { return i; } } return 0; } }
implements SectionIndexer,这个东西不是重点,他的作用是,当你开启ListView的快速滑动后,拖动滑动条是可以显示当前所处的数据的字母。只不过我这里实现方式,和系统的原理差不多,因此也是实现了这个接口,之后用到我们的IndexView上就好。
这里面重要的一点是:我们需要知道第一个字母出现的位置,以便于我们现实这个位置的title字母,其他位置隐藏。这样我们的分组效果就能实现了。 为了获取这个位置,我们的逻辑是这样的:通过位置获取当前位置的字母,再通过这个字母获取这个字母在列表中的第一个位置,如果第一个位置和当前位置相同则表示是第一个位置。上面的主要两个方法就是为这个服务的。
通过位置获取对应的字母字母的位置,原理很简单,先获取当前数据的排序字母,然后和字母列表比较,得到字母的位置。
@Override public int getSectionForPosition(int position) { T bean = datas.get(position); String name = getSortString(bean); String letter = SortUtil.getLetter(name); String header = String.valueOf(letter.charAt(0)); for (int i = 0; i < sections.length; i++) { if (sections[i].equalsIgnoreCase(header)) { return i; } } return 0; }
再通过字母的位置,获取列表中第一个数据的位置,之后在getView中将这个位置和当前位置做比较,如果相等,就显示title,不相等就隐藏。
//通过section位置,获取首个position位置 @Override public int getPositionForSection(int sectionIndex) { String section = sections[sectionIndex]; //todo ListView的数据要按照字母顺序排列 for (int i = 0; i < getCount(); i++) { T bean = datas.get(i); String headerLetter = SortUtil.getLetter(getSortString(bean)); if (String.valueOf(headerLetter.charAt(0)).equalsIgnoreCase(section)) { return i; } else if (sectionIndex == 0) { return 0; } } return -1; }
到这里,基本是结束了,接下来是使用方法:
1.定义一个实体类,实现Isort接口。
2.定义一个Adpater,继承BaseSortByLetterAdapter
3.Activity中,setAdapter之前,先使用SortUtil对数据进行排序。
ContactEntity.class
package com.mjc.contactlistdemo.core.contact.entity; import com.mjc.contactlistdemo.sort_by_letter.ISort; /** * Created by mjc on 2016/5/12. */ public class ContactEntity implements ISort { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String getSortName() { return name; } }
ContactAdapter.class
package com.mjc.contactlistdemo.core.contact; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.mjc.contactlistdemo.R; import com.mjc.contactlistdemo.core.contact.entity.ContactEntity; import com.mjc.contactlistdemo.sort_by_letter.BaseSortByLetterAdapter; import java.util.ArrayList; /** * Created by mjc on 2016/5/24. */ public class ContactAdapter extends BaseSortByLetterAdapter<ContactEntity> { public ContactAdapter(ArrayList<ContactEntity> datas) { super(datas); } @Override protected String getSortString(ContactEntity bean) { return bean.getSortName(); } @Override public int getCount() { return datas.size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { Holder mHolder; if (convertView == null) { mHolder = new Holder(); convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_contact, null); mHolder.mNameTv = (TextView) convertView.findViewById(R.id.tv_name); mHolder.mIndexTv = (TextView) convertView.findViewById(R.id.tv_index); convertView.setTag(mHolder); } else { mHolder = (Holder) convertView.getTag(); } //强转需要注意原来的类型 ContactEntity bean = datas.get(position); //获取对应字母的位置 int index = getSectionForPosition(position); //比较列表中第一个字母的位置和这个位置是否相等 if (getPositionForSection(index) == position) { mHolder.mIndexTv.setVisibility(View.VISIBLE); mHolder.mIndexTv.setText(sections[index]); } else { mHolder.mIndexTv.setVisibility(View.GONE); } mHolder.mNameTv.setText(bean.getName()); return convertView; } class Holder { public TextView mNameTv; public TextView mIndexTv; } }
item_contact.xml
<?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 android:id="@+id/tv_index" style="@style/KeyStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#f4f6f3" android:paddingTop="12dp" android:paddingBottom="4dp" android:textSize="14sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#fdfdfd" android:gravity="center_vertical" android:orientation="horizontal"> <ImageView android:layout_width="50dp" android:layout_height="50dp" android:paddingBottom="5dp" android:paddingLeft="10dp" android:paddingTop="5dp" android:src="@mipmap/ic_launcher" android:visibility="gone" /> <TextView android:id="@+id/tv_name" style="@style/InputStyle" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:padding="10dp" android:textSize="16sp" /> </LinearLayout> </LinearLayout>
MainActivity.class
package com.mjc.contactlistdemo.core.contact; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.ListView; import com.mjc.contactlistdemo.R; import com.mjc.contactlistdemo.core.contact.entity.ContactEntity; import com.mjc.contactlistdemo.sort_by_letter.IndexView; import com.mjc.contactlistdemo.sort_by_letter.LetterWindow; import com.mjc.contactlistdemo.sort_by_letter.SortUtil; import java.util.ArrayList; public class MainActivity extends AppCompatActivity implements IndexView.OnCharTouchEvent { private LetterWindow mLetterWindow; private ContactAdapter mContactAdapter; private IndexView mIndexView; private ListView mList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mList = (ListView) findViewById(R.id.list); mIndexView = (IndexView) findViewById(R.id.civ); mIndexView.setOnLetterTouchedListener(this); mLetterWindow = new LetterWindow(this); ArrayList<ContactEntity> list = new ArrayList<>(); ContactEntity bean1 = new ContactEntity(); bean1.setName("单熊信"); ContactEntity bean2 = new ContactEntity(); bean2.setName("谢天华"); ContactEntity bean3 = new ContactEntity(); bean3.setName("李自成"); ContactEntity bean4 = new ContactEntity(); bean4.setName("段天涯"); ContactEntity bean5 = new ContactEntity(); bean5.setName("张无忌"); ContactEntity bean6 = new ContactEntity(); bean6.setName("小红"); ContactEntity bean7 = new ContactEntity(); bean7.setName("李寻欢"); ContactEntity bean8 = new ContactEntity(); bean8.setName("王小亚"); ContactEntity bean9 = new ContactEntity(); bean9.setName("夏冬青"); ContactEntity bean10 = new ContactEntity(); bean10.setName("上官锦"); ContactEntity bean11 = new ContactEntity(); bean11.setName("炎亚纶"); ContactEntity bean12 = new ContactEntity(); bean12.setName("刘德华"); ContactEntity bean13 = new ContactEntity(); bean13.setName("陈浩民"); ContactEntity bean14 = new ContactEntity(); bean14.setName("马云"); ContactEntity bean15 = new ContactEntity(); bean15.setName("雷军"); ContactEntity bean16 = new ContactEntity(); bean16.setName("周宏伟"); ContactEntity bean17 = new ContactEntity(); bean17.setName("李易峰"); ContactEntity bean18 = new ContactEntity(); bean18.setName("鹿晗"); ContactEntity bean19 = new ContactEntity(); bean19.setName("邓超"); ContactEntity bean20 = new ContactEntity(); bean20.setName("李晨"); ContactEntity bean21 = new ContactEntity(); bean21.setName("张翰"); ContactEntity bean22 = new ContactEntity(); bean22.setName("邓丽君"); ContactEntity bean23 = new ContactEntity(); bean23.setName("曾志伟"); ContactEntity bean24 = new ContactEntity(); bean24.setName("阿甘"); ContactEntity bean25 = new ContactEntity(); bean25.setName("爸比"); ContactEntity bean26 = new ContactEntity(); bean26.setName("东方彧卿"); ContactEntity bean27 = new ContactEntity(); bean27.setName("方世玉"); ContactEntity bean28 = new ContactEntity(); bean28.setName("高芳"); ContactEntity bean29 = new ContactEntity(); bean29.setName("海大富"); ContactEntity bean30 = new ContactEntity(); bean30.setName("江离"); ContactEntity bean31 = new ContactEntity(); bean31.setName("康辉"); ContactEntity bean32 = new ContactEntity(); bean32.setName("牛郎"); ContactEntity bean33 = new ContactEntity(); bean33.setName("谢天华"); ContactEntity bean34 = new ContactEntity(); bean34.setName("单雄心"); ContactEntity bean35 = new ContactEntity(); bean35.setName("赛华佗"); list.add(bean1); list.add(bean2); list.add(bean3); list.add(bean4); list.add(bean5); list.add(bean6); list.add(bean7); list.add(bean8); list.add(bean9); list.add(bean10); list.add(bean11); list.add(bean12); list.add(bean13); list.add(bean14); list.add(bean15); list.add(bean16); list.add(bean17); list.add(bean18); list.add(bean19); list.add(bean20); list.add(bean21); list.add(bean22); list.add(bean23); list.add(bean24); list.add(bean25); list.add(bean26); list.add(bean27); list.add(bean28); list.add(bean29); list.add(bean30); list.add(bean31); list.add(bean32); list.add(bean33); list.add(bean34); list.add(bean35); SortUtil.sortByLetter(list); mContactAdapter = new ContactAdapter(list); mList.setAdapter(mContactAdapter); } @Override public void onTouch(String s) { mLetterWindow.show(s); int index = mContactAdapter.getSectionIndex(s); int position = mContactAdapter.getPositionForSection(index); if (position != -1) mList.setSelection(position); } @Override public void onLetterChanged(String preLetter, String letter) { mLetterWindow.update(letter); int index = mContactAdapter.getSectionIndex(letter); int position = mContactAdapter.getPositionForSection(index); if (position != -1) mList.setSelection(position); } @Override public void onRelease() { mLetterWindow.hide(); } }
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> <FrameLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> <ListView android:id="@+id/list" style="@style/CustomList" android:visibility="gone" android:layout_width="match_parent" android:layout_height="match_parent" /> <com.mjc.contactlistdemo.sort_by_letter.IndexView android:id="@+id/civ" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="right" /> </FrameLayout> </LinearLayout>
以上就是使用的过程,还是很简便的。
到这里,这个字母索引排序就算是写完了,谢谢!