Android自定义View实现字母导航栏
很多的android入门程序猿来说对于android自定义view,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义view上面花一些功夫,多写一些文章。
思路分析:
1、自定义view实现字母导航栏
2、listview实现联系人列表
3、字母导航栏滑动事件处理
4、字母导航栏与中间字母的联动
5、字母导航栏与listview的联动
效果图:
首先,我们先甩出主布局文件,方便后面代码的说明
<!--?xml version="1.0" encoding="utf-8"?--> <linearlayout android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> <edittext android:background="@drawable/search_border" android:drawableleft="@android:drawable/ic_menu_search" android:layout_height="wrap_content" android:layout_width="match_parent" android:padding="8dp"> <relativelayout android:layout_height="match_parent" android:layout_width="match_parent"> <listview android:divider="@null" android:id="@+id/lv" android:layout_height="match_parent" android:layout_width="match_parent"> <textview android:background="#888888" android:gravity="center" android:id="@+id/tv" android:layout_centerinparent="true" android:layout_height="50dp" android:layout_width="50dp" android:textcolor="#000000" android:textsize="18dp" android:visibility="gone"> <com.handsome.tulin.view.navview android:id="@+id/nv" android:layout_alignparentright="true" android:layout_height="match_parent" android:layout_margin="16dp" android:layout_width="20dp"> </com.handsome.tulin.view.navview></textview></listview></relativelayout> </edittext></linearlayout>
步骤一:分析自定义字母导航栏
思路分析:
1、我们在使用的时候把宽设置为20dp,高设置为填充父控件,所以这里获取的宽度为20dp
2、通过循环,画出竖直的字母,每画一次得重新设置一下颜色,因为我们需要一个选中的字母颜色和默认不一样
public class navview extends view { private paint textpaint = new paint(); private string[] s = 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 int choose = -1; //中间的文本 private textview tv; public navview(context context, attributeset attrs) { super(context, attrs); } public navview(context context) { super(context); } public navview(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); } private void initpaint() { textpaint.settextsize(20); textpaint.setantialias(true); textpaint.setcolor(color.black); } @override protected void ondraw(canvas canvas) { super.ondraw(canvas); //画字母 drawtext(canvas); } /** * 画字母 * * @param canvas */ private void drawtext(canvas canvas) { //获取view的宽高 int width = getwidth(); int height = getheight(); //获取每个字母的高度 int singleheight = height / s.length; //画字母 for (int i = 0; i < s.length; i++) { //画笔默认颜色 initpaint(); //高亮字母颜色 if (choose == i) { textpaint.setcolor(color.red); } //计算每个字母的坐标 float x = (width - textpaint.measuretext(s[i])) / 2; float y = (i + 1) * singleheight; canvas.drawtext(s[i], x, y, textpaint); //重置颜色 textpaint.reset(); } } }
步骤二:listview实现联系人列表
思路分析:
1、在主activity中,定义一个数据数组,使用工具类获取数组的第一个字母,使用collections根据第一个字母进行排序,由于工具类有点长,就不贴出来了。
2、创建一个listview子布局,创建一个adapter进行填充。
主布局:
public class mainactivity extends appcompatactivity { private textview tv; private listview lv; private navview nv; private list<user> list; private useradapter adapter; private string[] name = new string[]{ "潘粤明", "戴军", "薛之谦", "蓝雨", "任泉", "张杰", "秦俊杰", "陈坤", "田亮", "夏雨", "保剑锋", "陆毅", "乔振宇", "吉杰", "郭敬明", "巫迪文", "欢子", "井柏然", "左小祖咒", "段奕宏", "毛宁", "樊凡", "汤潮", "山野", "陈龙", "侯勇", "俞思远", "冯绍峰", "崔健", "杜淳", "张翰", "彭坦", "柏栩栩", "蒲巴甲", "凌潇肃", "毛方圆", "武艺", "耿乐", "钱泳辰"}; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initview(); initdata(); } private void initview() { tv = (textview) findviewbyid(r.id.tv); lv = (listview) findviewbyid(r.id.lv); nv = (navview) findviewbyid(r.id.nv); nv.settextview(tv); } private void initdata() { //初始化数据 list = new arraylist<>(); for (int i = 0; i < name.length; i++) { list.add(new user(name[i], characterutils.getfirstspell(name[i]).touppercase())); } //将拼音排序 collections.sort(list, new comparator<user>() { @override public int compare(user lhs, user rhs) { return lhs.getfirstcharacter().compareto(rhs.getfirstcharacter()); } }); //填充listview adapter = new useradapter(this, list); lv.setadapter(adapter); } }</user></user>
listview子布局:
<!--?xml version="1.0" encoding="utf-8"?--> <linearlayout android:background="#ffffff" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> <textview android:background="#dbdbda" android:id="@+id/tv_firstcharacter" android:layout_height="wrap_content" android:layout_width="match_parent" android:padding="8dp" android:text="a" android:textcolor="#000000" android:textsize="14dp"> <textview android:background="#ffffff" android:id="@+id/tv_name" android:layout_height="wrap_content" android:layout_width="match_parent" android:padding="8dp" android:text="张栋梁" android:textcolor="#2196f3" android:textsize="14dp"> </textview></textview></linearlayout>
adapter:
public class useradapter extends baseadapter { private list<user> list; private user user; private layoutinflater minflater; private context context; public useradapter(context context, list<user> list) { this.list = list; minflater = layoutinflater.from(context); this.context = context; } @override public int getcount() { return list.size(); } @override public object getitem(int position) { return list.get(position); } @override public long getitemid(int position) { return position; } @override public view getview(int position, view convertview, viewgroup parent) { if (convertview == null) { convertview = minflater.inflate(r.layout.adapter_user, null); } viewholder holder = getviewholder(convertview); user = list.get(position); if (position == 0) { //第一个数据要显示字母和姓名 holder.tv_firstcharacter.setvisibility(view.visible); holder.tv_firstcharacter.settext(user.getfirstcharacter()); holder.tv_name.settext(user.getusername()); } else { //其他数据判断是否为同个字母,这里使用ascii码比较大小 if (characterutils.getcnascii(list.get(position - 1).getfirstcharacter().charat(0)) < characterutils.getcnascii(user.getfirstcharacter().charat(0))) { //后面字母的值大于前面字母的值,需要显示字母 holder.tv_firstcharacter.setvisibility(view.visible); holder.tv_firstcharacter.settext(user.getfirstcharacter()); holder.tv_name.settext(user.getusername()); } else { //后面字母的值等于前面字母的值,不显示字母 holder.tv_firstcharacter.setvisibility(view.gone); holder.tv_name.settext(user.getusername()); } } return convertview; } /** * 获得控件管理对象 * * @param view * @return */ private viewholder getviewholder(view view) { viewholder holder = (viewholder) view.gettag(); if (holder == null) { holder = new viewholder(view); view.settag(holder); } return holder; } /** * 控件管理类 */ private class viewholder { private textview tv_firstcharacter, tv_name; viewholder(view view) { tv_firstcharacter = (textview) view.findviewbyid(r.id.tv_firstcharacter); tv_name = (textview) view.findviewbyid(r.id.tv_name); } } /** * 通过字符查找位置 * * @param s * @return */ public int getselectposition(string s) { for (int i = 0; i < getcount(); i++) { string firchar = list.get(i).getfirstcharacter(); if (firchar.equals(s)) { return i; } } return -1; } }</user></user>
步骤三:字母导航栏滑动事件处理、字母导航栏与中间字母的联动
思路分析:
1、在自定义view中重写dispatchtouchevent处理滑动事件,最后返回true。
2、在主activity传进来一个textview,在我们滑动的时候设置text,松开的时候消失text。设置text的时候需要计算text的位置,并且滑过多的话会出现数组越界的问题,所以我们在里面处理数组越界问题。
3、最后,提供一个接口,记录我们滑到的字母,为了后面可以和listview联动。
@override public boolean dispatchtouchevent(motionevent event) { //计算选中字母 int index = (int) (event.gety() / getheight() * s.length); //防止脚标越界 if (index >= s.length) { index = s.length - 1; } else if (index < 0) { index = 0; } switch (event.getaction()) { case motionevent.action_down: case motionevent.action_move: setbackgroundcolor(color.gray); //选中字母高亮 choose = index; //出现中间文字 tv.setvisibility(visible); tv.settext(s[choose]); //调用listview连动接口 if (listener != null) { listener.touchcharacterlistener(s[choose]); } //重绘 invalidate(); break; default: setbackgroundcolor(color.transparent); //取消选中字母高亮 choose = -1; //隐藏中间文字 tv.setvisibility(gone); //重绘 invalidate(); break; } return true; } public ontouchcharacterlistener listener; public interface ontouchcharacterlistener { void touchcharacterlistener(string s); } public void setlistener(ontouchcharacterlistener listener) { this.listener = listener; } /** * 传进来一个textview * * @param tv */ public void settextview(textview tv) { this.tv = tv; }
步骤四:字母导航栏和listview的联动
思路分析:
1、我们已经通过接口传递过去了一个选择的字母,和在adapter写好了根据字母查询position的方法,这个时候只要主activity对自定义view设置监听,判断即可。
//listview连动接口 nv.setlistener(new navview.ontouchcharacterlistener() { @override public void touchcharacterlistener(string s) { int position = adapter.getselectposition(s); if (position != -1) { lv.setselection(position); } } });
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。