仿Android联系人SideBar排序,根据拼音A-Z字母快速导航联系人姓名,以及输入搜索条件过滤,显示姓名的文字图片
关注finddreams,一起分享,一起进步!http://blog.csdn.net/finddreams/article/details/44623235
话不多说,直接上干货,图片如下:
仿Android联系人SideBar排序,通过获取手机中的联系人,并根据拼音A-Z字母快速导航联系人的位置,以及输入搜索条件过滤,显示姓名的文字图片。
这样的效果相信大家并不陌生,我们在APP中都司空见惯了,比如在选择地址的时候,选择国家,省份等等。这样的效果很多大神也都写过相应的博客,大体上实现的方式都差不多,今天我也给大家模仿一下Android联系人的排序实现。
1.首先我们把这几个工具类拷贝到自己的项目中,这些都是很常见的类:
CharacterParser –这是用来把中文转成拼音的工具类
PinyinComparator –拼音首字母的比较器
SideBar –右侧的竖条,显示的是二十六个字母以及*,和#号
SortModel –放排序name和key的bean
2.加上一个ClearEditText来实现带删除当前输入内容按钮EditText
在ClearEditText中输入内容,然后监听它的TextChangedListener来实现搜索条件的过滤,具体代码见最下面的源码:
3.接下来我们来看看实现这个效果改如何布局,看一下我们的layout布局文件:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:focusableInTouchMode="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.finddreams.sortedcontact.ClearEditText android:id="@+id/filter_edit" android:layout_width="fill_parent" android:layout_height="40dp" android:layout_marginLeft="5dp" android:layout_marginRight="5dp" android:layout_marginTop="5dip" android:gravity="center" android:background="@drawable/acm_inputbox" android:drawableLeft="@drawable/search" android:hint="@string/search" android:singleLine="true" android:textSize="15.0dip" /> <FrameLayout android:layout_width="fill_parent" android:layout_height="wrap_content" > <ListView android:id="@+id/sortlist" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@color/whites" android:listSelector="@drawable/comm_btn_selector" android:layout_gravity="center" /> <TextView android:id="@+id/dialog" android:layout_width="80.0dip" android:layout_height="80.0dip" android:layout_gravity="center" android:background="@drawable/number_base" android:gravity="center" android:textColor="#ffffffff" android:textSize="30.0dip" android:visibility="invisible" /> <com.finddreams.sortedcontact.sortlist.SideBar android:id="@+id/sidrbar" android:layout_width="30.0dip" android:layout_height="fill_parent" android:layout_gravity="right|center" /> </FrameLayout> </LinearLayout></RelativeLayout>
4.然后我们就可以在Activity代码中调用了,具体的代码如下:
/** * @Description:联系人显示界面 * @author http://blog.csdn.net/finddreams */public class MainActivity extends Activity { private View mBaseView; private ListView sortListView; private SideBar sideBar; private TextView dialog; private SortAdapter adapter; private ClearEditText mClearEditText; private Map<String, String> callRecords; private CharacterParser characterParser; private List<SortModel> SourceDateList; private PinyinComparator pinyinComparator; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.activity_main_contact); initView(); initData(); } private void initView() { sideBar = (SideBar) this.findViewById(R.id.sidrbar); dialog = (TextView) this.findViewById(R.id.dialog); sortListView = (ListView) this.findViewById(R.id.sortlist); } private void initData() { // 实例化汉字转拼音类 characterParser = CharacterParser.getInstance(); pinyinComparator = new PinyinComparator(); sideBar.setTextView(dialog); // 设置右侧触摸监听 sideBar.setOnTouchingLetterChangedListener(new OnTouchingLetterChangedListener() { @SuppressLint("NewApi") @Override public void onTouchingLetterChanged(String s) { // 该字母首次出现的位置 int position = adapter.getPositionForSection(s.charAt(0)); if (position != -1) { sortListView.setSelection(position); } } }); sortListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // 这里要利用adapter.getItem(position)来获取当前position所对应的对象 // Toast.makeText(getApplication(), // ((SortModel)adapter.getItem(position)).getName(), // Toast.LENGTH_SHORT).show(); String number = callRecords.get(((SortModel) adapter .getItem(position)).getName()); Toast.makeText(MainActivity.this, number, 0).show(); } }); new ConstactAsyncTask().execute(0); } private class ConstactAsyncTask extends AsyncTask<Integer, Integer, Integer> { @Override protected Integer doInBackground(Integer... arg0) { int result = -1; callRecords = ConstactUtil.getAllCallRecords(MainActivity.this); result = 1; return result; } @Override protected void onPostExecute(Integer result) { super.onPostExecute(result); if (result == 1) { List<String> constact = new ArrayList<String>(); for (Iterator<String> keys = callRecords.keySet().iterator(); keys .hasNext();) { String key = keys.next(); constact.add(key); } String[] names = new String[] {}; names = constact.toArray(names); SourceDateList = filledData(names); // 根据a-z进行排序源数据 Collections.sort(SourceDateList, pinyinComparator); adapter = new SortAdapter(MainActivity.this, SourceDateList); sortListView.setAdapter(adapter); mClearEditText = (ClearEditText) MainActivity.this .findViewById(R.id.filter_edit); mClearEditText .setOnFocusChangeListener(new OnFocusChangeListener() { @Override public void onFocusChange(View arg0, boolean arg1) { mClearEditText.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); } }); // 根据输入框输入值的改变来过滤搜索 mClearEditText.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // 当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表 filterData(s.toString()); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); } } @Override protected void onPreExecute() { super.onPreExecute(); } } /** * 为ListView填充数据 * * @param date * @return */ private List<SortModel> filledData(String[] date) { List<SortModel> mSortList = new ArrayList<SortModel>(); for (int i = 0; i < date.length; i++) { SortModel sortModel = new SortModel(); sortModel.setName(date[i]); // 汉字转换成拼音 String pinyin = characterParser.getSelling(date[i]); String sortString = pinyin.substring(0, 1).toUpperCase(); // 正则表达式,判断首字母是否是英文字母 if (sortString.matches("[A-Z]")) { sortModel.setSortLetters(sortString.toUpperCase()); } else { sortModel.setSortLetters("#"); } mSortList.add(sortModel); } return mSortList; } /** * 根据输入框中的值来过滤数据并更新ListView * * @param filterStr */ private void filterData(String filterStr) { List<SortModel> filterDateList = new ArrayList<SortModel>(); if (TextUtils.isEmpty(filterStr)) { filterDateList = SourceDateList; } else { filterDateList.clear(); for (SortModel sortModel : SourceDateList) { String name = sortModel.getName(); if (name.indexOf(filterStr.toString()) != -1 || characterParser.getSelling(name).startsWith( filterStr.toString())) { filterDateList.add(sortModel); } } } // 根据a-z进行排序 Collections.sort(filterDateList, pinyinComparator); adapter.updateListView(filterDateList); }}
5.为了获取到联系人的数据,我们首先当然是不要忘了添加获取联系人的权限了,其次这里有一个获取联系人的工具类,十分的方便:
public class ConstactUtil { /** * 获取所有数据 * * @return */ public static Map<String, String> getAllCallRecords(Context context) { Map<String, String> temp = new HashMap<String, String>(); Cursor c = context.getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); if (c.moveToFirst()) { do { // 获得联系人的ID号 String contactId = c.getString(c .getColumnIndex(ContactsContract.Contacts._ID)); // 获得联系人姓名 String name = c .getString(c .getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); // 查看该联系人有多少个电话号码。如果没有这返回值为0 int phoneCount = c .getInt(c .getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)); String number = null; if (phoneCount > 0) { // 获得联系人的电话号码 Cursor phones = context.getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null); if (phones.moveToFirst()) { number = phones .getString(phones .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); } phones.close(); } temp.put(name, number); } while (c.moveToNext()); } c.close(); return temp; }}
6.我们来看看SortAdapter 是如何写的
/** * @Description:用来处理集合中数据的显示与排序 * @author http://blog.csdn.net/finddreams */public class SortAdapter extends BaseAdapter implements SectionIndexer { private List<SortModel> list = null; private Context mContext; public SortAdapter(Context mContext, List<SortModel> list) { this.mContext = mContext; this.list = list; } /** * 当ListView数据发生变化时,调用此方法来更新ListView * * @param list */ public void updateListView(List<SortModel> list) { this.list = list; notifyDataSetChanged(); } public int getCount() { return this.list.size(); } public Object getItem(int position) { return list.get(position); } public long getItemId(int position) { return position; } public View getView(final int position, View view, ViewGroup arg2) { ViewHolder viewHolder = null; final SortModel mContent = list.get(position); if (view == null) { viewHolder = new ViewHolder(); view = LayoutInflater.from(mContext).inflate( R.layout.phone_constacts_item, null); viewHolder.tvTitle = (TextView) view.findViewById(R.id.title); viewHolder.tvLetter = (TextView) view.findViewById(R.id.catalog); viewHolder.icon = (ImageTextView) view.findViewById(R.id.icon); view.setTag(viewHolder); } else { viewHolder = (ViewHolder) view.getTag(); } // 根据position获取分类的首字母的Char ascii值 int section = getSectionForPosition(position); // 如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现 if (position == getPositionForSection(section)) { viewHolder.tvLetter.setVisibility(View.VISIBLE); viewHolder.tvLetter.setText(mContent.getSortLetters()); } else { viewHolder.tvLetter.setVisibility(View.GONE); } viewHolder.tvTitle.setText(this.list.get(position).getName()); viewHolder.icon.setText(this.list.get(position).getName()); viewHolder.icon .setIconText(mContext, this.list.get(position).getName()); return view; } final static class ViewHolder { TextView tvLetter; TextView tvTitle; ImageTextView icon; } /** * 根据ListView的当前位置获取分类的首字母的Char ascii值 */ public int getSectionForPosition(int position) { return list.get(position).getSortLetters().charAt(0); } /** * 根据分类的首字母的Char ascii值获取其第一次出现该首字母的位置 */ public int getPositionForSection(int section) { for (int i = 0; i < getCount(); i++) { String sortStr = list.get(i).getSortLetters(); char firstChar = sortStr.toUpperCase().charAt(0); if (firstChar == section) { return i; } } return -1; } /** * 提取英文的首字母,非英文字母用#代替。 * * @param str * @return */ private String getAlpha(String str) { String sortStr = str.trim().substring(0, 1).toUpperCase(); // 正则表达式,判断首字母是否是英文字母 if (sortStr.matches("[A-Z]")) { return sortStr; } else { return "#"; } } @Override public Object[] getSections() { return null; }}
7.为了实现每个名字前面带有这个名字中第一个字的图片,我们首先要引入一个自定义的ImageTextView类,它是继承自TextView的,我们所做的只是在拿到这个TextView的Text内容,然后根据这个内容画出一张图片,具体代码如下:
/**
* @Description: 文字图片,这个相信大家都知道,比如QQ底部导航上的未读消息数
* @author http://blog.csdn.net/finddreams
*/
public class ImageTextView extends TextView {
private Bitmap bitmap;
private String text;
Drawable d;
public ImageTextView(Context context) { super(context);}public ImageTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);}public ImageTextView(Context context, AttributeSet attrs) { super(context, attrs);}public void setIconText(Context context, String text) { text = this.getText().toString().substring(0, 1); bitmap = BitmapUtil.getIndustry(context, text); d = BitmapUtil.bitmapTodrawable(bitmap); this.setCompoundDrawables(d, null, null, null);}
}
其中BitmapUtil有两个方法:
/** * 根据文字获取图片 * @param text * @return */ public static Bitmap getIndustry(Context context, String text) { String color = "#ffeeeade"; Bitmap src = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher); int x = src.getWidth(); int y = src.getHeight(); Bitmap bmp = Bitmap.createBitmap(x, y, Bitmap.Config.ARGB_8888); Canvas canvasTemp = new Canvas(bmp); canvasTemp.drawColor(Color.parseColor(color)); Paint p = new Paint(Paint.FAKE_BOLD_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG); p.setColor(Color.parseColor("#ff4e0a13")); p.setAlpha(45); p.setFilterBitmap(true); int size = (int) (18 * context.getResources().getDisplayMetrics().density); p.setTextSize(size); float tX = (x - getFontlength(p, text)) / 2; float tY = (y - getFontHeight(p)) / 2 + getFontLeading(p); canvasTemp.drawText(text, tX, tY, p); return toRoundCorner(bmp, 2); }/** * @param bitmap * @return */ public static Drawable bitmapTodrawable(Bitmap bitmap) { Drawable drawable = new BitmapDrawable(bitmap); drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); return drawable; }
这样就可以实现文字图片的效果了,是不是非常的简单。
最后需要的朋友可以去这里下载源码,学习学习:
https://github.com/finddreams/SortedContactUI
更多相关文章
- 一句话锁定MySQL数据占用元凶
- 金三银四背后,一个 Android(安卓)程序员的面试心得
- MVVM框架的使用介绍和为什么选择用MVVM框架
- 如何将旧手机数据转移至iPhone7?旧Android数据转移到新iPhone全攻
- Android入门笔记 - 数据存储 - 网络
- Android(安卓)本地文件缓存各个方法获取的路径小结
- Android(安卓)Paging分页库的学习(一)—— 结合本地数据进行分页加
- Android近期任务列表Recent List(Recents Screen)的实现方式
- 在Android系统使用socket在Java层和native之间数据通信