欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义View实现字母导航栏

程序员文章站 2024-03-05 08:07:42
很多的android入门程序猿来说对于android自定义view,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义view上面花一些功夫,多写一些文章。...

很多的android入门程序猿来说对于android自定义view,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义view上面花一些功夫,多写一些文章。

思路分析:

1、自定义view实现字母导航栏

2、listview实现联系人列表

3、字母导航栏滑动事件处理

4、字母导航栏与中间字母的联动

5、字母导航栏与listview的联动

效果图:

Android自定义View实现字母导航栏

首先,我们先甩出主布局文件,方便后面代码的说明

<!--?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);
  }
 }
});

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