Android仿微信通讯录列表侧边栏效果

先看Android仿微信通讯录列表侧边栏效果图

这是比较常见的效果了吧

列表根据首字符的拼音字母来排序,且可以通过侧边栏的字母索引来进行定位。

实现这样一个效果并不难,只要自定义一个索引View,然后引入一个可以对汉字进行拼音解析的jar包——pinyin4j-2.5.0即可

首先,先来定义侧边栏控件View,只要直接画出来即可。

字母选中项会变为红色,且滑动时背景会变色,此时SideBar并不包含居中的提示文本

public class SideBar extends View {

  private Paint paint = new Paint();

  private int choose = -1;

  private boolean showBackground;

  public static String[] letters = {"#", "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 OnChooseLetterChangedListener onChooseLetterChangedListener;

  public SideBar(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  }

  public SideBar(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public SideBar(Context context) {
    super(context);
  }

  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (showBackground) {
      canvas.drawColor(Color.parseColor("#D9D9D9"));
    }
    int height = getHeight();
    int width = getWidth();
    //平均每个字母占的高度
    int singleHeight = height / letters.length;
    for (int i = 0; i < letters.length; i++) {
      paint.setColor(Color.BLACK);
      paint.setAntiAlias(true);
      paint.setTextSize(25);
      if (i == choose) {
        paint.setColor(Color.parseColor("#FF2828"));
        paint.setFakeBoldText(true);
      }
      float x = width / 2 - paint.measureText(letters[i]) / 2;
      float y = singleHeight * i + singleHeight;
      canvas.drawText(letters[i], x, y, paint);
      paint.reset();
    }
  }

  @Override
  public boolean dispatchTouchEvent(MotionEvent event) {
    int action = event.getAction();
    float y = event.getY();
    int oldChoose = choose;
    int c = (int) (y / getHeight() * letters.length);
    switch (action) {
      case MotionEvent.ACTION_DOWN:
        showBackground = true;
        if (oldChoose != c && onChooseLetterChangedListener != null) {
          if (c > -1 && c < letters.length) {
            onChooseLetterChangedListener.onChooseLetter(letters[c]);
            choose = c;
            invalidate();
          }
        }
        break;
      case MotionEvent.ACTION_MOVE:
        if (oldChoose != c && onChooseLetterChangedListener != null) {
          if (c > -1 && c < letters.length) {
            onChooseLetterChangedListener.onChooseLetter(letters[c]);
            choose = c;
            invalidate();
          }
        }
        break;
      case MotionEvent.ACTION_UP:
        showBackground = false;
        choose = -1;
        if (onChooseLetterChangedListener != null) {
          onChooseLetterChangedListener.onNoChooseLetter();
        }
        invalidate();
        break;
    }
    return true;
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    return super.onTouchEvent(event);
  }

  public void setOnTouchingLetterChangedListener(OnChooseLetterChangedListener onChooseLetterChangedListener) {
    this.onChooseLetterChangedListener = onChooseLetterChangedListener;
  }

  public interface OnChooseLetterChangedListener {

    void onChooseLetter(String s);

    void onNoChooseLetter();

  }

}

SideBar只是画出了侧边栏索引条而已,不包含居中的提示文本,这个在另一个布局添加即可

public class HintSideBar extends RelativeLayout implements SideBar.OnChooseLetterChangedListener {

  private TextView tv_hint;

  private SideBar.OnChooseLetterChangedListener onChooseLetterChangedListener;

  public HintSideBar(Context context, AttributeSet attrs) {
    super(context, attrs);
    LayoutInflater.from(context).inflate(R.layout.view_hint_side_bar, this);
    initView();
  }

  private void initView() {
    SideBar sideBar = (SideBar) findViewById(R.id.sideBar);
    tv_hint = (TextView) findViewById(R.id.tv_hint);
    sideBar.setOnTouchingLetterChangedListener(this);
  }

  @Override
  public void onChooseLetter(String s) {
    tv_hint.setText(s);
    tv_hint.setVisibility(VISIBLE);
    if (onChooseLetterChangedListener != null) {
      onChooseLetterChangedListener.onChooseLetter(s);
    }
  }

  @Override
  public void onNoChooseLetter() {
    tv_hint.setVisibility(INVISIBLE);
    if (onChooseLetterChangedListener != null) {
      onChooseLetterChangedListener.onNoChooseLetter();
    }
  }

  public void setOnChooseLetterChangedListener(SideBar.OnChooseLetterChangedListener onChooseLetterChangedListener) {
    this.onChooseLetterChangedListener = onChooseLetterChangedListener;
  }
}

HintSideBar通过回调接口来更新居中TextView的文本内容和可见性

使用到的布局

<?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">

  <com.czy.demo.SideBar
    android:id="@+id/sideBar"
    android:layout_width="30dp"
    android:layout_height="match_parent"
    android:layout_alignParentRight="true" />

  <TextView
    android:id="@+id/tv_hint"
    android:layout_width="70dp"
    android:layout_height="70dp"
    android:layout_centerInParent="true"
    android:background="#4b0e0e0e"
    android:gravity="center"
    android:textColor="#ffffff"
    android:textSize="30sp"
    android:visibility="invisible" />

</RelativeLayout>

此时就完成了索引View的绘制,不过定位功能还需要再通过回调接口来完成。

引入jar包后,先来设定一个工具类,包含一个可以解析字符串的方法,返回值为首字符对应的拼音首字母或者为包含一个空格的char类型数据。

public class Utils {

  /**
   * 如果字符串的首字符为汉字,则返回该汉字的拼音大写首字母
   * 如果字符串的首字符为字母,也转化为大写字母返回
   * 其他情况均返回' '
   *
   * @param str 字符串
   * @return 首字母
   */
  public static char getHeadChar(String str) {
    if (str != null && str.trim().length() != 0) {
      char[] strChar = str.toCharArray();
      char headChar = strChar[0];
      //如果是大写字母则直接返回
      if (Character.isUpperCase(headChar)) {
        return headChar;
      } else if (Character.isLowerCase(headChar)) {
        return Character.toUpperCase(headChar);
      }
      // 汉语拼音格式输出类
      HanyuPinyinOutputFormat hanYuPinOutputFormat = new HanyuPinyinOutputFormat();
      hanYuPinOutputFormat.setCaseType(UPPERCASE);
      hanYuPinOutputFormat.setToneType(WITHOUT_TONE);
      if (String.valueOf(headChar).matches("[\\u4E00-\\u9FA5]+")) {
        try {
          String[] stringArray = PinyinHelper.toHanyuPinyinStringArray(headChar, hanYuPinOutputFormat);
          if (stringArray != null && stringArray[0] != null) {
            return stringArray[0].charAt(0);
          }
        } catch (BadHanyuPinyinOutputFormatCombination e) {
          return ' ';
        }
      }
    }
    return ' ';
  }

}

然后再定义一个实体类,包含用户名,电话,用户名首字符的拼音首字母等三个属性
需要实现Comparable 接口,用于排序

public class User implements Comparable {

  private String userName;

  private String phone;

  private char headLetter;

  public User(String userName, String phone) {
    this.userName = userName;
    this.phone = phone;
    headLetter = Utils.getHeadChar(userName);
  }

  public String getUserName() {
    return userName;
  }

  public String getPhone() {
    return phone;
  }

  public char getHeadLetter() {
    return headLetter;
  }

  @Override
  public boolean equals(Object object) {
    if (this == object) {
      return true;
    }
    if (object == null || getClass() != object.getClass()) {
      return false;
    }
    User that = (User) object;
    return getUserName().equals(that.getUserName()) && getPhone().equals(that.getPhone());
  }

  @Override
  public int compareTo(Object object) {
    if (object instanceof User) {
      User that = (User) object;
      if (getHeadLetter() == ' ') {
        if (that.getHeadLetter() == ' ') {
          return 0;
        }
        return -1;
      }
      if (that.getHeadLetter() == ' ') {
        return 1;
      } else if (that.getHeadLetter() > getHeadLetter()) {
        return -1;
      } else if (that.getHeadLetter() == getHeadLetter()) {
        return 0;
      }
      return 1;
    } else {
      throw new ClassCastException();
    }
  }

}

主布局文件如下

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <android.support.v7.widget.RecyclerView
    android:id="@+id/rv_userList"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

  <com.czy.demo.HintSideBar
    android:id="@+id/hintSideBar"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="right" />

</FrameLayout>

联系人列表使用的是RecyclerView,还需要定义一个Adapter

public class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserHolder> {

  private List<User> userList;

  private LayoutInflater inflater;

  public UserAdapter(Context context) {
    inflater = LayoutInflater.from(context);
    userList = new ArrayList<>();
  }

  @Override
  public UserHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = inflater.inflate(R.layout.item_user, parent, false);
    return new UserHolder(view);
  }

  @Override
  public void onBindViewHolder(UserHolder holder, int position) {
    holder.tv_userName.setText(userList.get(position).getUserName());
    holder.tv_phone.setText(userList.get(position).getPhone());
  }

  public void setData(List<User> userList) {
    this.userList.clear();
    this.userList = userList;
  }

  public int getFirstPositionByChar(char sign) {
    if (sign == '#') {
      return 0;
    }
    for (int i = 0; i < userList.size(); i++) {
      if (userList.get(i).getHeadLetter() == sign) {
        return i;
      }
    }
    return -1;
  }

  @Override
  public int getItemCount() {
    return userList.size();
  }

  class UserHolder extends RecyclerView.ViewHolder {

    public TextView tv_userName;

    public TextView tv_phone;

    public UserHolder(View itemView) {
      super(itemView);
      tv_userName = (TextView) itemView.findViewById(R.id.tv_userName);
      tv_phone = (TextView) itemView.findViewById(R.id.tv_phone);
    }
  }

}

以下方法用于获取联系人列表中第一个首字符为sign的item的位置

public int getFirstPositionByChar(char sign)

主Activity代码如下

public class MainActivity extends AppCompatActivity implements SideBar.OnChooseLetterChangedListener {

  private List<User> userList;

  private UserAdapter adapter;

  private RecyclerView rv_userList;

  private LinearLayoutManager manager;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_main);
    HintSideBar hintSideBar = (HintSideBar) findViewById(R.id.hintSideBar);
    rv_userList = (RecyclerView) findViewById(R.id.rv_userList);
    hintSideBar.setOnChooseLetterChangedListener(this);
    manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
    rv_userList.setLayoutManager(manager);
    userList = new ArrayList<>();
    adapter = new UserAdapter(this);
    initData();
    adapter.setData(userList);
    rv_userList.setAdapter(adapter);
  }

  @Override
  public void onChooseLetter(String s) {
    int i = adapter.getFirstPositionByChar(s.charAt(0));
    if (i == -1) {
      return;
    }
    manager.scrollToPositionWithOffset(i, 0);
  }

  @Override
  public void onNoChooseLetter() {

  }
}

initData()用于向Adapter填充数据

public void initData() {
    User user1 = new User("陈", "12345678");
    User user2 = new User("赵", "12345678");
    ...
    userList.add(user1);
    userList.add(user2);
    ...
    Collections.sort(userList);
    adapter.notifyDataSetChanged();
  }

这样,整个效果就都完成了。

这里提供代码下载:Android 仿微信通讯录列表侧边栏

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义View实现通讯录字母索引(仿微信通讯录)

    一.效果:我们看到很多软件的通讯录在右侧都有一个字母索引功能,像微信,小米通讯录,QQ,还有美团选择地区等等.这里我截了一张美团选择城市的图片来看看: 我们今天就来实现图片中右侧模块的索引功能,包括触摸显示以选中的索引字母.这里我的UI界面主要是参照微信的界面来实现,所以各位也可以对照微信来看看效果,什么都不说了,只有效果图最具有说服力! 二.分析: 我们看到这样的效果我们心理都回去琢磨,他是如何实现的: 首先,它肯定是通过自定义 View 来实现的,因为 Android 没有提供类似这样的控件

  • Android利用Intent读取和更新通讯录

    一.简介 本节演示如何在安卓系统中通过用户配置文件(user profile)读取和更新该手机的所有联系人信息,以及如何导航到用户配置文件中的这些联系人. 二.基本概念  1.什么是 User Profile 用户配置文件(user profile)保存的是机主信息以及该手机中所有联系人的信息. 假定手机所有者的名字为"Mao mao yu",那么,user profile保存的就是"Mao mao yu"的通讯录(即机主所有联系人的姓名.电话.邮箱.--等信息).

  • 使用adb命令向Android模拟器中导入通讯录联系人的方法

    本文实例讲述了使用adb命令向Android模拟器中导入通讯录联系人的方法.分享给大家供大家参考.具体实现方法如下: 使用adb提供的命令, 可以非常方便地从PC中将通讯录导入android模拟器中. 首先要先准备好固定格式的contacts.vcf文件, 该文件即android中的通讯录存储文件. 格式如下: 复制代码 代码如下: BEGIN:VCARD  VERSION:3.0  N:15200000000;;;;  TEL;TYPE=cell:15200000000  END:VCARD 

  • Android开发之自定义view实现通讯录列表A~Z字母提示效果【附demo源码下载】

    本文实例讲述了Android开发之自定义view实现通讯录列表A~Z字母提示效果.分享给大家供大家参考,具体如下: 开发工具:eclipse 运行环境:htc G9 android2.3.3 话不多说,先看效果图 其实左右边的A~Z是一个自定义的View,它直接覆盖在ListView上. MyLetterListView: public class MyLetterListView extends View { OnTouchingLetterChangedListener onTouching

  • Android手机联系人快速索引(手机通讯录)

    最近需要实现一个手机通讯录的快速索引功能.根据姓名首字母快速索引功能.下面是一个手机联系人快速索引的效果,总体来说代码不算难,拼音转换的地方略有复杂.下面上源码:源码中有注释. 下面是效果图: MainActivity: import java.util.ArrayList; import java.util.Collections; import java.util.List; import android.app.Activity; import android.os.Bundle; imp

  • Android实现通讯录效果——获取手机号码和姓名

    首先给大家展示下运行效果图: 由于通讯录在手机里是以数据库贮存的 所以我们可以通过一个方法 context.getContentResolver().query(Phone.CONTENT_URI, null, null, null, null); 来获得通讯录 ,这个方法返回一个游标的数据类型,通过moveToNext()方法来获取所有的手机号码信息 当然读取手机通讯录需要权限 在adnroidManifest文件中声明即可 由于我也实现了打电话的功能 所以也要声明权限 <uses-permi

  • Android编程实现通讯录中联系人的读取,查询,添加功能示例

    本文实例讲述了Android编程实现通讯录中联系人的读取,查询,添加功能.分享给大家供大家参考,具体如下: 先加二个读和写权限: <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> 具体代码: package com.ebo

  • Android仿微信通讯录滑动快速定位功能

    先给大家展示下效果图: 实现代码如下: 下面简单说下实现原理. public class IndexBar extends LinearLayout implements View.OnTouchListener { private static final String[] INDEXES = new String[]{"#", "A", "B", "C", "D", "E", &qu

  • Android通讯录开发之删除功能的实现方法

    无论是Android开发或者是其他移动平台的开发,ListView肯定是一个大咖,那么对ListView的操作肯定是不会少的,上一篇博客介绍了如何实现全选和反选的功能,本篇博客介绍删除功能,删除列表中的项无谓就是及时刷新列表,这又跟UI线程扯上关系了,还是那句话,数据的更新通知一定要在UI线程上做,不然会出现各种错误,比如出现adapter数据源改变,但没有及时收到通知的情况.在执行遍历删除的时候,最好不要每删一个就直接通知,下面是我的实现方法,将需要删除的contact保存到一个List然后通

  • Android中使用Expandablelistview实现微信通讯录界面

    之前的博文<Android 中使用ExpandableListView 实现分组的实例>我简单介绍了使用ExpandableListView实现简单的好友分组功能,今天我们针对之前的所做的仿微信APP来对ExpandableListView做一个扩展介绍,实现效果如下(通讯里使用ExpandableListView实现): 相关知识点博文链接: Android 中使用ExpandableListView 实现分组的实例 详解Android中fragment和viewpager的那点事儿 详解A

  • Android使用ContentResolver搜索手机通讯录的方法

    本文实例讲述了Android使用ContentResolver搜索手机通讯录的方法.分享给大家供大家参考,具体如下: 在这个程序中使用ContentResolver来访问通讯录里联系人的关键字,并将所有找到的联系人存入CursorAdapter里.输入搜索人员名字a ,会将所有以a开头的名字都显示出来,输入*,则所有通讯录中的人名显示于AutoCompleteView的AdapterView里,若发生了User选择事件后,会将勾选的联系人电话号码显示于TextView里. 此程序中用到了Cont

随机推荐