android自定义sidebar,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的文本内容和可见性
使用到的布局
android:layout_width="match_parent"
android:layout_height="match_parent">
android:id="@+id/sideBar"
android:layout_width="30dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true" />
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" />
此时就完成了索引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();
}
}
}
主布局文件如下
android:layout_width="match_parent"
android:layout_height="match_parent">
android:id="@+id/rv_userList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:id="@+id/hintSideBar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="right" />
联系人列表使用的是RecyclerView,还需要定义一个Adapter
public class UserAdapter extends RecyclerView.Adapter {
private List 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 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 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自定义sidebar,Android仿微信通讯录列表侧边栏效果相关推荐
- android 字母索引三方,Android ListView字母索引(仿微信通讯录列表)
布局代码 xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_paren ...
- 【uniapp前端组件】仿微信通讯录列表组件
仿微信通讯录列表组件 示例图 前言 仿微信通讯录列表组件,可实现通讯列表以及选择多个联系人功能. 组件介绍 本组件有三个自定义组件构成,都已经集成在bugking7-contact-list中,该组件 ...
- android bmob 朋友圈,仿微信朋友圈视频效果 – MVideo
MVideo 仿微信朋友圈视频效果,可以拖拽及缩放,视频查看,基于ijkplayer. Demo 入门 Step 1:在buil文件中添加JitPack仓库: allprojects { reposi ...
- Android自定义ListView实现仿微信侧滑删除
经常在遇到问题第一时间都会在网上搜索解决的方法,因此看到很多前辈们的比较精辟的技术文章,学习了很多东西,现在将自己平时工作中开发的一些小功能坐下总结,也写出来,既方便自己理清思路记忆功能块实现思路,又 ...
- Android自定义View分享——仿微信朋友圈图片合并效果
写在前面 笔者近来在学习Android自定义View,收集了一些不算复杂但又"长得"还可以的自定义View效果实现,之前分享过两个效果:一个水平的进度条,一个圆形温度显示器,如果你 ...
- Android使用RecyclerView实现仿微信联系人列表
现在联系人列表基本都是按照字母或者拼音来进行分类,右边有一排字母供用户快速定位到指定的字母位置,效果图如下: OK,输入的联系人类型可能有很多种,比如汉字.英文.数字.特殊符号等等,其中汉字会转化成拼 ...
- Android 自定义Switch,仿微信开关键Switch
switch (changeFragmentEvent.getViewId()) {//启动设备(安卓接收)case 1:textView.setText("vvvvv555"+c ...
- Android 自定义seekbar【仿微信联系人】
/** * * 转载请标明出处:http://blog.csdn.net/u013598111/article/details/50452578 * @author:[JunTao_sun] ...
- RN仿微信通讯录列表
源码在此 先看一下预览图效果: pic1.jpg 首先通过构造器初始化state constructor(props) {super(props);this.state = {//Global这里是全 ...
- Android自定义View实现仿QQ实现运动步数效果
效果图: 1.attrs.xml中 <declare-styleable name="QQStepView"><attr name="outerColo ...
最新文章
- 导入语句 python_Python导入语句说明
- 不装客户端连接mysql_C#不安装oracle客户端,如何连接到oracle数据库
- 重磅!阿里开源自研语音识别模型DFSMN,准确率高达96.04%
- Django购物网站使用说明
- kafka生产者开发方式
- Mask Network与Conflux发起联合Bounty
- java 整数加减乘除_java实现超大整数加减乘除四则运算
- 分析一个在高并发下的财务支付锁的问题
- tomcat配置gc日志输出
- 一键进入高通9008模式_想闯中国智能汽车的高通和想花200亿的威马
- 使用APICloud AVM多端框架开发app通讯录功能
- unity 视频录制总结
- 课后实践9:以拼多多为例,原型设计
- 国产软件不背黑锅,4款强大又实用的电脑软件,用了舍不得卸载
- 汽车轮毂识别项目介绍
- 网络设备和常见网络拓扑
- VBA使用FileSystemObject将读取或写入文本文件
- 排序算法-冒泡排序的时间复杂度分析
- 10 款趋势策略的比较分析
- Linux设备驱动开发(5.4.58)-3-NEWCHR
热门文章
- 低通滤波与RC振荡产生正弦波
- springboot集成webservice接口
- PCB Layout的10个细节
- 有多少个斐波那契子数列(微软笔试题)
- comsol 学习笔记【基础知识,磁场与结构场耦合为主】
- 计算机的生产过程,笔记本电脑生产流程介绍 .pptx
- MFC 通用对话框之“查找替换“对话框
- [转]WebQQ登录过程分析
- python文件粉碎传输_如何自己实现文件粉碎机?
- MindMeld中文文档--2.构建会话应用程序的不同方法[Different Approaches for Building Conversational Applications]