Android 聯絡人列表介面(仿iphone、A~Z字母排列、過濾搜尋)
阿新 • • 發佈:2019-01-11
前些天需要做一個聯絡人的列表介面,無奈網上的的demo都不太好看,而且有些bug也不少,只好自己借鑑著弄好了一個出來,順便美化一下,感覺還是挺好看。
先看下效果是不是你想要的:
ContactsActivity.java
SideBar.java 這個類就是側邊的A~Z的擺列,需要自定義的就修改這裡public class ContactsActivity extends Activity { ListView mListView; EditText etSearch; ImageView ivClearText; private SideBar sideBar; private TextView dialog; private List<SortModel> mAllContactsList; private ContactsSortAdapter adapter; Context mContext; /** * 漢字轉換成拼音的類 */ private CharacterParser characterParser; /** * 根據拼音來排列ListView裡面的資料類 */ private PinyinComparator pinyinComparator; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_contacts); mContext = getApplicationContext(); init(); } private void init() { initView(); initListener(); loadContacts(); } private void initListener() { /** 清除輸入字元 **/ ivClearText.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { etSearch.setText(""); } }); etSearch.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { } @Override public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { } @Override public void afterTextChanged(Editable e) { String content = etSearch.getText().toString(); if ("".equals(content)) { ivClearText.setVisibility(View.INVISIBLE); } else { ivClearText.setVisibility(View.VISIBLE); } if (content.length() > 0) { ArrayList<SortModel> fileterList = (ArrayList<SortModel>) search(content); adapter.updateListView(fileterList); // mAdapter.updateData(mContacts); } else { adapter.updateListView(mAllContactsList); } mListView.setSelection(0); } }); // 設定右側[A-Z]快速導航欄觸控監聽 sideBar.setOnTouchingLetterChangedListener(new OnTouchingLetterChangedListener() { @Override public void onTouchingLetterChanged(String s) { // 該字母首次出現的位置 int position = adapter.getPositionForSection(s.charAt(0)); if (position != -1) { mListView.setSelection(position); } } }); // item事件 mListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long arg3) { ViewHolder viewHolder = (ViewHolder) view.getTag(); viewHolder.cbChecked.performClick(); adapter.toggleChecked(position); } }); } private void initView() { sideBar = (SideBar) findViewById(R.id.sidrbar); dialog = (TextView) findViewById(R.id.dialog); sideBar.setTextView(dialog); ivClearText = (ImageView) findViewById(R.id.ivClearText); etSearch = (EditText) findViewById(R.id.et_search); mListView = (ListView) findViewById(R.id.lv_contacts); /** 給ListView設定adapter **/ characterParser = CharacterParser.getInstance(); mAllContactsList = new ArrayList<SortModel>(); pinyinComparator = new PinyinComparator(); Collections.sort(mAllContactsList, pinyinComparator);// 根據a-z進行排序源資料 adapter = new ContactsSortAdapter(this, mAllContactsList); mListView.setAdapter(adapter); } /** * 載入聯絡人資料 */ private void loadContacts() { new Thread(new Runnable() { @Override public void run() { try { // 插敘 String queryTye[] = { Phone.DISPLAY_NAME, Phone.NUMBER, "sort_key", "phonebook_label", Phone.PHOTO_ID }; ContentResolver resolver = getApplicationContext().getContentResolver(); Cursor phoneCursor = resolver.query(Phone.CONTENT_URI, queryTye, null, null, "sort_key COLLATE LOCALIZED ASC"); if (phoneCursor == null || phoneCursor.getCount() == 0) { Toast.makeText(getApplicationContext(), "未獲得讀取聯絡人許可權 或 未獲得聯絡人資料", Toast.LENGTH_SHORT).show(); return; } int PHONES_NUMBER_INDEX = phoneCursor.getColumnIndex(Phone.NUMBER); int PHONES_DISPLAY_NAME_INDEX = phoneCursor.getColumnIndex(Phone.DISPLAY_NAME); int SORT_KEY_INDEX = phoneCursor.getColumnIndex("sort_key"); int PHONEBOOK_LABEL = phoneCursor.getColumnIndex("phonebook_label"); int PHOTO_ID = phoneCursor.getColumnIndex(Phone.PHOTO_ID); if (phoneCursor.getCount() > 0) { mAllContactsList = new ArrayList<SortModel>(); while (phoneCursor.moveToNext()) { String phoneNumber = phoneCursor.getString(PHONES_NUMBER_INDEX); if (TextUtils.isEmpty(phoneNumber)) continue; // 頭像id long photoId = phoneCursor.getLong(PHOTO_ID); String contactName = phoneCursor.getString(PHONES_DISPLAY_NAME_INDEX); String sortKey = phoneCursor.getString(SORT_KEY_INDEX); String book = phoneCursor.getString(PHONEBOOK_LABEL); SortModel sortModel = new SortModel(contactName, phoneNumber, sortKey); // //優先使用系統sortkey取,取不到再使用工具取 // String sortLetters = // getSortLetterBySortKey(sortKey); // Log.i("main", "sortLetters:"+sortLetters); // if (sortLetters == null) { // sortLetters = getSortLetter(contactName); // } if (book == null) { book = "#"; } else if (book.equals("#")) { book = "#"; } else if (book.equals("")) { book = "#"; } sortModel.sortLetters = book; sortModel.sortToken = parseSortKey(book); mAllContactsList.add(sortModel); } } phoneCursor.close(); runOnUiThread(new Runnable() { public void run() { Collections.sort(mAllContactsList, pinyinComparator); adapter.updateListView(mAllContactsList); } }); } catch (Exception e) { Log.e("xbc", e.getLocalizedMessage()); } } }).start(); } /** * 名字轉拼音,取首字母 * * @param name * @return */ private String getSortLetter(String name) { String letter = "#"; if (name == null) { return letter; } // 漢字轉換成拼音 // String pinyin = characterParser.getSelling(name); String pinyin = PinYin.getPinYin(name); Log.i("main", "pinyin:" + pinyin); String sortString = pinyin.substring(0, 1).toUpperCase(Locale.CHINESE); // 正則表示式,判斷首字母是否是英文字母 if (sortString.matches("[A-Z]")) { letter = sortString.toUpperCase(Locale.CHINESE); } return letter; } /** * 取sort_key的首字母 * * @param sortKey * @return */ private String getSortLetterBySortKey(String sortKey) { if (sortKey == null || "".equals(sortKey.trim())) { return null; } String letter = "#"; // 漢字轉換成拼音 String sortString = sortKey.trim().substring(0, 1).toUpperCase(Locale.CHINESE); // 正則表示式,判斷首字母是否是英文字母 if (sortString.matches("[A-Z]")) { letter = sortString.toUpperCase(Locale.CHINESE); } return letter; } /** * 模糊查詢 * * @param str * @return */ private List<SortModel> search(String str) { List<SortModel> filterList = new ArrayList<SortModel>();// 過濾後的list // if (str.matches("^([0-9]|[/+])*$")) {// 正則表示式 匹配號碼 if (str.matches("^([0-9]|[/+]).*")) {// 正則表示式 // 匹配以數字或者加號開頭的字串(包括了帶空格及-分割的號碼) String simpleStr = str.replaceAll("\\-|\\s", ""); for (SortModel contact : mAllContactsList) { if (contact.number != null && contact.name != null) { if (contact.simpleNumber.contains(simpleStr) || contact.name.contains(str)) { if (!filterList.contains(contact)) { filterList.add(contact); } } } } } else { for (SortModel contact : mAllContactsList) { if (contact.number != null && contact.name != null) { // 姓名全匹配,姓名首字母簡拼匹配,姓名全字母匹配 if (contact.name.toLowerCase(Locale.CHINESE).contains(str.toLowerCase(Locale.CHINESE)) || contact.sortKey.toLowerCase(Locale.CHINESE).replace(" ", "") .contains(str.toLowerCase(Locale.CHINESE)) || contact.sortToken.simpleSpell.toLowerCase(Locale.CHINESE) .contains(str.toLowerCase(Locale.CHINESE)) || contact.sortToken.wholeSpell.toLowerCase(Locale.CHINESE) .contains(str.toLowerCase(Locale.CHINESE))) { if (!filterList.contains(contact)) { filterList.add(contact); } } } } } return filterList; } String chReg = "[\\u4E00-\\u9FA5]+";// 中文字串匹配 // String chReg="[^\\u4E00-\\u9FA5]";//除中文外的字元匹配 /** * 解析sort_key,封裝簡拼,全拼 * * @param sortKey * @return */ public SortToken parseSortKey(String sortKey) { SortToken token = new SortToken(); if (sortKey != null && sortKey.length() > 0) { // 其中包含的中文字元 String[] enStrs = sortKey.replace(" ", "").split(chReg); for (int i = 0, length = enStrs.length; i < length; i++) { if (enStrs[i].length() > 0) { // 拼接簡拼 token.simpleSpell += enStrs[i].charAt(0); token.wholeSpell += enStrs[i]; } } } return token; } }
public class SideBar extends View { // 觸控事件 private OnTouchingLetterChangedListener onTouchingLetterChangedListener; // 26個字母 public static String[] b = { "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 int choose = -1;// 選中 private Paint paint = new Paint(); private TextView mTextDialog; private float singleHeight; public static int dialogColor[] = { R.drawable.dialog_color_blue, R.drawable.dialog_color_green, R.drawable.dialog_color_orange, R.drawable.dialog_color_purple, R.drawable.dialog_color_red };; public void setTextView(TextView mTextDialog) { this.mTextDialog = mTextDialog; } public SideBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public SideBar(Context context, AttributeSet attrs) { super(context, attrs); } public SideBar(Context context) { super(context); } /** * 重寫這個方法 */ protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 獲取焦點改變背景顏色. int height = getHeight();// 獲取對應高度 int width = getWidth(); // 獲取對應寬度 // 獲取每一個字母的高度 singleHeight = (height * 1f) / b.length; singleHeight = (height * 1f - singleHeight / 2) / b.length; for (int i = 0; i < b.length; i++) { paint.setColor(Color.rgb(23, 122, 216)); // paint.setColor(Color.WHITE); paint.setTypeface(Typeface.DEFAULT_BOLD); paint.setAntiAlias(true); paint.setTextSize(25); // 選中的狀態 if (i == choose) { paint.setColor(Color.parseColor("#c60000")); paint.setFakeBoldText(true); } // x座標等於中間-字串寬度的一半. float xPos = width / 2 - paint.measureText(b[i]) / 2; float yPos = singleHeight * i + singleHeight; canvas.drawText(b[i], xPos, yPos, paint); paint.reset();// 重置畫筆 } } @Override public boolean dispatchTouchEvent(MotionEvent event) { final int action = event.getAction(); final float y = event.getY();// 點選y座標 final int oldChoose = choose; final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener; final int c = (int) (y / getHeight() * b.length);// 點選y座標所佔總高度的比例*b陣列的長度就等於點選b中的個數. switch (action) { case MotionEvent.ACTION_UP: setBackgroundDrawable(new ColorDrawable(0x00000000)); choose = -1;// invalidate(); if (mTextDialog != null) { mTextDialog.setVisibility(View.INVISIBLE); } break; // 除開鬆開事件的任何觸控事件 default: setBackgroundResource(R.drawable.sidebar_background); if (oldChoose != c) { if (c >= 0 && c < b.length) { if (listener != null) { listener.onTouchingLetterChanged(b[c]); } if (mTextDialog != null) { mTextDialog.setText(b[c]); mTextDialog.setVisibility(View.VISIBLE); // 動態改變文字dialog的位置 int right = mTextDialog.getLeft(); mTextDialog.setX(right / 2 * 3); if(c>24){ mTextDialog.setY(singleHeight * 24); }else{ mTextDialog.setY(singleHeight * c); } mTextDialog.setBackground(getContext().getResources().getDrawable(dialogColor[c / 6])); } choose = c; invalidate(); } } break; } return true; } /** * 向外公開的方法 * * @param onTouchingLetterChangedListener */ public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener onTouchingLetterChangedListener) { this.onTouchingLetterChangedListener = onTouchingLetterChangedListener; } /** * 介面 * * @author coder * */ public interface OnTouchingLetterChangedListener { public void onTouchingLetterChanged(String s); } }