Android--实现具有字母检索功能的联系人列表

这两天一直在写一个联系人界面,尝试实现按字母检索的功能。在查询了一些资料后,终于实现了这一功能,期间要感谢我导师和大牛华的一些帮助。

废话不多说,先上效果图:
这里写图片描述
这里写图片描述
按住右侧的检索条可以跳到相应的位置,同时屏幕中间会出现一个相应的提示字符。

1、老规矩先写布局文件
这里就写一下主界面的xml
这里的字母索引框只是一个LinearLayout,具体的显示内容我们到主程序里面添加。

<?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:id="@+id/car_lists_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#f2f6f8">
<!-- 结果列表 -->
        <ListView
            android:id="@+id/listView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:cacheColorHint="#00000000"
            android:divider="@null"
            android:fadingEdge="none"
            android:scrollbars="none" >
        </ListView>
        <!-- 选择字母的提示 -->
        <TextView
            android:id="@+id/index_hint"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_gravity="center"
            android:background="@drawable/bg_contact_index"
            android:gravity="center"
            android:visibility="gone"
            android:textSize="51sp"
            android:textColor="#ffffff"/>
        <!-- 边框字母索引框 -->
        <LinearLayout
            android:id="@+id/layout"
            android:layout_width="20dip"
            android:layout_height="match_parent"
            android:layout_gravity="right"
            android:gravity="center"
            android:background="#00000000"
            android:orientation="vertical" 
            android:visibility="invisible">
        </LinearLayout>
</FrameLayout>
</LinearLayout>

这里采用了FrameLayout,因为FrameLayout把所有控件都叠在左上角,在这里用非常方便。

2、写contact的数据结构
这里只有两个参数就好了,名字和拼音。
因为要用到字母排序,所以这里要实现Comparable接口,按字典序排序。
这里调用String的compareToIgnoreCase来实现无视大小写的字母排序比较。
后面只需调用一个Collections.sort()就可以实现。

public class ContactBean implements Comparable{
    String name;
    String pinyin;

    public String getPinyin(){
     return pinyin;
    }

    public void setPinyin(String pinyin)
    {
     this.pinyin=pinyin;
    }

 public String getName() {
  return name;
 }
 public void setName(String nickName) {
  this.name = nickName;
 }
 @Override
 public int compareTo(Object arg0) {
      return pinyin.compareToIgnoreCase(((ContactBean) arg0).pinyin);
  }
}

3、实现排序算法
3.1 这里,先理一下思路,一般获取的都是都是汉字。所以第一步要实现汉字转化拼音。
汉字转换拼音用的是第三方的库,点击这里下载

 /** 
     * 将汉字转换为全拼 
     *  
     * @param src 
     * @return String 
     */ 
    public String getPinYin(String src) { 
        char[] t1 = null; 
        t1 = src.toCharArray(); 
        String[] t2 = new String[t1.length]; 
        // 设置汉字拼音输出的格式 
        HanyuPinyinOutputFormat t3 = new HanyuPinyinOutputFormat(); 
        //设置小写输出 
        t3.setCaseType(HanyuPinyinCaseType.LOWERCASE); 
        //设置无声调 
        t3.setToneType(HanyuPinyinToneType.WITHOUT_TONE); 
        //用v代表ü 
        t3.setVCharType(HanyuPinyinVCharType.WITH_V); 
        String t4 = ""; 
        int t0 = t1.length; 
        try { 
            for (int i = 0; i < t0; i++) { 
                // 判断能否为汉字字符  
                if (Character.toString(t1[i]).matches("[\\u4E00-\\u9FA5]+")) { 
                    t2 = PinyinHelper.toHanyuPinyinStringArray(t1[i], t3);// 将汉字的几种全拼都存到t2数组中 
                    t4 += t2[0];// 取出该汉字全拼的第一种读音并连接到字符串t4后 
                } else { 
                    // 如果不是汉字字符,间接取出字符并连接到字符串t4后 
                    t4 += Character.toString(t1[i]); 
                } 
            } 
        } catch (BadHanyuPinyinOutputFormatCombination e) { 
            e.printStackTrace(); 
        } 
        return t4; 
    }

3.2 接下来处理原始数据
对原始数据的进行处理,根据汉字设置ContactBean的pinyin。

 /** 
  * 初始化每项的拼音 
  *  
  * @param contactBeans 
  */    
    public void initPinYinData(List<ContactBean> contactBeans){ 

     for(ContactBean contactBean :contactBeans) 
     { 
        contactBean.setPinyin( getPinYin(contactBean.getName()) ); 
     } 

    }

3.3 可以对数据进行排序了
先在Activity里面定义一个两个list分别存放原始数据和排序后数据。
private List sourceData = new ArrayList();//原数据
private List datas;//保存转化后的数据(排序)
因为获取的原始数据可能含有数字,而按字典序排序之后,数字会出现在字母的前面,所以还需要一系列操作将数字放到字母后面。

 /** 
     * 按字母排序 
     * @param contactBeans 
     * @return 
     */   
    public LinkedList<ContactBean> sortIndex(List<ContactBean> contactBeans) 
    { 
        LinkedList<ContactBean> lists = new LinkedList<ContactBean>(contactBeans);//保存排好序的数据 
        Collections.sort(lists); 
        for(int i=0;i<lists.size();i++) 
        { 
             ContactBean temp=lists.peekFirst(); 
             if(isNumeric(temp.getPinyin())) 
             { 
                   lists.pollFirst(); 
                   lists.add(temp); 
             }       
        }            
     return lists;               
    }

因为已经实现了Comparable接口,所以只需调用Collections.sort()函数就可以了。
下半部分的操作是为将数字放到末尾。同时采用LinkedList是因为当数据量很大时,LinkedList的运算速度会比ArrayList快很多。
peekFirst()是获取列表的第一个元素,但是不移除它。pollFirst()则为获取列表第一个元素的同时移除该元素。

3.4.到上面数据已经处理好了,要做的就是将上面排序好的数据和索引建立映射关系,为跳转做好准备。
用HashMap来实现。

/** 
     * 建立索引 
     * @param contactBeans 
     */ 
    public void setDatas(List<ContactBean> contactBeans){ 
                datas= new ArrayList<ContactBean>( sortIndex(contactBeans) ); 
                selector = new HashMap<String, Integer>(); 
                //遍历排好序的数据,获取每个字母的位置 
               for (int i = 0; i < indexStr.length; i++) { 
                   for (int j = 0; j < datas.size(); j++) { 
                       if (datas.get(j).getPinyin().toLowerCase(Locale.getDefault()).startsWith(indexStr[i].toLowerCase(Locale.getDefault()))) 
                        { 
                            selector.put(indexStr[i], j); 
                             break; 
                        } 
                 String pinyin = datas.get(j).getPinyin(); 
                 if( indexStr[i].equals("#") && isNumeric(pinyin.substring(0,1))) 
                 { 
                   selector.put(indexStr[i], j); 
                    return; 
                 } 
              } 
        }    
    }

4.全部准备工作已经全部做好,可以绘制字母的索引框了。
同时绑定触摸到响应事件。

/** 
     * 绘制右侧的检索条 
     *  
     *  
     */ 

    public void drawIndexView(){ 
     LinearLayout.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, height); 

     for(int i=0;i<indexStr.length;i++) 
     { 
      TextView indexView=new TextView(this); 
      indexView.setLayoutParams(params); 
      indexView.setText(indexStr[i]); 
      indexView.setGravity(Gravity.CENTER); 
      indexView.setTextColor(Color.BLACK); 
      indexView.setTextSize(13); 
      layoutIndex.addView(indexView); 
     } 
     layoutIndex.setOnTouchListener(new OnTouchListener(){ 

   @Override 
   public boolean onTouch(View view, MotionEvent event) { 
    // TODO Auto-generated method stub 
    float y = event.getY(); 
    int index = (int) y / height;// 得到点击字母位置的索引 
    String key = ""; 
    if (index < indexStr.length && index > -1) { 
     key = indexStr[index]; 
     if(selector.containsKey(key)) 
     { 
      int position = selector.get(key); 
      if (mListView.getHeaderViewsCount() > 0){// 防止ListView有标题栏。 
       mListView.setSelectionFromTop( 
         position + mListView.getHeaderViewsCount(), 0); 
      } else { 
       mListView.setSelectionFromTop(position, 0);// 滑动到该字母第一项 
      } 

     } 
     if(key.equals("↑")) 
     { 
      mListView.setSelectionFromTop(0, 0);// 滑动到第一项 
     } 
     if(!key.equals("")) 
     { 
      indexHint.setText(key); 
      indexHint.setVisibility(View.VISIBLE); 
     } 
     switch (event.getAction()) { 
     case MotionEvent.ACTION_UP: 
     case MotionEvent.ACTION_CANCEL: 
     case MotionEvent.ACTION_OUTSIDE: 
      indexHint.setVisibility(View.GONE); 
      break; 
     case MotionEvent.ACTION_DOWN: 
      // layoutIndex.setBackground(); 
      break; 
     } 

    } 

    return true; 
   } 

     }); 


    }

4.1再根据布局的大小算出高度即可。

public void onDrawIndexView(int h)
 {
  if (!flag) {
   height = h / indexStr.length;
   drawIndexView();
   flag = true;
   layoutIndex.setVisibility(View.VISIBLE);
  }
 }

 public void drawIndex()
 {
  new Handler().postDelayed(new Runnable() {

   @Override
   public void run() {
    // TODO Auto-generated method stub
    if(layoutIndex.getMeasuredHeight() != 0)
    {
     onDrawIndexView(layoutIndex.getMeasuredHeight());
    }
    else
    {
     drawIndex();
    }
   }
  }, 1000);
 }

全部代码点这里

如有错误,欢迎指正,不胜感激!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值