Android手机通讯录


1、概述


最近由于项目需求,需要做一个查看手机通讯录,并且取出相应的数据。类似于下图:

技术分享

到的一个主要的知识点:SectionIndexer——能够有效地帮助我们对分组进行控制,由于SectionIndexer是一个接口,你可以自定义一个子类来实现SectionIndexer,

不过自己再写一个SectionIndexer的实现太麻烦了,这里我们直接使用Android提供好

的实现AlphabetIndexer,用它来实现联系人分组功能已经足够了。AlphabetIndexer的构造函数需要传入三个参数,第一个参数是cursor,

第二个参数是sortedColumnIndex整型,第三个参数是alphabet字符串。

其中cursor就是把我们从数据库中查出的游标传进去,

sortedColumnIndex就是指明我们是使用哪一列进行排序的,

而alphabet则是指定字母表排序规则,比如:"ABCDEFGHIJKLMNOPQRSTUVWXYZ"。

有了AlphabetIndexer,我们就可以通过它的getPositionForSection和getSectionForPosition方法,

找出当前位置所在的分组,和当前分组所在的位置,

从而实现类似于系统联系人的分组导航和挤压动画效果。

2、效果图

技术分享
大家可以清晰的看到,滑动界面,右侧的指示也会随着页面的变换而变换,
按住右侧,是按照字母来查询,由于这里是使用的genymotion,不能输入中文汉字,
所以都是英文的联系人,中文同样的效果。点击查询,字母的显示条就会消失,
这里使用的是模糊查询,只要名字中有你输入的关键字都会显示出来。






3、实现


下面我们就来开始实现,新建一个Android项目,命名为Contacts。首先我们还是先来完成布局文件,打开或新建activity_main.xml作为程序的主布局文件,代码会长一点,由于要实现右侧的A-Z的滑动栏,中间有一大部分的代码是类似的,这里就不贴出来了,感兴趣的朋友可以下载整个项目。
然后新建一个contact_item.xml的布局,这个布局用于在ListView中的每一行进行填充,代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#ffffff">

    <LinearLayout
        android:id="@+id/sort_key_layout"
        android:layout_width="fill_parent"
        android:layout_height="18dip"
        android:layout_marginRight="15dp"
        android:background="#64a300">

        <TextView
            android:id="@+id/sort_key"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginLeft="10dip"
            android:textColor="#ffffff"
            android:textSize="13sp" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/name_layout"
        android:layout_width="fill_parent"
        android:layout_height="60dip"
        android:orientation="vertical">

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:textColor="#303030"
            android:layout_marginLeft="15dp"
            android:text="李三"
            android:textSize="20sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_marginLeft="15dp"
            android:layout_marginRight="15dp"
            android:background="#4064a300" />
    </LinearLayout>

</LinearLayout>

在这个布局文件中,首先是放入了一个和前面完成一样的分组布局,因为不仅界面头部需要展示分组,在每个分组内的第一个无素之前都需要展示分组布局。然后是加入一个简单的LinearLayout,包含一个TextView用于显示联系人姓名。

这样我们的布局文件就全部写完了,下面开始来真正地实现功能。

先从简单的开始,新建一个PlayerInfo实体类:

package com.example.contactsdemo;

import java.io.Serializable;


/*
 * 用户信息
 * auth:liyachao
 * date:2015/4/6
 */
public class PlayerInfo {

    private String playerName;
    private String playerPhone;
    /**
     * 排序字母
     */
    private String sortKey;

    public String getSortKey() {
        return sortKey;
    }

    public void setSortKey(String sortKey) {
        this.sortKey = sortKey;
    }


    public String getPlayerPhone() {
        return playerPhone;
    }

    public void setPlayerPhone(String playerPhone) {
        this.playerPhone = playerPhone;
    }


    public String getPlayerName() {
        return playerName;
    }

    public void setPlayerName(String playerName) {
        this.playerName = playerName;
    }


}

这个实体类很简单,只包含了联系人姓名、排序键和联系人电话。

接下来完成联系人列表适配器的编写,新建一个ContactAdapter类继承自ArrayAdapter,加入如下代码:

package com.example.contactsdemo;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.SectionIndexer;
import android.widget.TextView;


import java.util.List;

/**
 * 联系人列表适配器。
 *
 * @author guolin
 */
public class ContactAdapter extends BaseAdapter {


    /**
     * 需要渲染的item布局文件
     */
    private int resource;
    private Context context;
    private List<PlayerInfo> players;
    private boolean flag = true;

    /**
     * 字母表分组工具
     */
    private SectionIndexer mIndexer;

    public ContactAdapter(Context context, int textViewResourceId, List<PlayerInfo> players) {

        resource = textViewResourceId;
        this.context = context;
        this.players = players;
    }


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

    @Override
    public PlayerInfo getItem(int position) {
        return players.get(position);
    }

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

    public void dataChanged(List<PlayerInfo> players) {
        this.players = players;
        notifyDataSetChanged();
        flag = false;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        PlayerInfo contact = getItem(position);
        LinearLayout layout = null;
        if (convertView == null) {
            layout = (LinearLayout) LayoutInflater.from(context).inflate(resource, null);
        } else {
            layout = (LinearLayout) convertView;
        }
        TextView name = (TextView) layout.findViewById(R.id.name);
        LinearLayout sortKeyLayout = (LinearLayout) layout.findViewById(R.id.sort_key_layout);
        TextView sortKey = (TextView) layout.findViewById(R.id.sort_key);
        name.setText(contact.getPlayerName());
        if (flag) {
            int section = mIndexer.getSectionForPosition(position);
            if (position == mIndexer.getPositionForSection(section)) {
                sortKey.setText(contact.getSortKey());
                sortKeyLayout.setVisibility(View.VISIBLE);
            } else {
                sortKeyLayout.setVisibility(View.GONE);
            }
        } else {
            sortKeyLayout.setVisibility(View.GONE);
        }
        return layout;
    }

    /**
     * 给当前适配器传入一个分组工具。
     *
     * @param indexer
     */
    public void setIndexer(SectionIndexer indexer) {
        mIndexer = indexer;
        flag = true;
    }

}

上面的代码中,最重要的就是getView方法,在这个方法中,我们首先判断是查找类型还是滑动页面类型,如果是查找类型,我们将头部的指示条就隐藏,如果不是再做判断,使用SectionIndexer的getSectionForPosition方法,通过当前的position值拿到了对应的section值,然后再反向通过刚刚拿到的section值,调用getPositionForSection方法,取回新的position值。如果当前的position值和新的position值是相等的,那么我们就可以认为当前position的项是某个分组下的第一个元素,我们应该将分组布局显示出来。

最后我们来编写程序的主界面,打开或新建MainActivity作为程序的主界面,代码如下所示:

package com.example.contactsdemo;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AlphabetIndexer;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

/**
 * chang date:2015/4/5 by liyachao
 */
public class MainActivity extends Activity {
    private ListView lv;
    private EditText edittext;
    /**
     * 联系人总人数
     */
    private int contactNumber;
    /**
     * 分组的布局
     */
    private LinearLayout titleLayout;
    /**
     * 分组上显示的字母
     */
    private TextView title;
    private ArrayList<PlayerInfo> allPlayerInfos;
    /**
     * 联系人列表适配器
     */
    private ContactAdapter adapter;

    /**
     * 用于进行字母表分组
     */
    private AlphabetIndexer indexer;

    private RelativeLayout sectionToastLayout;

    private TextView sectionToastText;

    private LinearLayout alphabetLayout;

    /**
     * A-Z的集合
     */
    private List<TextView> alphabetList;

    /**
     * 定义字母表的排序规则
     */
    private String alphabet = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";


    private int lastFirstVisibleItem = -1;

    /**
     * 电话号码*
     */
    private static final int PHONES_NUMBER_INDEX = 1;

    /**
     * 联系人显示名称*
     */
    private static final int PHONES_DISPLAY_NAME_INDEX = 0;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setControl();
    }


    private void setControl() {
        alphabetList = new ArrayList<TextView>();
        setAlphabetData();
        titleLayout = (LinearLayout) findViewById(R.id.title_layout1);
        title = (TextView) findViewById(R.id.title);
        sectionToastLayout = (RelativeLayout) findViewById(R.id.section_toast_layout);
        sectionToastText = (TextView) findViewById(R.id.section_toast_text);
        alphabetLayout = (LinearLayout) findViewById(R.id.alphabet_layout);

        lv = (ListView) findViewById(R.id.lv_select_contact);
        edittext = (EditText) findViewById(R.id.edittext);

        allPlayerInfos = getContactInfos();
        adapter = new ContactAdapter(this, R.layout.contact_item, allPlayerInfos);
        edittext.setHint("搜索" + contactNumber + "位联系人");
        adapter.setIndexer(indexer);
        if (allPlayerInfos.size() > 0) {
            setupContactsListView();
            setAlpabetListener();
        }

        /**
         * 向listview设置点击事件
         */
        lv.setOnItemClickListener(new MyOnItemClickListener());
        /**
         * 向edittext设置监听事件
         */
        edittext.addTextChangedListener(new MyTextWatcher());
    }

    /**
     * 根据填写的关键字在电话簿里寻找相关信息
     *
     * @param name
     * @return
     */
    private ArrayList<PlayerInfo> searchItem(String name) {
        ArrayList<PlayerInfo> mSearchList = new ArrayList<PlayerInfo>();
        for (int i = 0; i < allPlayerInfos.size(); i++) {
            int index = allPlayerInfos.get(i).getPlayerName().indexOf(name);
            // 存在匹配的数据
            if (index != -1) {
                mSearchList.add(allPlayerInfos.get(i));
            }
        }
        return mSearchList;
    }


    /**
     * 获取系统的所有的联系人信息.
     *
     * @return
     */
    public ArrayList<PlayerInfo> getContactInfos() {
        allPlayerInfos = new ArrayList<PlayerInfo>();
        ContentResolver resolver = getContentResolver();
        Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
        Cursor phoneCursor = resolver.query(uri,
                new String[]{Phone.DISPLAY_NAME, Phone.NUMBER, Phone.SORT_KEY_PRIMARY}
                , null, null, Phone.SORT_KEY_PRIMARY);
        if (phoneCursor.moveToFirst()) {
            do {
                //得到手机号码
                String phoneNumber = phoneCursor.getString(PHONES_NUMBER_INDEX);
                String sortKey = getSortKey(phoneCursor.getString(2));
                //得到联系人名称
                String contactName = phoneCursor.getString(PHONES_DISPLAY_NAME_INDEX);

                PlayerInfo playerInfo = new PlayerInfo();
                playerInfo.setPlayerName(contactName);
                playerInfo.setPlayerPhone(phoneNumber);
                playerInfo.setSortKey(sortKey);
                allPlayerInfos.add(playerInfo);

            } while (phoneCursor.moveToNext());
        }
        contactNumber = allPlayerInfos.size();
        startManagingCursor(phoneCursor);
        indexer = new AlphabetIndexer(phoneCursor, 2, alphabet);

        return allPlayerInfos;
    }

    /**
     * 获取sort key的首个字符,如果是英文字母就直接返回,否则返回#。
     *
     * @param sortKeyString 数据库中读取出的sort key
     * @return 英文字母或者#
     */
    private String getSortKey(String sortKeyString) {
        alphabetLayout.getHeight();
        String key = sortKeyString.substring(0, 1).toUpperCase();
        if (key.matches("[A-Z]")) {
            return key;
        }
        return "#";
    }


    /**
     * 为联系人ListView设置监听事件,
     * 根据当前的滑动状态来改变分组的显示位置,
     * 从而实现挤压动画的效果。
     * auth:liyachao
     */
    private void setupContactsListView() {

        lv.setAdapter(adapter);
        lv.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
                                 int totalItemCount) {
                int section = indexer.getSectionForPosition(firstVisibleItem);
                int nextSecPosition = indexer.getPositionForSection(section + 1);
                setSortAlphabet(section);


                if (firstVisibleItem != lastFirstVisibleItem) {
                    ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) titleLayout.getLayoutParams();
                    params.topMargin = 0;
                    titleLayout.setLayoutParams(params);
                    title.setText(String.valueOf(alphabet.charAt(section)));
                }
                if (nextSecPosition == firstVisibleItem + 1) {
                    View childView = view.getChildAt(0);
                    if (childView != null) {
                        int titleHeight = titleLayout.getHeight();
                        int bottom = childView.getBottom();
                        ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) titleLayout
                                .getLayoutParams();
                        if (bottom < titleHeight) {
                            float pushedDistance = bottom - titleHeight;
                            params.topMargin = (int) pushedDistance;
                            titleLayout.setLayoutParams(params);
                        } else {
                            if (params.topMargin != 0) {
                                params.topMargin = 0;
                                titleLayout.setLayoutParams(params);
                            }
                        }
                    }
                }
                lastFirstVisibleItem = firstVisibleItem;
            }
        });

    }

    /**
     * 设置默认的字幕背景和字体颜色
     */
    private void setSortAlphabet(int section) {
        TextView tv;
        for (int i = 0; i < 27; i++) {
            if (section == i) {
                tv = alphabetList.get(section);
                tv.setTextColor(Color.parseColor("#ffffff"));
                tv.setBackgroundResource(R.drawable.text_bg_frame);
            } else {
                tv = alphabetList.get(i);
                tv.setTextColor(Color.parseColor("#303030"));
                tv.setBackgroundColor(Color.parseColor("#ffffff"));
            }
            tv.setGravity(Gravity.CENTER);
        }
    }

    /**
     * 设置字母表上的触摸事件,根据当前触摸的位置结合字母表的高度,
     * 计算出当前触摸在哪个字母上。
     * 当手指按在字母表上时,展示弹出式分组。手指离开字母表时,
     * 将弹出式分组隐藏。
     * auth:liyachao
     */
    private void setAlpabetListener() {
        alphabetLayout.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                float alphabetHeight = alphabetLayout.getHeight();
                float y = event.getY();

                int sectionPosition = (int) ((y / alphabetHeight) / (1f / 27f));
                if (sectionPosition < 0) {
                    sectionPosition = 0;
                } else if (sectionPosition > 26) {
                    sectionPosition = 26;
                }

                String sectionLetter = String.valueOf(alphabet.charAt(sectionPosition));
                int position = indexer.getPositionForSection(sectionPosition);
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        alphabetList.get(sectionPosition).setTextColor(Color.parseColor("#ffffff"));
                        alphabetList.get(sectionPosition).setBackgroundResource(R.drawable.text_bg_frame);
                        sectionToastLayout.setVisibility(View.VISIBLE);
                        sectionToastText.setText(sectionLetter);
                        lv.setSelection(position);
                        break;
                    case MotionEvent.ACTION_MOVE:
                        setSortAlphabet(sectionPosition);
                        sectionToastText.setText(sectionLetter);
                        lv.setSelection(position);
                        break;
                    default:
                        setSortAlphabet(sectionPosition);
                        sectionToastLayout.setVisibility(View.GONE);
                }
                return true;
            }
        });
    }

    class MyOnItemClickListener implements OnItemClickListener {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            String name = ((PlayerInfo) parent.getItemAtPosition(position)).getPlayerName() + "";
            String phone = ((PlayerInfo) parent.getItemAtPosition(position)).getPlayerPhone() + "";
            String str = name + "的电话为: " + phone;
            Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();
        }
    }

    class MyTextWatcher implements TextWatcher {

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            String str = s.toString();
            if (str.equals("")) {
                adapter.dataChanged(allPlayerInfos);
                adapter.setIndexer(indexer);
                titleLayout.setVisibility(View.VISIBLE);
            } else {
                Log.i("tag", "name111: " + str);
                ArrayList<PlayerInfo> temp;
                temp = searchItem(str.trim());
                adapter.dataChanged(temp);
                titleLayout.setVisibility(View.GONE);
            }
            lv.setAdapter(adapter);
        }
    }

    private void setAlphabetData() {
        TextView jing = (TextView) findViewById(R.id.contact_);
        alphabetList.add(jing);
        TextView A = (TextView) findViewById(R.id.contact_A);
        alphabetList.add(A);
        TextView B = (TextView) findViewById(R.id.contact_B);
        alphabetList.add(B);
        TextView C = (TextView) findViewById(R.id.contact_C);
        alphabetList.add(C);
        TextView D = (TextView) findViewById(R.id.contact_D);
        alphabetList.add(D);
        TextView E = (TextView) findViewById(R.id.contact_E);
        alphabetList.add(E);
        TextView F = (TextView) findViewById(R.id.contact_F);
        alphabetList.add(F);
        TextView G = (TextView) findViewById(R.id.contact_G);
        alphabetList.add(G);
        TextView H = (TextView) findViewById(R.id.contact_H);
        alphabetList.add(H);
        TextView I = (TextView) findViewById(R.id.contact_I);
        alphabetList.add(I);
        TextView J = (TextView) findViewById(R.id.contact_J);
        alphabetList.add(J);
        TextView K = (TextView) findViewById(R.id.contact_K);
        alphabetList.add(K);
        TextView L = (TextView) findViewById(R.id.contact_L);
        alphabetList.add(L);
        TextView M = (TextView) findViewById(R.id.contact_M);
        alphabetList.add(M);
        TextView N = (TextView) findViewById(R.id.contact_N);
        alphabetList.add(N);
        TextView O = (TextView) findViewById(R.id.contact_O);
        alphabetList.add(O);
        TextView P = (TextView) findViewById(R.id.contact_P);
        alphabetList.add(P);
        TextView Q = (TextView) findViewById(R.id.contact_Q);
        alphabetList.add(Q);
        TextView R1 = (TextView) findViewById(R.id.contact_R);
        alphabetList.add(R1);
        TextView S = (TextView) findViewById(R.id.contact_S);
        alphabetList.add(S);
        TextView T = (TextView) findViewById(R.id.contact_T);
        alphabetList.add(T);
        TextView U = (TextView) findViewById(R.id.contact_U);
        alphabetList.add(U);
        TextView V = (TextView) findViewById(R.id.contact_V);
        alphabetList.add(V);
        TextView W = (TextView) findViewById(R.id.contact_W);
        alphabetList.add(W);
        TextView X = (TextView) findViewById(R.id.contact_X);
        alphabetList.add(X);
        TextView Y = (TextView) findViewById(R.id.contact_Y);
        alphabetList.add(Y);
        TextView Z = (TextView) findViewById(R.id.contact_Z);
        alphabetList.add(Z);
    }

}
最后要记住,要在AndroidManifest.xml给出读取手机联系人的权限声明:

<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>  






郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。