Android 实现A-Z字母排序的ListView的检索

手机通讯录实现A-Z字母排序的ListView检索

首先看下效果图,分析下效果图,
类似通讯录效果的按拼音首字母排序效果
整体上来说分为两个部分,第一部分是最右侧的自定义View,第二部分就是ListView了。看效果图发现它的主要功能有:①展示通讯录 ②将通讯录按照首字母分类排序 ③滑动右边的字母的同时左边的ListView会跳到相应的位置等。通过这个demo将学到以下的知识点:

  • 自定义控件的相关知识
  • pinyin4j.jar的用法
  • BaseAdapter的使用

废话不多说,下面我们来一步一步实现上面的效果。

1、右侧自定义View的实现

       实现这样一个效果就是滑动最右边自定义的View,然后界面中间展示所滑到的字母。首先分析一下,我的想法是:
       ① 需要用canvas的draw方法”A-Z”画上去,怎么画上去呢?先计算每个字符所占的高度,然后通过改变Y坐标的值,沿竖直方向依次将字母绘画上去,在自定义View中通过这样的代码实现:

//计算每个字符所占的高度
        float singleHeight = getHeight() / letter.length;
        int width = getWidth();
        for (int i=0;i<letter.length;i++)
        {
            String text = letter[i];
            float xPosition = width/2-mPaint.measureText(text)/2;
            //不断的改变Y坐标值
            float yPosition = singleHeight*i+singleHeight;
            //通过不断的改变yPosition将数组中的数据一个一个绘制到自定义的View中去
            canvas.drawText(text,xPosition,yPosition,mPaint);
        }

       ② 在滑动时如何通知Activity滑动到哪儿了?通过一个mOnSlidingListener监听,在滑动时将当前滑到的字母通过回调方式即用mOnSlidingListener.sliding ( letter[position] ) 传递给Activity,下面附上自定义View的代码:

/**重写了onTouchEvent方法,然后通过监听down,move,up事件来执行相关的操作,
 * 当down时首先会改变整个view的背景色,
 * 然后将当前滑到的字母通过回调的方式即调用mOnSlidingListener.sliding(letter[position])(
 * 这里的mOnSlidingListener就是在activity中的setOnSlidingListener所注册的监听器)传递到Activity中*/
/**
 * Created by WuTing on 2016/8/16.
 */
public class MyCharView extends View {


    private Paint mPaint;
    private boolean isShowBg = false;//用于区分是否显示view的背景
    private OnSlidingListener mOnSlidingListener;//滑动此view的监听器
    private int choose = -1;//用于标记当前所选中的位置
    private TextView mTvDialog;//用于接收从activity中传过来的,中间用于展示字母的textView
    //需要展示的数据
    private String[] letter = { "A", "B", "C", "D",
            "E", "F", "G", "H","I","J", "K", "L", "M", "N","O","P", "Q",
            "R", "S", "T","W", "X", "Y", "Z" ,"#"};


    /**构造方法*/
    public MyCharView(Context context) {
        this(context,null);//继承参数两个的方法
    }

    public MyCharView(Context context, AttributeSet attrs) {
        this(context,attrs,0);//继承参数三个的方法
    }

    public MyCharView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setTextSize(26);
        mPaint.setColor(Color.parseColor("#8c8c8c"));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //当此View被按下时所显示的背景颜色
        if (isShowBg)
        {
            canvas.drawColor(Color.parseColor("#40000000"));
        }
        //计算每个字符所占的高度
        float singleHeight = getHeight() / letter.length;
        int width = getWidth();
        for (int i=0;i<letter.length;i++)
        {
            String text = letter[i];
            float xPosition = width/2-mPaint.measureText(text)/2;
            //不断的改变Y坐标值
            float yPosition = singleHeight*i+singleHeight;
            //通过不断的改变yPosition将数组中的数据一个一个绘制到自定义的View中去
            canvas.drawText(text,xPosition,yPosition,mPaint);
        }
    }

    public boolean onTouchEvent(MotionEvent event) {

        int action = event.getAction();
        int position = (int) (event.getY()/getHeight()*letter.length);
        int oldChoose = choose;
        switch (action)
        {
            case MotionEvent.ACTION_DOWN:

                isShowBg = true;
                if (oldChoose != position && mOnSlidingListener!=null)
                {
                    if (position > 0 && position < letter.length)
                    {
                        //将点中的字母变色
                        //将滑动的字母传递到activity中
                        mOnSlidingListener.sliding(letter[position]);
                        choose = position;
                        if (mTvDialog != null)
                        {
                            mTvDialog.setVisibility(VISIBLE);
                            mTvDialog.setText(letter[position]);
                        }
                    }
                    /**  请求重新绘制View,即重新调用draw()方法 */
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_MOVE:

                isShowBg = true;
                if (oldChoose != position && mOnSlidingListener != null) {
                    if (position >=0 && position < letter.length) {
                        mOnSlidingListener.sliding(letter[position]);
                        choose=position;
                        if(mTvDialog!=null){
                            mTvDialog.setVisibility(View.VISIBLE);
                            mTvDialog.setText(letter[position]);
                        }
                    }
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                isShowBg = false;
                choose = -1;
                if (mTvDialog!=null)
                {
                    mTvDialog.setVisibility(GONE);
                }
                /**  请求重新绘制View,即重新调用draw()方法 */
                invalidate();
                break;
        }

        return true;
    }

    //MyLetterView的一个滑动的监听
    public void setOnSlidingListener(OnSlidingListener mOnSlidingListener) {
        this.mOnSlidingListener = mOnSlidingListener;
    }
    public interface OnSlidingListener {
        public void sliding(String str);
    }

    public void setTextView(TextView tvDialog) {
        mTvDialog=tvDialog;
    }

}

布局文件 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_dialog"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_centerInParent="true"
        android:background="@mipmap/circle"
        android:gravity="center"
        android:textColor="#ffffffff"
        android:textSize="30dp"
        android:visibility="gone" />
    <myWidget.MyCharView
        android:id="@+id/my_letterview"
        android:layout_width="25dp"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:layout_marginRight="2dp"
        android:layout_marginTop="7dp"/>
    <ListView
        android:id="@+id/ltv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_toLeftOf="@id/my_letterview"
        android:scrollbars="none"
        android:divider="#c1c1c1"
        android:dividerHeight="0.5dp">

    </ListView>
</RelativeLayout>

MainActivity代码:

public class MainActivity extends AppCompatActivity {

    private MyCharView mMyCharView;
    private TextView tvDialog;
    private ListView ltv;
    private ContactsAdapter mContactsAdapter;
    private List<Contacts> allContactsList = new ArrayList<Contacts>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//        getContacts();
        initView();
        initEvents();

    }
    private void initEvents() {
        //将中间展示字母的TextView传递到myLetterView中,并在其中控制他的显示与隐藏
        mMyCharView.setTextView(tvDialog);
        //注册mMyLetterView中监听(跟setOnClickListener这种系统默认写好的监听一样只不过这里是我们自己写的)
        //mContactsAdapter = new ContactsAdapter(this,allContactsList);
        //Log.e("all  Length",allContactsList.size()+"人");
        //ltv.setAdapter(mContactsAdapter);
        mMyCharView.setOnSlidingListener(new MyCharView.OnSlidingListener() {
            @Override
            public void sliding(String str) {
                //if (mContactsAdapter.alphaIndexer.get(str) != null)
                //{
                    //根据MyLetterView滑动到的数据获得ListView应该展示的位置
                    //int position = mContactsAdapter.alphaIndexer.get(str);
                    //将listView展示到相应的位置
                    //ltv.setSelection(position);
                //}
                tvDialog.setText(str);
            }
        });
    }

    private void initView() {
        tvDialog = (TextView) findViewById(R.id.tv_dialog);
        ltv = (ListView) findViewById(R.id.ltv);
        mMyCharView = (MyCharView) findViewById(R.id.my_letterview);

    }

在MAinActivity中可以看到MyCharView注册了在MyCharView中的监听,通过回调的方式将MyCharView中滑动到的文字传递给MainAcitivity中,并通过tvDialog显示在屏幕中间,它的效果图如下:
这里写图片描述


2、ListView数据的展示

       数据展示,我的想法是:首先的到手机通讯录的内容,再得到每个通讯人名字的拼音,然后根据拼音将通讯录中的人名进行排序,排序后显示在ListView中,判断相邻的两个数据的拼音首字母是否相同,不同的话就显示tv_char(即起分隔作用的大写字母)。下面来一步一步实现listView的数据展示:
       ① 得到手机通讯录的内容,通过context.getContentResolver()就可以获取手机中的通讯录信息。GetContacts具体实现代码如下:

public class GetContacts{

    public static List<Contacts> getContacts(Context context)
    {
        List<Contacts> mList = new ArrayList<Contacts>();
        List<String> tokenList = new ArrayList<String>();
        //context.getContentResolver()获取手机中的通讯录信息
        Cursor cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
        String phoneNumber;
        String phoneName;
        while (cursor.moveToNext())
        {
            phoneNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
            phoneName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.SORT_KEY_ALTERNATIVE));

            Contacts contacts = new Contacts();
            contacts.setName(phoneName);
            contacts.setNumber(phoneNumber);
            //添加每个联系人信息到mList中
            mList.add(contacts);
        }
        return mList;
    }
}

需要构造一个实体类Contacts来存储联系人信息,Contacts的代码如下:
Contacts 实体类:

/**
 *Contacts  联系人实体类 
 **/
public class Contacts {

    String name;
    String number;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
}

       ② 得到每个联系人的全拼和拼音首字母,这里我用的是pinyin4j.jar。(pinyin4j是一个支持将简体和繁体中文转换到成拼音的Java开源类库,pinyin4j的官方下载地址:http://sourceforge.net/projects/pinyin4j/files/ ,下载解压后lib中的pinyin4j-2.5.0.jar就是要用的pinyin4j的jar包)不多说,上代码:
GetHeadPin工具类:

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;

/**
 * Created by WuTing on 2016/8/17.
 */
public class GetHeadPin {

    /**将汉字转换为全拼
     * */
    public static String getPinYin(String src)
    {
        char[] t1 = null;
        t1 = src.toCharArray();
        String[] t2 = new String[t1.length];

        //设置汉字拼音输出的格式
        HanyuPinyinOutputFormat t3 = new HanyuPinyinOutputFormat();
        t3.setCaseType(HanyuPinyinCaseType.UPPERCASE);
        t3.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
        t3.setVCharType(HanyuPinyinVCharType.WITH_V);

        String t4 = "";
        int t0 = t1.length;
        try {
            for (int i=0;i<t0;i++)
            {
                //判断能否为汉字字符
                System.out.println(t1[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;
    }
    /**
     * 提取每个汉字拼音的首字母
     *
     */
    public static String getPinYinHeadChar(String str)
    {
        String convert = "";
        for (int j = 0;j<str.length();j++)
        {
            char word = str.charAt(0);
            //提取汉字的首字母
            String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(word);
            if (pinyinArray != null)
            {
                convert = pinyinArray[0].charAt(0)+"";
            }
            else
            {
                convert = word+"";
            }
        }
        return convert;
    }

}

       ③ 按照拼音字母排序,其实就是用Java中的一个Collections.sort(allContactsList,comparator)排序函数的用法,comparator的代码如下:

/**
* 自定义的排序规则,按照A-Z进行排序
*/
    @SuppressWarnings("rawtypes")
    Comparator comparator = new Comparator<Contacts>() {
        @Override
        public int compare(Contacts lhs, Contacts rhs) {
            String a = new GetHeadPin().getPinYin(lhs.getName()).substring(0, 1);
            String b = new GetHeadPin().getPinYin(rhs.getName()).substring(0, 1);
            int flag = a.compareTo(b);
            if (flag == 0) {
                return a.compareTo(b);
            } else {
                return flag;
            }
        }
    }; 

       ④ 在ContactsAdapter适配器中判断相邻联系人拼音首字母是否相同,如果不相同的话,将布局中的展示字母的TextView展示出来,ContactsAdapter代码如下:
ContactsAdapter适配器:

public class ContactsAdapter extends BaseAdapter {

    private Context mContext;
    private List<Contacts> mNameList;//通讯录姓名列表集合
    public HashMap<String,Integer> alphaIndexer = new HashMap<String,Integer>();//存放存在的汉语拼音首字母和与之对应的列表位置
    private String[] sections;//存放存在的汉语拼音首字母
    private ViewHolder mViewHolder;

    public ContactsAdapter(Context context,List<Contacts> nameList) {
        mNameList = nameList;
        mContext = context;
        sections = new String[mNameList.size()];
        for (int i=0;i<mNameList.size();i++)
        {
            //当前汉语拼音首字母
            String currentStr = new GetHeadPin().getPinYinHeadChar(new GetHeadPin().getPinYin(mNameList.get(i).getName()));
            //上一个汉语拼音首字母,如果不存在为""
            String previewStr = (i - 1)>= 0? new GetHeadPin().getPinYinHeadChar(new GetHeadPin().getPinYin(mNameList.get(i-1).getName())):"";
            if (!previewStr.equals(currentStr))
            {
                String name = new GetHeadPin().getPinYinHeadChar(new GetHeadPin().getPinYin(mNameList.get(i).getName()));
                alphaIndexer.put(name,i);
                sections[i] = name;
            }
        }
    }


    @Override
    public int getCount() {
        return mNameList.size();
    }

    @Override
    public Object getItem(int position) {
        return mNameList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {

        if (convertView == null)
        {
            mViewHolder = new ViewHolder();
            convertView = View.inflate(mContext, R.layout.contacts_item,null);
            mViewHolder.tv_alpha = (TextView) convertView.findViewById(R.id.tv_char);
            mViewHolder.view_devide = (View) convertView.findViewById(R.id.view_devide);
            mViewHolder.mNameTextView = (TextView) convertView.findViewById(R.id.txt_contact_name);
            mViewHolder.mNumberTextView = (TextView) convertView.findViewById(R.id.txt_contact_number);
            mViewHolder.ll_main = (LinearLayout) convertView.findViewById(R.id.ll_main);
            convertView.setTag(mViewHolder);
        }
        else
        {
            mViewHolder = (ViewHolder) convertView.getTag();
        }

        if (position >= 1)
        {
            mViewHolder.mNameTextView.setText(mNameList.get(position).getName().toString());
            mViewHolder.mNumberTextView.setText(mNameList.get(position).getNumber().toString());
            mViewHolder.ll_main.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext,mNameList.get(position).toString(), Toast.LENGTH_SHORT).show();
                }
            });
            //当前汉语拼音首字母
            String currentStr = new GetHeadPin().getPinYinHeadChar(new GetHeadPin().getPinYin(mNameList.get(position).getName()));
            //上一个汉语拼音首字母,如果不存在为""
            String previewStr = (position - 1)>= 0? new GetHeadPin().getPinYinHeadChar(new GetHeadPin().getPinYin(mNameList.get(position-1).getName())):"";
            //如果当前的条目的通讯录名字的拼音首字母   和 其前一条条目的名字首字母不相同,
            // 则将布局中的展示字母的TextView展示出来
            if (!previewStr.equals(currentStr))
            {
                mViewHolder.tv_alpha.setText(currentStr);
                mViewHolder.tv_alpha.setVisibility(View.VISIBLE);
                mViewHolder.view_devide.setVisibility(View.VISIBLE);
            }
            else
            {
                mViewHolder.tv_alpha.setVisibility(View.GONE);
                mViewHolder.view_devide.setVisibility(View.GONE);
            }
        }
        return convertView;
    }


    private class ViewHolder {
        TextView tv_alpha;
        View view_devide;
        TextView mNameTextView;
        TextView mNumberTextView;
        LinearLayout ll_main;
    }
}

最终的MainActivity代码:

public class MainActivity extends AppCompatActivity {

    private MyCharView mMyCharView;
    private TextView tvDialog;
    private ListView ltv;
    private ContactsAdapter mContactsAdapter;
    private List<Contacts> allContactsList = new ArrayList<Contacts>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getAllContacts();
        initView();
        initEvents();

    }
    private void initEvents() {
        //将中间展示字母的TextView传递到myLetterView中,并在其中控制他的显示与隐藏
        mMyCharView.setTextView(tvDialog);
        //注册mMyLetterView中监听(跟setOnClickListener这种系统默认写好的监听一样只不过这里是我们自己写的)
        mContactsAdapter = new ContactsAdapter(this,allContactsList);
        ltv.setAdapter(mContactsAdapter);
        mMyCharView.setOnSlidingListener(new MyCharView.OnSlidingListener() {
            @Override
            public void sliding(String str) {
                if (mContactsAdapter.alphaIndexer.get(str) != null)
                {
                    //根据MyLetterView滑动到的数据获得ListView应该展示的位置
                    int position = mContactsAdapter.alphaIndexer.get(str);
                    //将listView展示到相应的位置
                    ltv.setSelection(position);
                }
                tvDialog.setText(str);
            }
        });
    }

    private void initView() {
        tvDialog = (TextView) findViewById(R.id.tv_dialog);
        ltv = (ListView) findViewById(R.id.ltv);
        mMyCharView = (MyCharView) findViewById(R.id.my_letterview);

    }

    private void getAllContacts() {
        allContactsList = GetContacts.getContacts(this);
        Collections.sort(allContactsList,comparator);
    }

    /**
     * 自定义的排序规则,按照A-Z进行排序
     */
    @SuppressWarnings("rawtypes")
    Comparator comparator = new Comparator<Contacts>() {
        @Override
        public int compare(Contacts lhs, Contacts rhs) {
            String a = new GetHeadPin().getPinYin(lhs.getName()).substring(0, 1);
            String b = new GetHeadPin().getPinYin(rhs.getName()).substring(0, 1);
            int flag = a.compareTo(b);
            if (flag == 0) {
                return a.compareTo(b);
            } else {
                return flag;
            }
        }
    };


}

源码地址

一只刚入门小菜鸟的第一篇文章,难免会有疏漏或者不恰当的地方。如果你有什么疑问,或者发现文章中的错误,欢迎批评指正,谢谢!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值