这篇写下常用的类似通讯录似得效果 右边自定义一个sidebar 左边使用listview实现
1、自定义SideBar
①重写onDraw 计算每个字母所占的高度 就是整体的高度除以要画的字母的个数,所以每个字母的高为依次叠加
因为竖行排列,所以每个字母的宽都一样,每个字母的x为整体的宽度减去该字母的宽度的一半
②重写触摸事件 拿到触摸的y除以总的高度 然后乘以所有字母的个数 就可以拿到点击的是第几个字母,如果该字母改变则进行重绘
③设置回调 点击该字母具体方法由外界实现
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 float size;
private int choose = -1;// 选中
private Paint paint = new Paint();
private TextView mTextDialog;
public void setTextView(TextView mTextDialog) {
this.mTextDialog = mTextDialog;
}
public SideBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public SideBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SideBar(Context context) {
super(context);
init(context);
}
private void init(Context context) {
size = DeviceUtil.dip2px(context, 13);
}
/**
* 重写这个方法
*/
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 获取焦点改变背景颜色.
int height = getHeight();// 获取对应高度
int width = getWidth(); // 获取对应宽度
int singleHeight = height / b.length;// 获取每一个字母的高度
for (int i = 0; i < b.length; i++) {
if (!isInEditMode()) {
paint.setColor(Color.parseColor("#838383"));
}
//设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
paint.setAntiAlias(true);
paint.setTextSize(size);
// 选中的状态
if (i == choose) {
paint.setColor(getResources().getColor(R.color.yellow_light));
paint.setFakeBoldText(true); //true为粗体,false为非粗体
}
// x坐标等于中间-字符串宽度的一半.
float xPos = width / 2 - paint.measureText(b[i]) / 2;
float yPos = singleHeight * i + singleHeight;
canvas.drawText(b[i], xPos, yPos, paint);
paint.reset();// 重置画笔
}
}
@SuppressWarnings("deprecation")
@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:
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);
}
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);
}
}
2、准备好汉字转拼音的工具类
①将jar包丢到libs目录下
② 汉字转拼音工具类
public class PinYinUtil { /** * 获取汉字的拼音 * @param chinese * @return */ public static String getPinYin(String chinese){ if(TextUtils.isEmpty(chinese))return null; //输出的格式化对象,用来决定输出字母的大小写,是否带有音标之类 HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); format.setCaseType(HanyuPinyinCaseType.UPPERCASE);//设置大写字母 format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);//设置不带有音汉字进行获取,所以要将字符串转为字符数组,一个一个获取,最后拼接 StringBuilder builder = new StringBuilder(); char[] chars = chinese.toCharArray(); for (int i = 0; i < chars.length; i++) { char c = chars[i]; //1.过滤空格 if(Character.isWhitespace(c)){ //如果是空格,则忽略即可 continue; } //2.简单判断是否是汉字:一个字节范围是-128~127,所以汉字一定大于127 if(c > 127){ //说明有可能是汉字,那么利用Pinyin4j的api进行获取 try { //由于多音字的存在,返回的是数组,比如:单: {dan, chan ,shan} String[] pinyinArr = PinyinHelper.toHanyuPinyinStringArray(c, format); if(pinyinArr!=null){ //问题来了:取哪个拼音?答:暂时只能取第1个 //为啥呢?:首先大部分汉字只有一个读音,对于多音字的情况,由于我们实在无能为力去 //判断它的真实读音,也只能取第1个罢了。 builder.append(pinyinArr[0]); } } catch (Exception e) { e.printStackTrace(); //如果异常,说明不是正确的汉字,直接忽略 } }else { //说明肯定不是汉字,一般是英文字母,我们选择直接拼接 builder.append(c); } } return builder.toString(); } }
3、将数据放到listview上展示
①布局如下
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ListView
android:id="@+id/school_friend_member"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="@android:color/transparent"
android:divider="@mipmap/bg_cut_line"
android:dividerHeight="1px"
android:fadingEdge="none"
android:listSelector="@android:color/transparent"
android:scrollbars="none"></ListView>
<TextView
android:id="@+id/school_friend_dialog"
android:layout_width="80.0dip"
android:layout_height="80.0dip"
android:layout_gravity="center"
android:background="@drawable/bg_show_head_toast"
android:gravity="center"
android:textColor="#ffffffff"
android:textSize="30.0dip"
android:visibility="invisible" />
<com.hiteamtech.ddbes.ui.view.SideBar
android:id="@+id/school_friend_sidrbar"
android:layout_width="30dp"
android:layout_height="match_parent"
android:layout_gravity="right|center" />
</FrameLayout>
②初始化各个类
mListView = (ListView) findViewById(R.id.school_friend_member);
mSideBar = (SideBar) findViewById(R.id.school_friend_sidrbar);
mDialog = (TextView) findViewById(R.id.school_friend_dialog);
mSideBar.setOnTouchingLetterChangedListener(this);
mSideBar.setTextView(mDialog);
③给获取到的集合排序
Collections.sort(complete, new Comparator<UserBean>() { @Override public int compare(UserBean o1, AUserBean o2) { String pinying1; String pinying2; if(o1.getAssetname()!=null){ pinying1= PinYinUtil.getPinYin(o1.getUserName()); }else{ pinying1= PinYinUtil.getPinYin("z"); } if(o2.getAssetname()!=null){ pinying2= PinYinUtil.getPinYin(o2.getUserName()); }else{ pinying2=PinYinUtil.getPinYin("z"); } return pinying1.compareTo(pinying2); } });
④给listview设置adapter
adapter如下:
public class InviteFriendListAdapter extends BaseAdapter implements SectionIndexer {
private LayoutInflater inflater;
private Context mContext;
private List<UserBean> list;
public InviteFriendListAdapter(Context context, List<UserBean> list) {
this.mContext = context;
this.list = list;
}
/**
* 当ListView数据发生变化时,调用此方法来更新ListView
*
* @param list
*/
public void updateListView(List<UserBean> list) {
this.list = list;
notifyDataSetChanged();
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
// inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater = LayoutInflater.from(mContext);
convertView = inflater.inflate(R.layout.invitation_friends_item_layout, null);
holder = new ViewHolder();
holder.ivHead = (CircleImageView) convertView.findViewById(R.id.head);
holder.tvTitle = (TextView) convertView.findViewById(R.id.title);
holder.tvLetter = (TextView) convertView.findViewById(R.id.catalog);
holder.tvLine = (TextView) convertView.findViewById(R.id.line);
holder.checkFriendCb = (CheckBox) convertView.findViewById(R.id.checkFriendCb);
holder.tvContent = (LinearLayout) convertView.findViewById(R.id.content);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final UserBean phoneContactBean = list.get(position);
if (phoneContactBean != null) {
// 根据position获取分类的首字母的Char ascii值
int section = getSectionForPosition(position);
// 如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现
if (position == getPositionForSection(section)) {
holder.tvLetter.setVisibility(View.VISIBLE);
holder.tvLetter.setText("☆".equals(phoneContactBean.getSortLetters()) ? phoneContactBean.getSortLetters() + "(管理员)" : phoneContactBean.getSortLetters());
holder.tvLine.setVisibility(View.VISIBLE);
} else {
holder.tvLetter.setVisibility(View.GONE);
holder.tvLine.setVisibility(View.GONE);
}
holder.tvTitle.setText(phoneContactBean.getUserName());
if (phoneContactBean.isChecked()) {
holder.checkFriendCb.setChecked(true);
} else {
holder.checkFriendCb.setChecked(false);
}
Glide.with(mContext).load(phoneContactBean.getHeadimg()).asBitmap().error(R.mipmap.headimg_default).into(holder.ivHead);
holder.tvContent.setOnClickListener(new OnCheckFriendClick(phoneContactBean));
}
return convertView;
}
private class OnCheckFriendClick implements View.OnClickListener {
private UserBean userBean;
public OnCheckFriendClick(UserBean userBean) {
this.userBean = userBean;
}
@Override
public void onClick(View v) {
if (userBean.isChecked()) {
userBean.setChecked(false);
} else {
userBean.setChecked(true);
}
notifyDataSetChanged();
}
}
class ViewHolder {
CircleImageView ivHead;
TextView tvLetter;
TextView tvTitle;
TextView tvLine;
CheckBox checkFriendCb;
LinearLayout tvContent;
}
/**
* 根据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;
}
@Override
public Object[] getSections() {
return null;
}
public List<UserBean> getList() {
return list;
}
}