这里主要参考了使用SectionIndexer实现微信通讯录的效果

在这里做个记录

效果图

页面使用RelativeLayout,主要分为三个部分,match_parent的主listView,右边字母的SideBar,还有就是微信那种点击字母时浮动的一个TextView

布局:

  fragment_contacts.xml

 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:listSelector="@android:color/transparent"
android:id="@+id/listFragmentContacts"/>
<TextView
android:id="@+id/textFragmentContacts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:padding="20dp"
android:textSize="40sp"
android:textColor="@android:color/white"
android:background="#33000000"
android:visibility="gone"
tools:visibility="visible"
tools:text="A"/>
<lee.example.com.testdj.customWeight.ContactsMySideBar
android:id="@+id/sideBarFragmentContacts"
android:layout_width="24dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"/>
</RelativeLayout>

  layout_contacts_item.xml

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"> <!--section,表示组-->
<TextView
android:id="@+id/tvSectionContactsItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left"
android:textSize="22sp"
android:textColor="#666666"
tools:text="section"/> <TextView
android:id="@+id/tvNormalContactsItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="6dp"
android:textColor="@android:color/black"
android:background="@android:color/white"
android:textSize="20sp"
tools:text="text"/>
</LinearLayout>

实现:

首先,自定义View来实现SideBar,ContactsMySideBar.java

 public class ContactsMySideBar extends View{

     private static final String TAG = "ContactsMySideBar";

     private SectionIndexer sectionIndexer;
private final char[] letters = new char[]{'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 Paint paint; //画笔用来绘制字母
private ListView listView;
private int focusedIndex = -1; //点击选中的索引
private int drawWidth, drawHeight; //绘制单个字母的宽高 public interface OnTouchChangedListener{
void onTouchDown(char c);
void onTouchUp();
} private OnTouchChangedListener listener; public void setOnTouchChangedListener(OnTouchChangedListener listener){
this.listener = listener;
} public void setListView(ListView listView){
this.listView = listView;
sectionIndexer = (SectionIndexer) listView.getAdapter();
} public ContactsMySideBar(Context context) {
super(context);
init();
} public ContactsMySideBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public ContactsMySideBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
} private void init(){
//设置画笔属性
paint = new Paint();
paint.setColor(Color.GRAY);
paint.setTextSize(18f);
paint.setTextAlign(Paint.Align.CENTER);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
drawWidth = getMeasuredWidth()/2; //宽度为sideBar的一半
drawHeight = getMeasuredHeight()/letters.length; //设置高度
} @Override
protected void onDraw(Canvas canvas) {
for (int i = 0; i < letters.length; i++){
canvas.drawText(String.valueOf(letters[i]), drawWidth, drawHeight + (i * drawHeight), paint);
}
} @Override
public boolean onTouchEvent(MotionEvent event) {
int pointerY = (int) event.getY();
int selectedIndex = pointerY / drawHeight;
if (selectedIndex >= letters.length){
selectedIndex = letters.length - 1;
}else if(selectedIndex < 0){
selectedIndex = 0;
} switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//点击时设置为半透明
setBackgroundColor(Color.parseColor("#33000000"));
case MotionEvent.ACTION_MOVE:
if (sectionIndexer == null){
sectionIndexer = (SectionIndexer) listView.getAdapter();
}
// Log.d(TAG, letters[selectedIndex] + "");
//根据数组中的元素获取对应的组位置
int position = sectionIndexer.getPositionForSection(letters[selectedIndex]);
Log.d(TAG, "" + position);
if (position == -1){
return true;
}
if (selectedIndex != 0){
if (position != 0){
//改变当前listView所处位置
listView.setSelection(position);
}
}else {
listView.setSelection(position);
} //重绘SideBar
invalidate();
if (null != listener){
listener.onTouchDown(letters[selectedIndex]);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
//松开手指取消背景色
setBackgroundResource(android.R.color.transparent);
invalidate();
if (null != listener){
listener.onTouchUp();
}
break;
}
return true;
}
}

这部分代码很简单,重写了OnMeasure和OnDraw来绘制SideBar,在OnTouchEvent中写出了SideBar的响应事件

定义listView的adapter

SideBarAdapter.java
 public class SideBarAdapter extends BaseAdapter implements SectionIndexer{

     private static final String TAG = "SideBarAdapter";

     private Context context;
private List<ContactsModel> modelList;
private LayoutInflater layoutInflater; public SideBarAdapter(Context context, List<ContactsModel> modelList){
this.context = context;
this.modelList = modelList;
layoutInflater = LayoutInflater.from(context);
} @Override
public int getCount() {
return modelList.size();
} @Override
public ContactsModel getItem(int i) {
return modelList.get(i);
} @Override
public long getItemId(int i) {
return i;
} @Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
Holder holder;
if (convertView == null){
convertView = layoutInflater.inflate(R.layout.layout_contacts_item, viewGroup, false);
holder = new Holder();
holder.normalTv = (TextView) convertView.findViewById(R.id.tvNormalContactsItem);
holder.sectionTv = (TextView) convertView.findViewById(R.id.tvSectionContactsItem);
convertView.setTag(holder);
}else {
holder = (Holder) convertView.getTag();
}
String text = modelList.get(i).getName();
setSectionTv(i, holder, text);
setNormalTv(holder, text); return convertView;
} private void setNormalTv(Holder holder, String text){
holder.normalTv.setText(text);
} private void setSectionTv(int position, Holder holder, String text){
//获取每个item字符串的头一个字符
// Log.d(TAG, ZhCharactersUtil.changeToSpell(text));
char firstChar = ZhCharactersUtil.changeToSpell(text).toUpperCase().charAt(0);
//若为第一个位置直接设置组view就行
if (position == 0) {
holder.sectionTv.setVisibility(View.VISIBLE);
holder.sectionTv.setText((firstChar + "").toUpperCase());
}
//若不是,需判断当前item首字母与上一个item首字母是否一致,再设置组view
else {
String preLabel = modelList.get(position - 1).getName();
//获取上一个item的首字母
char preFirstChar = ZhCharactersUtil.changeToSpell(preLabel).toUpperCase().charAt(0);
if (firstChar != preFirstChar) {
holder.sectionTv.setVisibility(View.VISIBLE);
holder.sectionTv.setText((firstChar + "").toUpperCase());
} else {
//若与上一个item首字母一致则不需要重复设置组view
holder.sectionTv.setVisibility(View.GONE);
}
}
} @Override
public Object[] getSections() {
//获取组信息的数组,比如这里可以返回char[]{'A','B',...}
return new Object[0];
} @Override
public int getPositionForSection(int section) {
//根据组信息获取索引
for (int i = 0; i < modelList.size(); i++) {
String str = modelList.get(i).getName();
char firstChar = ZhCharactersUtil.changeToSpell(str).toUpperCase().charAt(0);
if (firstChar == section) {
return i;
}
}
return 0;
} @Override
public int getSectionForPosition(int i) {
//根据索引获取组信息,这里不做处理
return 0;
} private final class Holder {
TextView normalTv, sectionTv;
}
}

这里用到了一个第三方,jpinyin,是用来对汉字进行处理,转换成拼音等等,ZhCharactersUtil.java就是用来处理汉字的,我这里只需要取拼音的第一个字母

 public class ZhCharactersUtil {
public static String changeToSpell(String string){
char[] chars = PinyinHelper.getShortPinyin(string).toCharArray();
return chars[0] + "";
} }

这里的ContactsModel里边只有两个属性,一个name,一个firstCharacter,其中firstCharacter是用来进行排序的,上边adapter的代码可以用modelList.get(i).getFirstCharacter()来简化一些,偷懒了,所以开始写好了就放在那没动,这里这个model就不记录了。

在Activity中使用,我这里是在fragment中使用,都是一样的

 private ListView listView;
//悬浮的textView
private TextView maskText;
private ContactsMySideBar sideBar; //然后对他们进行初始化
 private void initData(){
ContactsModel model1 = new ContactsModel("张一");
ContactsModel model2 = new ContactsModel("张二");
ContactsModel model3 = new ContactsModel("李一");
ContactsModel model4 = new ContactsModel("李二");
ContactsModel model5 = new ContactsModel("赵一");
ContactsModel model6 = new ContactsModel("赵二");
ContactsModel model7 = new ContactsModel("王三");
ContactsModel model8 = new ContactsModel("王二");
ContactsModel model9 = new ContactsModel("阿张");
ContactsModel model10 = new ContactsModel("不行");
ContactsModel model11 = new ContactsModel("特别");
ContactsModel model12 = new ContactsModel("将就");
ContactsModel model13 = new ContactsModel("公司");
ContactsModel model14 = new ContactsModel("空是");
ContactsModel model15 = new ContactsModel("好附件打开");
ContactsModel model16 = new ContactsModel("现在");
ContactsModel model17 = new ContactsModel("哦跑"); ContactsModel model18 = new ContactsModel("aaasss");
ContactsModel model19 = new ContactsModel("bbb");
ContactsModel model20 = new ContactsModel("s");
ContactsModel model21 = new ContactsModel("jjj");
ContactsModel model22 = new ContactsModel("oot"); modelList.add(model1);
modelList.add(model2);
modelList.add(model3);
modelList.add(model4);
modelList.add(model5);
modelList.add(model6);
modelList.add(model7);
modelList.add(model8);
modelList.add(model9);
modelList.add(model10);
modelList.add(model11);
modelList.add(model12);
modelList.add(model13);
modelList.add(model14);
modelList.add(model15);
modelList.add(model16);
modelList.add(model17);
// modelList.add(model18);
// modelList.add(model19);
// modelList.add(model20);
// modelList.add(model21);
// modelList.add(model22);
Collections.sort(modelList, new Comparator<ContactsModel>() {
@Override
public int compare(ContactsModel contactsModel, ContactsModel t1) {
return contactsModel.getFirstCharacter().compareTo(t1.getFirstCharacter());
}
}); adapter = new SideBarAdapter(getContext(), modelList);
listView.setAdapter(adapter);
sideBar.setListView(listView);
sideBar.setOnTouchChangedListener(new ContactsMySideBar.OnTouchChangedListener() {
@Override
public void onTouchDown(char c) {
maskText.setText(c + "");
maskText.setVisibility(View.VISIBLE);
} @Override
public void onTouchUp() {
maskText.setVisibility(View.GONE);
}
});
}

这里添加了一些假数据,完成。

通过SectionIndexer实现微信通讯录的更多相关文章

  1. Android中使用ExpandableListView实现微信通讯录界面(完善仿微信APP)

    之前的博文<Android中使用ExpandableListView实现好友分组>我简单介绍了使用ExpandableListView实现简单的好友分组功能,今天我们针对之前的所做的仿微信 ...

  2. 剖析:如何用 SwiftUI 5天组装一个微信 —— 通讯录发现我篇

    前置资源 GitHub: SwiftUI-WeChatDemo 第零章:用 SwiftUI 5天组装一个微信 第一章:剖析:如何用 SwiftUI 5天组装一个微信 -- 聊天界面篇 通讯录 通讯录的 ...

  3. itchat 爬了爬自己的微信通讯录

    参考 一件有趣的事: 爬了爬自己的微信朋友 忘记从谁那里看到的了,俺也来试试 首先在annconda prompt里面安装了itchat包 pip install itchat 目前对python这里 ...

  4. 2016年我们重新思考移动互联网创业的风险, 微信还是APP?

    感觉这两年前端开发又火起来了,很多做内容创业和做微电商创业的人,往往都选择了运营微信号.对于做纯技术开发的人来说,一般是看不上微信号的,感觉没什么技术含量,或者说没什么技术壁垒.也有另一批人观点相反的 ...

  5. C#开发微信门户及应用(19)-微信企业号的消息发送(文本、图片、文件、语音、视频、图文消息等)

    我们知道,企业号主要是面向企业需求而生的,因此内部消息的交流显得非常重要,而且发送.回复消息数量应该很可观,对于大企业尤其如此,因此可以结合企业号实现内部消息的交流.企业号具有关注安全.消息无限制等特 ...

  6. 微信公众平台开发教程(十一)微信"企业号“上线

    什么是企业号? 企业号是微信为企业客户提供的移动应用入口 关注更安全 只有企业通讯录的成员才能关注企业号,分级管理员.保密消息等各种特性确保企业内部信息的安全. 应用可配置 企业可自行在企业号中可配置 ...

  7. 微信公开课(北京站)速记 微信、微信支付、O2O的定义与关联

    本文为4月29日微信公开课(北京站)微信产品部演讲全文速记,讲述了微信官方对微信.微信支付.O2O的定义与关联等问题的看法与观点. 作者:微信产品部 刘涵涛 吴毅 去年夏天有一个全民打飞机的盛况,这实 ...

  8. ios应用接入微信开放平台

    前几天试了一下服务端接入微信公众平台,昨天又看了一下APP接入开放平台 开放平台和公众平台的差别 公众平台针对的是公众账号,除了提供管理后台之外.也开放了若干接口,让微信server和开发人员自己的应 ...

  9. 微信 python 接口 -- itchat 文档

    itchat 一. 安装 $ pip install itchat 特殊的字典使用方式 通过打印 itchat 的用户以及注册消息的参数, 可以发现这些值都是字典. 但实际上 itchat 精心构造了 ...

随机推荐

  1. python框架django中结合vue进行前后端分离

    一:创建django项目 1.django-admin startproject mysite # 创建mysite项目 2.django-admin startapp app01# 创建app01应 ...

  2. NHibernate 过滤器(第十五篇)

    NHibernate过滤器相当于定义一个非常类似于类和集合上使用的where子句.ISession 中默认是不启用过滤器的,必须通过ISession.EnableFilter()方法显式的启用. 该方 ...

  3. Objective-C字面量语法总结

    通常情况下,创建数组,字典的时候需要写一些很长的方法名,今天就总结一下如何使用字面量语法代替这些方法. 1.数值的创建 NSNumber *number1 = [NSNumber numberWith ...

  4. 【java】实体类中 Set<对象> 按照对象的某个字段对set排序

    背景: User实体类 有个属性是 Set<PositionChange> 职位变更字段 如下: PositionChange实体类  有个属性是positionStartDate   什 ...

  5. python笔记12-python多线程之事件(Event)

    前言 小伙伴a,b,c围着吃火锅,当菜上齐了,请客的主人说:开吃!,于是小伙伴一起动筷子,这种场景如何实现 Event(事件) Event(事件):事件处理的机制:全局定义了一个内置标志Flag,如果 ...

  6. 两篇整合Activiti Modeler到业务系统

    1. 无法进入editor. http://localhost:8080/YouPRJ/modeler/service/editor?id=2050,前提是这个id必需存在与act_re_model表 ...

  7. mysql安装以后无法登陆的的解决方法((ERROR 1698 (28000): Access denied for user 'root'@'localhost'))

    mysql安装以后无法登陆的的解决方法((ERROR 1698 (28000): Access denied for user 'root'@'localhost')) 解决步骤: [====> ...

  8. Mount CIFS

    mount -t cifs -o username="共享用户",password="密码" //ip/sharing_folder /mountpoint [ ...

  9. 转:一位10年Java工作经验的架构师聊Java和工作经验

    黄勇( 博客),从事近十年的 JavaEE 应用开发工作,现任阿里巴巴公司系统架构师.对分布式服务架构与大数据技术有深入研究,具有丰富的 B/S 架构开发经验与项目实战经验,擅长敏捷开发模式.国内开源 ...

  10. SVN配置常见错误

    1.svnserve.conf:12: Option expected 为什么会出现这个错误呢,就是因为subversion读取配置文件svnserve.conf时,无法识别有前置空格的配置文件,如 ...