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"><LinearLayoutandroid:id="@+id/sort_key_layout"android:layout_width="fill_parent"android:layout_height="18dip"android:layout_marginRight="15dp"android:background="#64a300"><TextViewandroid: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><LinearLayoutandroid:id="@+id/name_layout"android:layout_width="fill_parent"android:layout_height="60dip"android:orientation="vertical"><TextViewandroid: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" /><TextViewandroid: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;}@Overridepublic int getCount() {return players.size();}@Overridepublic PlayerInfo getItem(int position) {return players.get(position);}@Overridepublic long getItemId(int position) {return position;}public void dataChanged(List<PlayerInfo> players) {this.players = players;notifyDataSetChanged();flag = false;}@Overridepublic 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;@Overrideprotected 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() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}@Overridepublic 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() {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {}@Overridepublic 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>
總結
以上是生活随笔為你收集整理的Android手机通讯录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 常用实现跨域的方法
- 下一篇: android sina oauth2.