在开发app的过程中,如果用到通讯录或者类似的列表,需要快速在其中定位,可以根据列表项的拼音首字母来定位,这时候就需要用到右侧字母索引了。必如现在的微信通讯录界面就是如此。在实现这种功能的过程中,还是挺复杂的,很难我觉得。在网上各种查找资料,困难重重,好在最后终于捯饬出来了,伤不起。。。。特此记录一下写的过程。
1、创建自定的view,用作右侧列表索引。
- public class RulerWidget extends View {
- public static String[] indexStr = {
- "#", "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"
- };
- public static int INDEX_LENGTH = indexStr.length;
- OnTouchingLetterChangedListener onTouchingLetterChangedListener;
- Paint mPaint = new Paint();
- boolean showBkg = false;
- int choose = -1;
- public RulerWidget(Context context) {
- super(context);
- }
- public RulerWidget(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
- public RulerWidget(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- // if(showBkg){
- canvas.drawColor(Color.parseColor("#40000000"));
- // }
- int height = getHeight();
- int width = getWidth();
- int singleHeight = height / indexStr.length;
- for(int i=0;i<indexStr.length;i++){
- mPaint.setColor(Color.WHITE);
- mPaint.setTextSize(24);
- mPaint.setTypeface(Typeface.DEFAULT_BOLD);
- mPaint.setAntiAlias(true);
- if(i == choose){
- mPaint.setColor(Color.parseColor("#3399ff"));
- mPaint.setFakeBoldText(true);
- }
- float xPos = width/2 - mPaint.measureText(indexStr[i])/2;
- float yPos = singleHeight * i + singleHeight;
- canvas.drawText(indexStr[i], xPos, yPos, mPaint);
- mPaint.reset();
- }
- }
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- final int action = event.getAction();
- final float y = event.getY();
- final int oldChoose = choose;
- final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
- final int c = (int) (y/getHeight()*indexStr.length);
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- showBkg = true;
- if(oldChoose != c && listener != null){
- if(c > 0 && c< indexStr.length){
- listener.onTouchingLetterChanged(indexStr[c]);
- choose = c;
- invalidate();
- }
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if(oldChoose != c && listener != null){
- if(c > 0 && c< indexStr.length){
- listener.onTouchingLetterChanged(indexStr[c]);
- choose = c;
- invalidate();
- }
- }
- break;
- case MotionEvent.ACTION_UP:
- // showBkg = false;
- choose = -1;
- invalidate();
- break;
- }
- return true;
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- return super.onTouchEvent(event);
- }
- public void setOnTouchingLetterChangedListener(
- OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
- this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
- }
- }
对应的回调接口定义:
- /**
- * @date 2014-9-3
- * @Description: ruler触摸回调
- */
- public interface OnTouchingLetterChangedListener{
- public void onTouchingLetterChanged(String s);
- }
2、创建fragment片段对应的布局文件:
- <?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" >
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#ffffff" >
- <ListView
- android:id="@+id/listView"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:cacheColorHint="#00000000"
- android:fadingEdge="none"
- android:scrollbars="none" >
- </ListView>
- <TextView
- android:id="@+id/tv"
- android:layout_width="60dp"
- android:layout_height="60dp"
- android:gravity="center"
- android:background="#f0606060"
- android:layout_gravity="center"
- android:text="A"
- android:textColor="#ffffff"
- android:textSize="30sp" />
- <com.hy.ticket.view.RulerWidget
- android:id="@+id/sidrbar"
- android:layout_width="30.0dip"
- android:layout_height="fill_parent"
- android:layout_gravity="right|center" />
- </FrameLayout>
- </LinearLayout>
3、引入pinyin4j.jar包(版本建议使用最新),写一个工具类,包含获取中文字符串拼音首字母,获取中文拼音等方法。
- public class StringHelper {
- public static String getPingYin(String src) {
- char[] t = src.toCharArray();
- String[] strs = new String[t.length];
- HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
- format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
- format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
- format.setVCharType(HanyuPinyinVCharType.WITH_V);
- String str = "";
- try{
- for(int i=0; i<t.length; i++) {
- if(java.lang.Character.toString(t[i]).matches(
- "[\\u4E00-\\u9FA5]+")) {
- strs = PinyinHelper.toHanyuPinyinStringArray(t[i], format);
- str += strs[0];
- }else{
- str += java.lang.Character.toString(t[i]);
- }
- }
- //长沙,长春多音字处理
- if(str.equals("zhangsha")) {
- str = "changsha";
- }
- if(str.equals("zhangchun")) {
- str = "changchun";
- }
- return str;
- }catch(Exception e) {
- e.printStackTrace();
- }
- return str;
- }
- public static String getHeaderChar(String src) {
- String convert = "";
- char word = src.charAt(0);
- String[] arr = PinyinHelper.toHanyuPinyinStringArray(word);
- if(arr != null) {
- convert += arr[0].charAt(0);
- }else{
- convert += word;
- }
- return convert.toUpperCase();
- }
- public static String getPinYinHeaderChar(String src) {
- String convert = "";
- for(int i=0; i<src.length(); i++) {
- char word = src.charAt(i);
- String[] arr = PinyinHelper.toHanyuPinyinStringArray(word);
- if(arr != null) {
- convert += arr[0].charAt(0);
- }else{
- convert += word;
- }
- }
- return convert.toUpperCase();
- }
- }
4、建立一个工具类,用于根据首字母排序,获取大些字母及对应的中文字符串(如A 阿尔法 B 北京 C 长沙 长春 常州等):
- public class SortUtil {
- public static List<Station> sortList(String[] srcNames, List<Station> list) {
- List<Station> newList = new ArrayList<Station>();
- for(int i=0; i<srcNames.length; i++) {
- if(srcNames[i].length() != 1) {
- for(int j=0; j<list.size(); j++) {
- if(srcNames[i].equals(list.get(j).getPinYinName())) {
- Station s = new Station(list.get(j).getName(), list.get(j).getPinYinName());
- newList.add(s);
- }
- }
- }else{
- newList.add(new Station(srcNames[i]));
- }
- }
- return newList;
- }
- public static String[] sortIndex(List<Station> stations) {
- TreeSet<String> set = new TreeSet<String>();
- // 获取初始化数据源中的首字母,添加到set中
- for (Station station : stations) {
- set.add(StringHelper.getPinYinHeaderChar(station.getName()).substring(
- 0, 1));
- }
- // 新数组的长度为原数据加上set的大小
- String[] names = new String[stations.size() + set.size()];
- int i = 0;
- for (String string : set) {
- names[i] = string;
- i++;
- }
- String[] pinYinNames = new String[stations.size()];
- for (int j = 0; j < stations.size(); j++) {
- stations.get(j).setPinYinName(
- StringHelper
- .getPingYin(stations.get(j).getName().toString()));
- pinYinNames[j] = StringHelper.getPingYin(stations.get(j).getName()
- .toString());
- }
- // 将原数据拷贝到新数据中
- System.arraycopy(pinYinNames, 0, names, set.size(), pinYinNames.length);
- // 自动按照首字母排序
- Arrays.sort(names, String.CASE_INSENSITIVE_ORDER);
- return names;
- }
- }
listViewAdapter,填充ListView的适配器:
- public class ListViewAdapter extends BaseAdapter {
- private Context context;
- private List<Station> stations;
- private ViewHolder viewHolder;
- public ListViewAdapter(Context context, List<Station> stations) {
- this.context = context;
- this.stations = stations;
- }
- @Override
- public int getCount() {
- return stations.size();
- }
- @Override
- public Object getItem(int position) {
- return stations.get(position);
- }
- @Override
- public long getItemId(int position) {
- return position;
- }
- @Override
- public boolean isEnabled(int position) {
- if (stations.get(position).getName().length() == 1)// 如果是字母索引
- return false;// 表示不能点击
- return super.isEnabled(position);
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- String item = stations.get(position).getName();
- viewHolder = new ViewHolder();
- if(item.length() == 1) {
- convertView = LayoutInflater.from(context).inflate(R.layout.index, null);
- TextView indexTV = (TextView) convertView.findViewById(R.id.indexTV);
- indexTV.setText(stations.get(position).getName());
- viewHolder.indexTV = indexTV;
- }else{
- convertView = LayoutInflater.from(context).inflate(R.layout.item, null);
- TextView itemTV = (TextView) convertView.findViewById(R.id.itemTV);
- itemTV.setText(stations.get(position).getName());
- viewHolder.itemTV = itemTV;
- }
- return convertView;
- }
- //内部类
- private class ViewHolder {
- private TextView itemTV;
- private TextView indexTV;
- }
- }
5、最后一步,建立布局文件对应的fragment片段(或Activity,这里使用的是fragment):
- public class ListStation extends Fragment {
- private Map<String, Integer> selector;
- private LinearLayout layoutIndex;
- private ListView listView;
- private TextView textView;
- private ListViewAdapter adapter;
- private String[] sections = {
- "#", "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"
- };
- private List<Station> stations;
- private List<Station> newStations;
- private int height;
- private boolean flag = false;
- private LinearLayout layout;
- private RulerWidget ruler;
- private Handler handler;
- private OverlayThread overlayThread;
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.station_list, container, false);
- listView = (ListView) view.findViewById(R.id.listView);
- textView = (TextView) view.findViewById(R.id.tv);
- ruler = (RulerWidget) view.findViewById(R.id.sidrbar);
- ruler.setOnTouchingLetterChangedListener(new LetterListViewListener());
- initOverlay();
- return view;
- }
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- stations = StationService.getAllStation();
- String[] allNames = SortUtil.sortIndex(stations);
- newStations = SortUtil.sortList(allNames, stations);
- //这个map是建立大写字母对应位于listView位置的索引
- selector = new HashMap<String, Integer>();
- for(int i=0; i<allNames.length; i++) {
- if(allNames[i].length() == 1) {
- selector.put(allNames[i], i);
- }
- }
- adapter = new ListViewAdapter(getActivity(), newStations);
- listView.setAdapter(adapter);
- handler = new Handler();
- overlayThread = new OverlayThread();
- }
- //初始化汉语拼音首字母弹出提示框
- private void initOverlay() {
- LayoutInflater inflater = LayoutInflater.from(getActivity());
- // overlay = (TextView) inflater.inflate(R.layout.overlay, null);
- textView.setVisibility(View.INVISIBLE);
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_APPLICATION,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
- PixelFormat.TRANSLUCENT);
- WindowManager windowManager = (WindowManager) getActivity().getSystemService(Context.WINDOW_SERVICE);
- // windowManager.addView(textView, lp);
- }
- private class LetterListViewListener implements OnTouchingLetterChangedListener{
- @Override
- public void onTouchingLetterChanged(final String s) {
- if(selector.get(s) != null) {
- int position = selector.get(s);
- listView.setSelection(position);
- textView.setText(s);
- textView.setVisibility(View.VISIBLE);
- handler.removeCallbacks(overlayThread);
- //延迟一秒后执行,让overlay为不可见
- handler.postDelayed(overlayThread, 1500);
- }
- }
- }
- //设置overlay不可见
- private class OverlayThread implements Runnable {
- @Override
- public void run() {
- textView.setVisibility(View.GONE);
- }
- }
- /*class GetDataAsyTask extends AsyncTask {
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- }
- @Override
- protected Object doInBackground(Object... params) {
- stations = StationService.getAllStation();
- String[] allNames = SortUtil.sortIndex(stations);
- newStations = SortUtil.sortList(allNames, stations);
- if(newStations != null) {
- newStations.clear();
- newStations = null;
- }
- if(stations != null) {
- stations.clear();
- stations = null;
- }
- if(allNames != null) {
- allNames = null;
- }
- return null;
- }
- @Override
- protected void onPostExecute(Object result) {
- // super.onPostExecute(result);
- if(newStations == null) {
- // textView.setVisibility(View.VISIBLE);
- return;
- }
- handleSuccessData();
- }
- }
- private Integer getPosition(final int j) {
- Integer pos = null;
- int i = j;
- while (pos == null && i <= RulerWidget.indexStr.length - 1) {
- pos = selector.get(RulerWidget.indexStr[i]);
- i++;
- }
- if (pos == null) {
- pos = newStations.size() - 1;
- }
- return pos;
- }
- private void handleSuccessData() {
- listView.setVisibility(View.VISIBLE);
- ruler.setVisibility(View.VISIBLE);
- adapter = new ListViewAdapter(getActivity(), newStations);
- ruler.setOnRulerTouch(new OnRulerTouch() {
- @Override
- public void onUP() {
- }
- @Override
- public void onOthers() {
- }
- @Override
- public void onMove(int position) {
- textView.setText(RulerWidget.indexStr[position]);
- listView.setSelection(getPosition(position));
- }
- @Override
- public void onDown(int position) {
- textView.setVisibility(View.VISIBLE);
- textView.setText(RulerWidget.indexStr[position]);
- listView.setSelection(getPosition(position));
- }
- });
- listView.setAdapter(adapter);
- adapter.notifyDataSetChanged();
- }
- */
- }
这样,带拼音索引的listView建立完成。。