1. 程式人生 > >Android 聯絡人列表介面(仿iphone、A~Z字母排列、過濾搜尋)

Android 聯絡人列表介面(仿iphone、A~Z字母排列、過濾搜尋)

前些天需要做一個聯絡人的列表介面,無奈網上的的demo都不太好看,而且有些bug也不少,只好自己借鑑著弄好了一個出來,順便美化一下,感覺還是挺好看。

先看下效果是不是你想要的:

                       

ContactsActivity.java

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;
	}

}
SideBar.java 這個類就是側邊的A~Z的擺列,需要自定義的就修改這裡
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);
	}

}