需要一个依赖,导入把中文转为拼音的功能

implementation 'com.belerweb:pinyin4j:2.5.1'

在values创建一个attrs

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="SideBar"><attr name="scaleSize" format="integer"/><attr name="scaleItemCount" format="integer"/><attr name="scaleWidth" format="dimension"/></declare-styleable>
</resources>

两个工具类,直接复制粘贴就行

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;/*** 汉字转换位汉语拼音,英文字符不变*/
public class Cn2Spell {public static StringBuffer sb = new StringBuffer();/*** 获取汉字字符串的首字母,英文字符不变* 例如:阿飞→af*/public static String getPinYinHeadChar(String chines) {sb.setLength(0);char[] chars = chines.toCharArray();HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);for (int i = 0; i < chars.length; i++) {if (chars[i] > 128) {try {sb.append(PinyinHelper.toHanyuPinyinStringArray(chars[i], defaultFormat)[0].charAt(0));} catch (Exception e) {e.printStackTrace();}} else {sb.append(chars[i]);}}return sb.toString();}/*** 获取汉字字符串的第一个字母*/public static String getPinYinFirstLetter(String str) {sb.setLength(0);char c = str.charAt(0);String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c);if (pinyinArray != null) {sb.append(pinyinArray[0].charAt(0));} else {sb.append(c);}return sb.toString();}/*** 获取汉字字符串的汉语拼音,英文字符不变*/public static String getPinYin(String chines) {sb.setLength(0);char[] nameChar = chines.toCharArray();HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);for (int i = 0; i < nameChar.length; i++) {if (nameChar[i] > 128) {try {sb.append(PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0]);} catch (Exception e) {e.printStackTrace();}} else {sb.append(nameChar[i]);}}return sb.toString();}}
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.TextView;import com.example.wechat.R;@SuppressLint("AppCompatCustomView")
public class SideBar extends TextView {private String[] letters = new String[]{"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 textPaint;private Paint bigTextPaint;private Paint scaleTextPaint;private Canvas canvas;private int itemH;private int w;private int h;/*** 普通情况下字体大小*/float singleTextH;/*** 缩放离原始的宽度*/private float scaleWidth;/*** 滑动的Y*/private float eventY = 0;/*** 缩放的倍数*/private int scaleSize = 1;/*** 缩放个数item,即开口大小*/private int scaleItemCount = 6;private ISideBarSelectCallBack callBack;public SideBar(Context context) {this(context, null);}public SideBar(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SideBar(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(attrs);}private void init(AttributeSet attrs) {if (attrs != null) {TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SideBar);scaleSize = ta.getInteger(R.styleable.SideBar_scaleSize, 1);scaleItemCount = ta.getInteger(R.styleable.SideBar_scaleItemCount, 6);scaleWidth = ta.getDimensionPixelSize(R.styleable.SideBar_scaleWidth, dp(100));ta.recycle();}textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);textPaint.setColor(getCurrentTextColor());textPaint.setTextSize(getTextSize());textPaint.setTextAlign(Paint.Align.CENTER);bigTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);bigTextPaint.setColor(getCurrentTextColor());bigTextPaint.setTextSize(getTextSize() * (scaleSize + 3));bigTextPaint.setTextAlign(Paint.Align.CENTER);scaleTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);scaleTextPaint.setColor(getCurrentTextColor());scaleTextPaint.setTextSize(getTextSize() * (scaleSize + 1));scaleTextPaint.setTextAlign(Paint.Align.CENTER);}public void setDataResource(String[] data) {letters = data;invalidate();}public void setOnStrSelectCallBack(ISideBarSelectCallBack callBack) {this.callBack = callBack;}/*** 设置字体缩放比例** @param scale*/public void setScaleSize(int scale) {scaleSize = scale;invalidate();}/*** 设置缩放字体的个数,即开口大小** @param scaleItemCount*/public void setScaleItemCount(int scaleItemCount) {this.scaleItemCount = scaleItemCount;invalidate();}private int dp(int px) {final float scale = getContext().getResources().getDisplayMetrics().density;return (int) (px * scale + 0.5f);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_MOVE:if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {eventY = event.getY();invalidate();return true;} else {eventY = 0;invalidate();break;}case MotionEvent.ACTION_CANCEL:eventY = 0;invalidate();return true;case MotionEvent.ACTION_UP:if (event.getX() > (w - getPaddingRight() - singleTextH - 10)) {eventY = 0;invalidate();return true;} elsebreak;}return super.onTouchEvent(event);}@Overrideprotected void onDraw(Canvas canvas) {this.canvas = canvas;DrawView(eventY);}private void DrawView(float y) {int currentSelectIndex = -1;if (y != 0) {for (int i = 0; i < letters.length; i++) {float currentItemY = itemH * i;float nextItemY = itemH * (i + 1);if (y >= currentItemY && y < nextItemY) {currentSelectIndex = i;if (callBack != null) {callBack.onSelectStr(currentSelectIndex, letters[i]);}//画大的字母Paint.FontMetrics fontMetrics = bigTextPaint.getFontMetrics();float bigTextSize = fontMetrics.descent - fontMetrics.ascent;canvas.drawText(letters[i], w - getPaddingRight() - scaleWidth - bigTextSize, singleTextH + itemH * i, bigTextPaint);}}}drawLetters(y, currentSelectIndex);}private void drawLetters(float y, int index) {//第一次进来没有缩放情况,默认画原图if (index == -1) {w = getMeasuredWidth();h = getMeasuredHeight();itemH = h / letters.length;Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();singleTextH = fontMetrics.descent - fontMetrics.ascent;for (int i = 0; i < letters.length; i++) {canvas.drawText(letters[i], w - getPaddingRight(), singleTextH + itemH * i, textPaint);}//触摸的时候画缩放图} else {//遍历所有字母for (int i = 0; i < letters.length; i++) {//要画的字母的起始Y坐标float currentItemToDrawY = singleTextH + itemH * i;float centerItemToDrawY;if (index < i)centerItemToDrawY = singleTextH + itemH * (index + scaleItemCount);elsecenterItemToDrawY = singleTextH + itemH * (index - scaleItemCount);float delta = 1 - Math.abs((y - currentItemToDrawY) / (centerItemToDrawY - currentItemToDrawY));float maxRightX = w - getPaddingRight();//如果大于0,表明在y坐标上方scaleTextPaint.setTextSize(getTextSize() + getTextSize() * delta);float drawX = maxRightX - scaleWidth * delta;//超出边界直接花在边界上if (drawX > maxRightX)canvas.drawText(letters[i], maxRightX, singleTextH + itemH * i, textPaint);elsecanvas.drawText(letters[i], drawX, singleTextH + itemH * i, scaleTextPaint);}}}public interface ISideBarSelectCallBack {void onSelectStr(int index, String selectStr);}}

前期工作做好了,然后界面

<?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"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/userlist"android:layout_width="match_parent"android:layout_height="match_parent" /><com.example.wechat.util.SideBarandroid:id="@+id/sidebar"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_alignParentRight="true"android:paddingRight="10dp"android:textColor="@color/colorGreen"android:textSize="15sp" /></RelativeLayout>

item,这个根布局需要用相对布局,线性布局老是有bug,我也不知道原因,反正换成相对布局就正常显示了

<?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"><LinearLayoutandroid:id="@+id/letterlayout"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/colorLightGray"><TextViewandroid:id="@+id/letter"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="6dp"android:text="A"android:textColor="#646262"android:textSize="20sp" /></LinearLayout><LinearLayoutandroid:id="@+id/layout"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_below="@id/letterlayout"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><com.example.wechat.util.RoundImageViewandroid:id="@+id/header"android:layout_width="55dp"android:layout_height="55dp"android:layout_margin="5dp"android:src="@drawable/head_test" /><TextViewandroid:id="@+id/name"android:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center_vertical"android:padding="10dp"android:text="@string/app_name"android:textColor="@color/colorBlack"android:textSize="20sp" /></LinearLayout><Viewandroid:id="@+id/underline"android:layout_width="match_parent"android:layout_height="0.5dp"android:layout_below="@id/name"android:layout_marginLeft="60dp"android:background="@color/colorLightGray"/></LinearLayout></RelativeLayout>

User在这个功能里用到的属性只有name,headerid,pinyin和start,两个接口一个是用来传递消息,一个用来排序

package com.example.wechat.bean;import com.example.wechat.util.Cn2Spell;import java.io.Serializable;
import java.util.Date;public class User implements Serializable, Comparable<User> {String name;    //名字String content; //内容String time; //日期int headerid; //头像String pinyin;  //中文转换为拼音String start; //首字母public User(String name, String content, String time, int headerid) {this.name = name;this.content = content;this.time = time;this.headerid = headerid;}public User(String name,int headerid) {this.headerid = headerid;this.name = name;pinyin = Cn2Spell.getPinYin(name);start = pinyin.substring(0, 1).toUpperCase();if (!start.matches("[A-Z]")) {start = "#";}}public void setPinyin(String pinyin) {this.pinyin = pinyin;}public void setStart(String start) {this.start = start;}public String getPinyin() {return pinyin;}public String getStart() {return start;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String getTime() {return time;}public void setTime(String time) {this.time = time;}public int getHeaderid() {return headerid;}public void setHeaderid(int headerid) {this.headerid = headerid;}@Overridepublic int compareTo(User user) {if (start.equals("#") && !user.getStart().equals("#")) {return 1;} else if (!start.equals("#") && user.getStart().equals("#")) {return -1;} else {return pinyin.compareToIgnoreCase(user.getPinyin());}}
}

Adapter,里面有一点逻辑判断什么时候显示字母的View,什么时候显示下划线,很简单的逻辑一看就懂

package com.example.wechat.adapter;import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;import com.example.wechat.R;
import com.example.wechat.activity.ItemDetail;
import com.example.wechat.bean.User;
import com.example.wechat.util.RoundImageView;import java.util.ArrayList;
import java.util.List;import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;public class FriendAdapter extends RecyclerView.Adapter<FriendAdapter.ViewHolder> {List<User> users = new ArrayList<>();Context context;LayoutInflater inflater;public FriendAdapter(List<User> users, Context context) {this.users = users;this.context = context;inflater = LayoutInflater.from(context);}@NonNull@Overridepublic FriendAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = inflater.inflate(R.layout.friend_item, null);ViewHolder viewHolder = new ViewHolder(view);return viewHolder;}@Overridepublic void onBindViewHolder(@NonNull FriendAdapter.ViewHolder holder, final int position) {final User user = users.get(position);holder.name.setText(user.getName());holder.header.setImageResource(user.getHeaderid());holder.view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(context, ItemDetail.class);intent.putExtra("user_data",user);context.startActivity(intent);}});String mark = user.getStart();if (position == getPosition(mark)){holder.letterlayout.setVisibility(View.VISIBLE);holder.letter.setText(user.getStart());}else {holder.letterlayout.setVisibility(View.GONE);}if (position!=getItemCount()-1&&user.getStart().equalsIgnoreCase(users.get(position+1).getStart())){holder.underline.setVisibility(View.VISIBLE);}else {holder.underline.setVisibility(View.GONE);}}private int getPosition(String mark) {for (int i=0;i<getItemCount();i++){if (users.get(i).getStart().equalsIgnoreCase(mark)){return i;}}return -1;}@Overridepublic int getItemCount() {return users.size();}public class ViewHolder extends RecyclerView.ViewHolder {LinearLayout letterlayout;TextView letter;TextView name;RoundImageView header;View view;View underline;public ViewHolder(@NonNull View itemView) {super(itemView);view = itemView;underline = itemView.findViewById(R.id.underline);letterlayout = itemView.findViewById(R.id.letterlayout);letter = itemView.findViewById(R.id.letter);name = itemView.findViewById(R.id.name);header = itemView.findViewById(R.id.header);}}
}

Fragment

package com.example.wechat.fragment;import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;import com.example.wechat.R;
import com.example.wechat.adapter.FriendAdapter;
import com.example.wechat.bean.User;
import com.example.wechat.util.SideBar;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;public class FriendFragment extends Fragment {RecyclerView userlist;SideBar sideBar;FriendAdapter friendAdapter;List<User> users = new ArrayList<>();@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {View view = inflater.inflate(R.layout.friendfragment, container, false);userlist = view.findViewById(R.id.userlist);sideBar = view.findViewById(R.id.sidebar);users.clear();initData();initView();return view;}private void initView() {LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());userlist.setLayoutManager(linearLayoutManager);friendAdapter = new FriendAdapter(users, getContext());userlist.setAdapter(friendAdapter);sideBar.setOnStrSelectCallBack(new SideBar.ISideBarSelectCallBack() {@Overridepublic void onSelectStr(int index, String selectStr) {for (int i = 0; i < users.size(); i++) {if (selectStr.equalsIgnoreCase(users.get(i).getStart())){userlist.scrollToPosition(i);return;}}}});}private void initData() {users.add(new User("丽丽",R.drawable.head_test));users.add(new User("妈妈",R.drawable.mama));users.add(new User("爸爸",R.drawable.baba));users.add(new User("#xx",R.drawable.head_test));users.add(new User("虹猫",R.drawable.head_test));users.add(new User("蓝兔",R.drawable.head_test));users.add(new User("阿牛",R.drawable.head_test));users.add(new User("CK",R.drawable.head_test));users.add(new User("灯虎",R.drawable.head_test));users.add(new User("尔康",R.drawable.head_test));users.add(new User("凡哥",R.drawable.head_test));users.add(new User("Gr",R.drawable.head_test));users.add(new User("123阿斯顿",R.drawable.head_test));Collections.sort(users);}
}

安卓模仿微信通讯录--RecyclerView+SideBar相关推荐

  1. 自定义实现微信通讯录效果View

    欢迎访问我的个人独立博客 ittiger.cn,原创文章,未经允许不得随意转载. 前言 在使用App过程中,经常会有使用到联系人或城市列表的场景,其实这两种效果是一样的,都是右边有个索引列表,点击索引 ...

  2. 安卓仿微信界面,导航,右上角菜单栏

    下面是安卓开发仿微信界面的代码. 分为3步,第一步是界面的编写,第二步是导航界面,第三步是右上角菜单栏. 开始第一步前先预览一下效果. 第一步,界面. 界面的思路是利用ViewPager+Fragme ...

  3. 【Android 仿微信通讯录 导航分组列表-上】使用ItemDecoration为RecyclerView打造带悬停头部的分组列表

    *本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 转载请标明出处: http://blog.csdn.net/zxt0601/article/details/52355199 本文 ...

  4. RecyclerView+index索引实现仿微信通讯录

    感觉之前写的有点乱,所以有重新整理了一下这个博客: demo下载地址:http://download.csdn.net/detail/qq_34501274/9799175 最近跟朋友聊天,说道博客相 ...

  5. 安卓应用开发 -- 柚信 -- “模仿微信即时通讯软件“

    1. 设计内容及要求 1.1. 综述 1.1.1. 系统概述 我们要做的就是类似微信这样的面向大众的聊天软件,基本功能和微信类似.首先,系统分为两大部分,第一部分是客户端,是用户使用的部分,第二部分是 ...

  6. 安卓:ListView组件实现微信通讯录效果(我的王者队友们)

    ListView控件是列表视图展示,排列方式是纵向. ListView组件实现微信通讯录效果,包含头像和文字,不能用entries这个属性来添加,要用SimpleAdapter适配器来添加数据 布局: ...

  7. android 微信 备份通讯录,微信通讯录备份在哪里?安卓手机的备份方法介绍

    由于科技的发展,只能手机也在不断的更新换代,一个品牌每年都会推出好几款机型,所以用户在使用这些科技产品的时候更新换代的速度也是比较快的,在更新的过程中难免会丢失一些数据,所以大家对于手机内的一些信息操 ...

  8. Android微信通讯录界面代码,Android中使用Expandablelistview实现微信通讯录界面

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

  9. 通过SectionIndexer实现微信通讯录

    这里主要参考了使用SectionIndexer实现微信通讯录的效果 在这里做个记录 效果图 页面使用RelativeLayout,主要分为三个部分,match_parent的主listView,右边字 ...

最新文章

  1. 【C#】类——里式转换
  2. Win7+Ubuntu11
  3. KVM — 与 QEMU 和 Libvirt 的关系
  4. Apache开启Gzip压缩,LAMP网页压缩
  5. div 不受父级标签影响_前端H5开发中常用的标签
  6. 再讲IQueryablelt;Tgt;,揭开表达式树的神秘面纱
  7. 【转】Objective-C 属性特性(assign , retain , copy , readonly , readwrite , atomic , nonatomic)...
  8. SQLyog 下载地址
  9. 【业务安全01】业务安全基础及测试流程
  10. oracle odac安装图解,Oracle数据访问组件ODAC的安装方法
  11. 大西瓜支付宝/QQ/微信收款码三合一源码+实测可用
  12. python-txt转换为pdf
  13. Eclipse中Debug调试
  14. [译]Hierarchical Macro Strategy Model for MOBA Game AI(王者荣耀)--翻译
  15. 队列练习之Example004-设计一个循环队列,用 front 和 rear 分别作为队头和队尾指针,另外用一个标志 tag 表示队列是空还是不空
  16. AI初学者必看的4个顶级人工智能领域岗位
  17. c语言常数-ox6a是什么意思,C语言第2讲-数据类型运算符和表达式.pdf
  18. 河北钢铁的数字化雄心
  19. Hyper-V共享主机磁盘 2021-12-10
  20. 怎么用C++编个网站

热门文章

  1. 广东未来科技荣膺2021粤港澳大湾区新经济企业100强
  2. 墨菲定律和吉德林法则
  3. 移动增值业务新人入职培训
  4. 声网首发RTE行业专著《实时万象》,拉开RTE2022序幕
  5. DTOJ#3230. 好题 (hao)
  6. [生而为人-思考] Knowledge Cooking -3rd 分享会记录
  7. 公司KPI考核代码行数,程序员神操作:10行变500行!
  8. crm自动编号(第二天再次001从开始)
  9. VC++6.0安装成功后如何启动应用或者如何创建快捷方式
  10. 物联网考计算机是跨专业,2014考研计算机等专业 加入物联网技术方向_跨考网