1、Activity:
/**
* Created by admin on 2015/9/22.
*/
public class PhoneBookActivity extends Activity {
private static final String TAG = "PhoneBookActivity";
private IndexableListView phoneListView;
// 电话本数据List
ArrayList<PhoneInfo> phoneInfosList;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.test_phone_main);
phoneListView = (IndexableListView) findViewById(R.id.phoneListView);
phoneInfosList = new ArrayList<>();
// 初始化数据
getPhoneContacts();
// 按字典序进行排序
Collections.sort(phoneInfosList, new Comparator<PhoneInfo>() {
@Override
public int compare(PhoneInfo lhs, PhoneInfo rhs) {
return lhs.getFirstLetter() - rhs.getFirstLetter();
}
});
// 判断ListView中的每个字母索引的头部
PhoneInfo.tagIsHead(phoneInfosList);
PhoneAdapter adapter = new PhoneAdapter(this);
phoneListView.setAdapter(adapter);
phoneListView.setFastScrollEnabled(true); // 设置快速滑动,这一步不可少
}
private class PhoneAdapter extends BaseAdapter implements SectionIndexer {
// 定义索引列表
private String mSections = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";
LayoutInflater layoutInflater;
public PhoneAdapter (Context context) {
layoutInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return phoneInfosList != null ? phoneInfosList.size() : 0;
}
@Override
public PhoneInfo getItem(int position) {
return phoneInfosList != null ? phoneInfosList.get(position) : null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.phone_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.img_phonePhoto = (ImageView) convertView.findViewById(R.id.img_phonePhoto);
viewHolder.txt_contactName= (TextView) convertView.findViewById(R.id.txt_contactName);
viewHolder.txt_phoneNumber= (TextView) convertView.findViewById(R.id.txt_phoneNumber);
viewHolder.txtHeadLetter = (TextView) convertView.findViewById(R.id.txtHeadLetter);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
try {
PhoneInfo info = getItem(position);
viewHolder.img_phonePhoto.setImageBitmap(info.getContactPhoto());
viewHolder.txt_contactName.setText(info.getContactName());
viewHolder.txt_phoneNumber.setText(info.getPhoneNumber());
if (info.isHead()) {
viewHolder.txtHeadLetter.setText(String.valueOf(info.getFirstLetter()));
viewHolder.txtHeadLetter.setVisibility(View.VISIBLE);
} else {
viewHolder.txtHeadLetter.setVisibility(View.GONE);
}
} catch (Exception e) {
Log.e(TAG, e.toString());
}
return convertView;
}
@Override
public int getPositionForSection(int section) {
// 如果当前部分没有item,则之前的部分将被选择
for (int i = section; i >= 0; i--) {
for (int j = 0; j < getCount(); j++) {
if (i == 0) { // #
// For numeric section 数字
for (int k = 0; k <= 9; k++) {// 1...9
// 字符串第一个字符与1~9之间的数字进行匹配
if (StringMatcher.match(String.valueOf(getItem(j).getFirstLetter()), String.valueOf(k)))
return j;
}
} else { // A~Z
// getItem即获取ListView中的数据,并与索引进行匹配,找到Item的位置
if (StringMatcher.match(String.valueOf(getItem(j).getFirstLetter()),
String.valueOf(mSections.charAt(i))))
return j;
}
}
}
return 0;
}
@Override
public int getSectionForPosition(int position) {
return 0;
}
@Override
public Object[] getSections() {
String[] sections = new String[mSections.length()];
for (int i = 0; i < mSections.length(); i++)
sections[i] = String.valueOf(mSections.charAt(i));
return sections;
}
class ViewHolder {
ImageView img_phonePhoto;
TextView txt_contactName;
TextView txt_phoneNumber;
TextView txtHeadLetter;
}
}
// 获取通讯录:
/**得到手机通讯录联系人信息**/
private void getPhoneContacts() {
ContentResolver resolver = this.getContentResolver();
// 获取手机联系人
Cursor phoneCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
PhoneInfo.PHONES_PROJECTION, null, null, null);
if (phoneCursor != null) {
while (phoneCursor.moveToNext()) {
//得到手机号码
String phoneNumber = phoneCursor.getString(PhoneInfo.PHONES_NUMBER_INDEX);
//当手机号码为空的或者为空字段 跳过当前循环
if (TextUtils.isEmpty(phoneNumber))
continue;
//得到联系人名称
String contactName = phoneCursor.getString(PhoneInfo.PHONES_DISPLAY_NAME_INDEX);
//得到联系人ID
Long contactid = phoneCursor.getLong(PhoneInfo.PHONES_CONTACT_ID_INDEX);
//得到联系人头像ID
Long photoid = phoneCursor.getLong(PhoneInfo.PHONES_PHOTO_ID_INDEX);
phoneInfosList.add(new PhoneInfo(phoneNumber, contactName, contactid, photoid, resolver, this));
}
phoneCursor.close();
}
}
}
2、使用开源项目中自定义的ListView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.woozzu.android.widget.IndexableListView
android:id="@+id/phoneListView"
android:layout_width="match_parent"
android:layout_height="match_parent"></com.woozzu.android.widget.IndexableListView>
</LinearLayout>
/**
* 自定义索引列表
*/
public class IndexableListView extends ListView {
// 是否允许快速滑动
private boolean mIsFastScrollEnabled = false;
// 索引条
private IndexScroller mScroller = null;
private GestureDetector mGestureDetector = null;
public IndexableListView(Context context) {
super(context);
}
public IndexableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public IndexableListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean isFastScrollEnabled() {
return mIsFastScrollEnabled;
}
// 初始化Scroller
@Override
public void setFastScrollEnabled(boolean enabled) {
mIsFastScrollEnabled = enabled;
if (mIsFastScrollEnabled) {
if (mScroller == null)
mScroller = new IndexScroller(getContext(), this);
} else {
if (mScroller != null) {
mScroller.hide();
mScroller = null;
}
}
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
// 调用Scroller的绘制函数
// Overlay index bar
if (mScroller != null)
mScroller.draw(canvas);
}
// 处理触摸事件
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Intercept ListView's touch event
// 如果触摸事件发生在Scroller上,则屏蔽掉ListView上的触摸事件
if (mScroller != null && mScroller.onTouchEvent(ev))
return true;
// 当ListView发生滑动时,显示Scroller
if (mGestureDetector == null) {
// 创建一个GestureDetector
mGestureDetector = new GestureDetector(getContext(),
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// 显示索引条
mScroller.show();
return super.onFling(e1, e2, velocityX, velocityY);
}
});
}
mGestureDetector.onTouchEvent(ev);
return super.onTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
// 设置Adapter
@Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
if (mScroller != null)
mScroller.setAdapter(adapter);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mScroller != null)
mScroller.onSizeChanged(w, h, oldw, oldh);
}
}
3、自定义绘制右边的索引条:
/**
* 右侧的索引条
*/
public class IndexScroller {
private float mIndexbarWidth; // 索引条宽度
private float mIndexbarMargin; // 索引条外边距
private float mPreviewPadding; //
private float mDensity; // 密度
private float mScaledDensity; // 缩放密度
private float mAlphaRate; // 透明度
private int mState = STATE_HIDDEN; // 状态
private int mListViewWidth; // ListView宽度
private int mListViewHeight; // ListView高度
private int mCurrentSection = -1; // 当前部分
private boolean mIsIndexing = false; // 是否正在索引
private ListView mListView = null;
private SectionIndexer mIndexer = null;
private String[] mSections = null;
private RectF mIndexbarRect;
// 4种显示状态(已隐藏、正在显示、已显示、正在隐藏)
private static final int STATE_HIDDEN = 0;
private static final int STATE_SHOWING = 1;
private static final int STATE_SHOWN = 2;
private static final int STATE_HIDING = 3;
public IndexScroller(Context context, ListView lv) {
mDensity = context.getResources().getDisplayMetrics().density;
mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
mListView = lv;
setAdapter(mListView.getAdapter());
// 设置索引条宽度
mIndexbarWidth = 20 * mDensity; // 索引条宽度
mIndexbarMargin = 10 * mDensity;// 索引条间距
mPreviewPadding = 5 * mDensity; // 内边距
}
// Scroller控件绘制函数
public void draw(Canvas canvas) {
if (mState == STATE_HIDDEN)
return;
// mAlphaRate determines the rate of opacity(透明度)
Paint indexbarPaint = new Paint();
indexbarPaint.setColor(Color.BLACK);
indexbarPaint.setAlpha((int) (64 * mAlphaRate));
// 设置抗锯齿效果
indexbarPaint.setSubpixelText(true);
indexbarPaint.setAntiAlias(true);
// 画右侧字母索引的圆角矩形
canvas.drawRoundRect(mIndexbarRect, 5 * mDensity, 5 * mDensity, indexbarPaint);
if (mSections != null && mSections.length > 0) {
// Preview is shown when mCurrentSection is set
// 绘制选中时中间突出显示的TextView
if (mCurrentSection >= 0) {
Paint previewPaint = new Paint(); // 用来绘画所以条背景的画笔
previewPaint.setColor(Color.BLACK);// 设置画笔颜色为黑色
previewPaint.setAlpha(96); // 设置透明度
previewPaint.setAntiAlias(true);// 设置抗锯齿
previewPaint.setShadowLayer(3, 0, 0, Color.argb(64, 0, 0, 0)); // 设置阴影层
Paint previewTextPaint = new Paint(); // 用来绘画索引字母的画笔
previewTextPaint.setColor(Color.WHITE); // 设置画笔为白色
previewTextPaint.setAntiAlias(true); // 设置抗锯齿
previewTextPaint.setTextSize(50 * mScaledDensity); // 设置字体大小
// 文本的宽度
float previewTextWidth = previewTextPaint.measureText(mSections[mCurrentSection]);
// 计算字体的大小
float previewSize = 2 * mPreviewPadding + previewTextPaint.descent() - previewTextPaint.ascent();
RectF previewRect = new RectF(
(mListViewWidth - previewSize) / 2,
(mListViewHeight - previewSize) / 2,
(mListViewWidth - previewSize) / 2 + previewSize,
(mListViewHeight - previewSize) / 2 + previewSize);
// 中间索引的那个框
canvas.drawRoundRect(previewRect, 5 * mDensity, 5 * mDensity, previewPaint);
// 绘画索引字母
canvas.drawText(
mSections[mCurrentSection],
previewRect.left + (previewSize - previewTextWidth) / 2 - 1,
previewRect.top + mPreviewPadding - previewTextPaint.ascent() + 1,
previewTextPaint);
}
// 绘画右侧索引条的字母
Paint indexPaint = new Paint();
indexPaint.setColor(Color.WHITE);
indexPaint.setAlpha((int) (255 * mAlphaRate));
indexPaint.setAntiAlias(true);
indexPaint.setTextSize(12 * mScaledDensity);
float sectionHeight = (mIndexbarRect.height() - 2 * mIndexbarMargin) / mSections.length;
float paddingTop = (sectionHeight - (indexPaint.descent() - indexPaint.ascent())) / 2;
for (int i = 0; i < mSections.length; i++) {
float paddingLeft = (mIndexbarWidth - indexPaint.measureText(mSections[i])) / 2;
canvas.drawText(mSections[i], mIndexbarRect.left + paddingLeft,
mIndexbarRect.top + mIndexbarMargin + sectionHeight * i + paddingTop - indexPaint.ascent(), indexPaint);
}
}
}
// 响应触摸事件
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: // 按下,开始索引
// If down event occurs inside index bar region, start indexing
if (mState != STATE_HIDDEN && contains(ev.getX(), ev.getY())) {
setState(STATE_SHOWN);
// It demonstrates that the motion event started from index bar
mIsIndexing = true;
// Determine which section the point is in, and move the list to
// that section
mCurrentSection = getSectionByPoint(ev.getY());
mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));
return true;
}
break;
case MotionEvent.ACTION_MOVE: // 移动
if (mIsIndexing) {
// If this event moves inside index bar
if (contains(ev.getX(), ev.getY())) {
// Determine which section the point is in, and move the
// list to that section
mCurrentSection = getSectionByPoint(ev.getY());
mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));
}
return true;
}
break;
case MotionEvent.ACTION_UP: // 抬起
if (mIsIndexing) {
mIsIndexing = false;
mCurrentSection = -1;
}
if (mState == STATE_SHOWN)
setState(STATE_HIDING);
break;
}
return false;
}
public void onSizeChanged(int w, int h, int oldw, int oldh) {
mListViewWidth = w;
mListViewHeight = h;
// left, top, right , bottom
mIndexbarRect = new RectF(w - mIndexbarMargin - mIndexbarWidth, mIndexbarMargin, w - mIndexbarMargin, h - mIndexbarMargin);
}
// 显示
public void show() {
if (mState == STATE_HIDDEN)
setState(STATE_SHOWING);
else if (mState == STATE_HIDING)
setState(STATE_HIDING);
}
// 隐藏
public void hide() {
if (mState == STATE_SHOWN)
setState(STATE_HIDING);
}
// 注意此限制,所以自定义Adapter一定注意implement SectionIndexer接口
public void setAdapter(Adapter adapter) {
if (adapter instanceof SectionIndexer) {
mIndexer = (SectionIndexer) adapter;
mSections = (String[]) mIndexer.getSections();
}
}
// 设置显示状态
private void setState(int state) {
if (state < STATE_HIDDEN || state > STATE_HIDING)
return;
// 最终都是通过Handler进行更新
mState = state;
switch (mState) {
case STATE_HIDDEN:
// Cancel any fade effect
// 取消渐退的效果
mHandler.removeMessages(0);
break;
case STATE_SHOWING:
// Start to fade in
// 开始渐进效果
mAlphaRate = 0;
fade(0);
break;
case STATE_SHOWN:
// Cancel any fade effect
// 取消渐退的效果
mHandler.removeMessages(0);
break;
case STATE_HIDING:
// Start to fade out after three seconds
// 隐藏3秒钟
mAlphaRate = 1;
fade(3000);
break;
}
}
// 判断触摸点是否在Scroller控件上
private boolean contains(float x, float y) {
// Determine if the point is in index bar region, which includes the
// right margin of the bar
return (x >= mIndexbarRect.left && y >= mIndexbarRect.top && y <= mIndexbarRect.top + mIndexbarRect.height());
}
private int getSectionByPoint(float y) {
if (mSections == null || mSections.length == 0)
return 0;
if (y < mIndexbarRect.top + mIndexbarMargin)
return 0;
if (y >= mIndexbarRect.top + mIndexbarRect.height() - mIndexbarMargin)
return mSections.length - 1;
return (int) ((y - mIndexbarRect.top - mIndexbarMargin) / ((mIndexbarRect
.height() - 2 * mIndexbarMargin) / mSections.length));
}
// 延时显示fade效果
private void fade(long delay) {
mHandler.removeMessages(0);
mHandler.sendEmptyMessageAtTime(0, SystemClock.uptimeMillis() + delay);
}
// 处理进度条View的更新操作
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (mState) {
case STATE_SHOWING:
// Fade in effect
// 淡进效果
mAlphaRate += (1 - mAlphaRate) * 0.2;
if (mAlphaRate > 0.9) {
mAlphaRate = 1;
setState(STATE_SHOWN);
}
// 请求重绘ListView
mListView.invalidate();
fade(10);
break;
case STATE_SHOWN:
// If no action, hide automatically
setState(STATE_HIDING);
break;
case STATE_HIDING:
// Fade out effect
// 淡出效果
mAlphaRate -= mAlphaRate * 0.2;
if (mAlphaRate < 0.1) {
mAlphaRate = 0;
setState(STATE_HIDDEN);
}
mListView.invalidate();
fade(10);
break;
}
}
};
}
4、ListView的工具类,用来匹配字符:
public class StringMatcher {
// 这些变量是韩文,小巫也不知道是什么意思,有谁懂的马上联系我啊
private final static char KOREAN_UNICODE_START = '가'; // 韩文字符编码开始?
private final static char KOREAN_UNICODE_END = '힣'; // 韩文字符编码结束?
private final static char KOREAN_UNIT = '까' - '가'; // 不知道是啥?
// 韩文的一些字符初始化
private final static char[] KOREAN_INITIAL = { 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ',
'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ',
'ㅎ' };
/**
* 字符匹配
* @param value 需要keyword匹配的字符串
* @param keyword #ABCDEFGHIJKLMNOPQRSTUVWXYZ中的一个
* @return
*/
public static boolean match(String value, String keyword) {
if (value == null || keyword == null)
return false;
if (keyword.length() > value.length())
return false;
int i = 0, j = 0;
do {
// 如果是韩文字符并且在韩文初始数组里面
if (isKorean(value.charAt(i)) && isInitialSound(keyword.charAt(j))) {
if (keyword.charAt(j) == getInitialSound(value.charAt(i))) {
i++;
j++;
} else if (j > 0)
break;
else
i++;
} else {
// 逐个字符匹配
if (keyword.charAt(j) == value.charAt(i)) {
i++;
j++;
} else if (j > 0)
break;
else
i++;
}
} while (i < value.length() && j < keyword.length());
// 如果最后j等于keyword的长度说明匹配成功
return (j == keyword.length()) ? true : false;
}
// 判断字符是否在韩文字符编码范围内
private static boolean isKorean(char c) {
if (c >= KOREAN_UNICODE_START && c <= KOREAN_UNICODE_END)
return true;
return false;
}
// 判断是否在韩文字符里面
private static boolean isInitialSound(char c) {
for (char i : KOREAN_INITIAL) {
if (c == i)
return true;
}
return false;
}
// 获得韩文初始化字符数组里面的一个字符
private static char getInitialSound(char c) {
return KOREAN_INITIAL[(c - KOREAN_UNICODE_START) / KOREAN_UNIT];
}
}
5、 电话信息记录类:
/**
* Created by admin on 2015/9/21.
*/
public class PhoneInfo {
/** 查询通讯录相关参数 **/
public static final String[] PHONES_PROJECTION = new String[] {
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.PHOTO_ID, ContactsContract.CommonDataKinds.Phone.CONTACT_ID };
public final static int PHONES_DISPLAY_NAME_INDEX = 0;
public final static int PHONES_NUMBER_INDEX = 1;
public final static int PHONES_PHOTO_ID_INDEX = 2;
public final static int PHONES_CONTACT_ID_INDEX = 3;
private String contactName;
private String phoneNumber;
private Long contactid;
private Long photoid;
private Bitmap contactPhoto;
private char firstLetter; // contactName的首字母(大写)
private boolean isHead; // 判断是否是一个索引的首元素
// 获取contactName首字母的相关参数
private final static int[] li_SecPosValue = { 1601, 1637, 1833, 2078, 2274,
2302, 2433, 2594, 2787, 3106, 3212, 3472, 3635, 3722, 3730, 3858,
4027, 4086, 4390, 4558, 4684, 4925, 5249, 5590 };
private final static String[] lc_FirstLetter = { "A", "B", "C", "D", "E",
"F", "G", "H", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
"T", "W", "X", "Y", "Z" };
public PhoneInfo() {
}
public PhoneInfo(String phoneNumber, String contactName, Long contactid, Long photoid, ContentResolver resolver, Context context) {
this.phoneNumber = phoneNumber;
this.contactName = contactName;
this.contactid = contactid;
this.photoid = photoid;
getContactPhoto(photoid, resolver, context);
firstLetter = getFirstLetter(contactName).charAt(0);
isHead = false;
}
// 根据photoid获取图片Bitmap
public Bitmap getContactPhoto(Long photoid, ContentResolver resolver, Context context) {
//photoid大于0 表示联系人有头像 如果没有给此人设置头像则给他一个默认的
if(photoid > 0 ) {
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactid);
InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(resolver, uri);
contactPhoto = BitmapFactory.decodeStream(input);
}else {
contactPhoto = BitmapFactory.decodeResource(context.getResources(), R.mipmap.error_photo);
}
return contactPhoto;
}
// 判断每个string的首字母
/**
* 取得给定汉字的首字母,即声母
* @param chinese 给定的汉字
* @return 给定汉字的声母
*/
private String getFirstLetter(String chinese) {
if (chinese == null || chinese.trim().length() == 0) {
return "";
}
chinese = this.conversionStr(chinese, "GB2312", "ISO8859-1");
if (chinese.length() > 1) { // 判断是不是汉字
int li_SectorCode = (int) chinese.charAt(0); // 汉字区码
int li_PositionCode = (int) chinese.charAt(1); // 汉字位码
li_SectorCode = li_SectorCode - 160;
li_PositionCode = li_PositionCode - 160;
int li_SecPosCode = li_SectorCode * 100 + li_PositionCode; // 汉字区位码
if (li_SecPosCode > 1600 && li_SecPosCode < 5590) {
for (int i = 0; i < 23; i++) {
if (li_SecPosCode >= li_SecPosValue[i]
&& li_SecPosCode < li_SecPosValue[i + 1]) {
chinese = lc_FirstLetter[i];
break;
}
}
} else { // 非汉字字符,如图形符号或ASCII码
chinese = this.conversionStr(chinese, "ISO8859-1", "GB2312");
chinese = chinese.substring(0, 1);
}
}
return chinese;
}
/**
* 字符串编码转换
* @param str 要转换编码的字符串
* @param charsetName 原来的编码
* @param toCharsetName 转换后的编码
* @return 经过编码转换后的字符串
*/
private String conversionStr(String str, String charsetName,String toCharsetName) {
try {
str = new String(str.getBytes(charsetName), toCharsetName);
} catch (UnsupportedEncodingException ex) {
System.out.println("字符串编码转换异常:" + ex.getMessage());
}
return str;
}
/** 为一个List<PhoneInfo> 提供一个函数判断该元素是否一个字母索引的首字母 **/
public static void tagIsHead (List<PhoneInfo> phoneInfos) {
boolean[] isHead = new boolean[27];
int index;
// 先清除所有标志
for (PhoneInfo info : phoneInfos) {
info.isHead = false;
index = info.firstLetter >= 'A' && info.firstLetter <= 'Z' ? info.firstLetter - 'A' : 26;
if (!isHead[index]) {
isHead[index] = true;
info.isHead = true;
}
}
}
/** 一些列Setter函数 **/
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public void setContactName(String contactName) {
this.contactName = contactName;
}
public void setContactid(Long contactid) {
this.contactid = contactid;
}
public void setPhotoid(Long photoid, ContentResolver resolver, Context context) {
this.photoid = photoid;
getContactPhoto(photoid, resolver, context);
}
public void setContactPhoto(Bitmap contactPhoto) {
this.contactPhoto = contactPhoto;
}
public void setFirstLetter(char firstLetter) {
this.firstLetter = firstLetter;
}
public void setIsHead(boolean isHead) {
this.isHead = isHead;
}
/** 一些getter函数 **/
public String getPhoneNumber() {
return phoneNumber;
}
public String getContactName() {
return contactName;
}
public Long getContactid() {
return contactid;
}
public Long getPhotoid() {
return photoid;
}
public Bitmap getContactPhoto() {
return contactPhoto;
}
public char getFirstLetter() {
return firstLetter;
}
public boolean isHead() {
return isHead;
}
}
6、每个Item的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="wrap_content"
android:padding="15dp"
>
<TextView
android:id="@+id/txtHeadLetter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/grey21"
android:textColor="@color/white"
android:gravity="left|center_vertical"
android:paddingLeft="10dp"
android:visibility="gone"
/>
<ImageView
android:id="@+id/img_phonePhoto"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_below="@id/txtHeadLetter"
/>
<TextView
android:id="@+id/txt_contactName"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_toRightOf="@id/img_phonePhoto"
android:layout_marginLeft="10dp"
android:gravity="center_vertical"
android:layout_below="@id/txtHeadLetter"
/>
<TextView
android:id="@+id/txt_phoneNumber"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_toRightOf="@id/img_phonePhoto"
android:layout_marginLeft="10dp"
android:layout_below="@id/txt_contactName"
android:gravity="center_vertical"
/>
</RelativeLayout>