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

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

(0)

相关推荐

  • Android仿网易客户端顶部导航栏效果

    最近刚写了一个网易客户端首页导航条的动画效果,现在分享出来给大家学习学习.我说一下这个效果的核心原理.下面是效果图: 首先是布局,这个布局是我从网易客户端反编译后弄来的.大家看后应该明白,布局文件如下: <FrameLayout android:id="@id/column_navi" android:layout_width="fill_parent" android:layout_height="wrap_content" androi

  • Android实现沉浸式导航栏实例代码

    废话不多说了,直接给大家贴代码了,具体代码如下所示: private SystemBarTintManager tintManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // getWindow().addFlags(WindowManager.Layo

  • Android实现沉浸式通知栏通知栏背景颜色跟随app导航栏背景颜色而改变

    最近好多app都已经满足了沉浸式通知栏, 所谓沉浸式通知栏:就是把用来导航的各种界面操作空间隐藏在以程序内容为主的情景中,通过相对"隐形"的界面来达到把用户可视范围最大化地用到内容本身上. 而最新安卓4.4系统的通知栏沉浸模式就是在软件打开的时候通知栏和软件顶部颜色融为一体,这样不仅可以使软件和系统本身更加融为一体. 就是手机的通知栏的颜色不再是白色.黑色简单的两种了,本人用的小米4手机,米4手机中的自带软件都支持沉浸式通知栏, 举个例子:大家可以看一下自己的qq,它的标题的背景颜色是

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

    很多的Android入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章. 思路分析: 1.自定义View实现字母导航栏 2.ListView实现联系人列表 3.字母导航栏滑动事件处理 4.字母导航栏与中间字母的联动 5.字母导航栏与ListView的联动 效果图: 首先,我们先甩出主布局文件,方便后面代码的说明 <!--?xml version="1.0" encoding=&qu

  • Android实现底部导航栏功能(选项卡)

    现在很多android的应用都采用底部导航栏的功能,这样可以使得用户在使用过程中随意切换不同的页面,现在我采用TabHost组件来自定义一个底部的导航栏的功能. 我们先看下该demo实例的框架图: 其中各个类的作用以及资源文件就不详细解释了,还有资源图片(在该Demo中借用了其它应用程序的资源图片)也不提供了,大家可以自行更换自己需要的资源图片.直接上各个布局文件或各个类的代码: 1. res/layout目录下的 maintabs.xml 源码: <?xml version="1.0&q

  • Android自定义View实现字母导航栏的代码

    思路分析: 1.自定义View实现字母导航栏 2.ListView实现联系人列表 3.字母导航栏滑动事件处理 4.字母导航栏与中间字母的联动 5.字母导航栏与ListView的联动 效果图: 首先,我们先甩出主布局文件,方便后面代码的说明 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/re

  • Android 自定义Dialog去除title导航栏的解决方法

    如下所示: Dialog dialog = new Dialog(context); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setContentView(view); 以上这篇Android 自定义Dialog去除title导航栏的解决方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • Android自定义字母导航栏

    本文实例为大家分享了Android字母导航栏的具体代码,供大家参考,具体内容如下 效果 实现逻辑 明确需求 字母导航栏在实际开发中还是比较多见的,城市选择.名称选择等等可能需要到. 现在要做到的就是在滑动控件过程中可以有内容以及 下标的回调,方便处理其他逻辑! 整理思路 1.确定控件的尺寸,防止内容显示不全.相关的逻辑在onMeasure()方法中处理: 2.绘制显示的内容,在按下和抬起不同状态下文字.背景的颜色.相关逻辑在onDraw()方法中: 3.滑动事件的处理以及事件回调.相关逻辑在on

  • Android design包自定义tablayout的底部导航栏的实现方法

    以前做项目大多用的radiobutton,今天用tablayout来做一个tab切换页面的的效果. 实现的效果就是类似QQ.微信的页面间(也就是Fragment间)的切换.如图: 布局只要一个tablayout <android.support.design.widget.TabLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id=&

  • Android自定义View之组合控件实现类似电商app顶部栏

    本文实例为大家分享了Android自定义View之组合控件,仿电商app顶部栏的相关代码,供大家参考,具体内容如下 效果图: 分析:左右两边可以是TextView和Button,设置drawableTop即可,中间的看着像是EditText,但是用过淘宝天猫等类似app的话会发现点击搜索不是在当前Activit进行搜索的,是跳转到另外的页面进行的,所以用TextView然后设置背景即可. 实现流程 参数列表: 设置属性文件:values下建立attrs.xml文件,添加需要自定义的属性. <?x

  • Android自定义view实现侧滑栏详解

    目录 前言 需求 效果图 编写代码 主要问题 前言 上一篇文章学了下自定义View的onDraw函数及自定义属性,做出来的滚动选择控件还算不错,就是逻辑复杂了一些.这篇文章打算利用自定义view的知识,直接手撕一个安卓侧滑栏,涉及到自定义LayoutParams.带padding和margin的measure和layout.利用requestLayout实现动画效果等,有一定难度,但能重新学到很多知识! 需求 这里类似旧版QQ(我特别喜欢之前的侧滑栏),有两层页面,滑动不是最左侧才触发的,而是从

  • 解析Android 8.1平台SystemUI 导航栏加载流程

    需求 基于MTK8163 8.1平台定制导航栏部分,在左边增加音量减,右边增加音量加 思路 需求开始做之前,一定要研读SystemUI Navigation模块的代码流程!!!不要直接去网上copy别人改的需求代码,盲改的话很容易出现问题,然而无从解决.网上有老平台(8.0-)的讲解System UI的导航栏模块的博客,自行搜索.8.0对System UI还是做了不少细节上的改动,代码改动体现上也比较多,但是总体基本流程并没变. 源码阅读可以沿着一条线索去跟代码,不要过分在乎代码细节!例如我客制

  • Android自定义水波纹底部导航的实现

    今天给大家带来一个自定义的底部导航,我不会做动图,只能搞一张图片给大家看看,大家见谅 这个就是自定义的tablayout底部搞好的样式了 TabLayout提供了一个水平布局用于展示tabs,继承自HorizontalScrollView.一般与Viewpager结合使用实现页面和标签联动的效果,是时下APP中非常常用的一个控件 首先我们需要创建一个类或者是模块都可以 package com.example.map.tab; import android.content.Context; imp

随机推荐