仿Android联系人SideBar排序,依据拼音A-Z字母快速查找,以及输入搜索条件过滤,显示姓名的文字图片
关注finddreams,一起分享,一起进步!http://blog.****.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.****.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.****.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.****.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