Android仿微信通讯录列表侧边栏效果
程序员文章站
2023-12-13 20:53:28
先看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 仿微信通讯录列表侧边栏
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。