Android自定义View——实现联系人列表字母索引
2016-11-18 13:53
627 查看
相信大家对这个列表字母索引已经不陌生了,在很多app中也随处可见,像没团的城市地址选择,微信联系人列表,手机通讯录…等等。既然是个这么nb这么实用的功能我们怎么能不Get到来呢,下面就让我们一起造一个出来吧
一:我们可以大致将他分成3小块,右边的字母列表、中央的当前字母提示、ListView列表。ok分析好了那我们就一步步来编码实现吧
二:首先来实现右边的字母列表
1. 在画这个字母列表之前,先画张图来大致计算一下字母的坐标,如下图:
/*绘制的列表导航字母*/ private String words[] = {"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 Paint wordsPaint; /*字母背景画笔*/ private Paint bgPaint; /*每一个字母的宽度*/ private int itemWidth; /*每一个字母的高度*/ private int itemHeight; /*手指按下的字母索引*/ private int touchIndex = 0; /*手指按下的字母改变接口*/ private onWordsChangeListener listener;
//得到画布的宽度和每一个字母所占的高度 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); itemWidth = getMeasuredWidth(); //使得边距好看一些 int height = getMeasuredHeight() - 10; itemHeight = height / 27; }
2. 开始绘制A~Z~#的字符,先绘制字母背景,在绘制文字
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (int i = 0; i < words.length; i++) { //判断是不是我们按下的当前字母 if (touchIndex == i) { //绘制文字圆形背景 canvas.drawCircle(itemWidth / 2, itemHeight / 2 + i * itemHeight, 23, bgPaint); wordsPaint.setColor(Color.WHITE); } else { wordsPaint.setColor(Color.GRAY); } //获取文字的宽高 Rect rect = new Rect(); wordsPaint.getTextBounds(words[i], 0, 1, rect); int wordWidth = rect.width(); //绘制字母 float wordX = itemWidth / 2 - wordWidth / 2; float wordY = itemWidth / 2 + i * itemHeight; canvas.drawText(words[i], wordX, wordY, wordsPaint); } }
3.现在效果就是这个样子了
4. 现在来实现手指滑动或者点击字母列表的时候来改变当前选中的字母和在屏幕中央进行显示。这里怎么实现呢?很容易就想到这里肯定是在onTouchEvent
中做处理,在使用接口回调来在屏幕中央显示当前字母
/** * 当手指触摸按下的时候改变字母背景颜色 */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: float y = event.getY(); //关键点===获得我们按下的是那个索引(字母) int index = (int) (y / itemHeight); if (index != touchIndex) touchIndex = index; //防止数组越界 if (listener != null && 0 <= touchIndex && touchIndex <= words.length - 1) { //回调按下的字母 listener.wordsChange(words[touchIndex]); } invalidate(); break; case MotionEvent.ACTION_UP: //手指抬起,不做任何操作 break; } return true; }
/*手指按下了哪个字母的回调接口*/ public interface onWordsChangeListener { void wordsChange(String words); } /*设置手指按下字母改变监听*/ public void setOnWordsChangeListener(onWordsChangeListener listener) { this.listener = listener; }
5.接口都写好了就可以在主界面中来显示了,activity_main
的布局
<?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"> <ListView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="none" /> <!--字母导航--> <com.zsy.words.view.WordsNavigation android:id="@+id/words" android:layout_width="30dp" android:layout_height="match_parent" android:layout_alignParentRight="true" /> <!--这个就用来显示我们当前按下的字母--> <TextView android:id="@+id/tv" android:layout_width="80dp" android:layout_height="80dp" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:background="@drawable/tvstyle" android:gravity="center" android:textSize="40sp" android:visibility="gone" /> </RelativeLayout
6.MainActivity
中设置字母改变监听
tv = (TextView) findViewById(R.id.tv); word = (WordsNavigation) findViewById(R.id.words); word.setOnWordsChangeListener(this); //手指按下字母改变监听回调 @Override public void wordsChange(String words) { updateWord(words); }
/** * 更新中央的字母提示 * * @param words 首字母 */ private void updateWord(String words) { tv.setText(words); tv.setVisibility(View.VISIBLE); //清空之前的所有消息 handler.removeCallbacksAndMessages(null); //500ms后让tv隐藏 handler.postDelayed(new Runnable() { @Override public void run() { tv.setVisibility(View.GONE); } }, 500); }
三:现在就只剩下ListView列表,和滑动列表来改变字母的背景了同时滑动列表来改变我们listView的数据显示
1.为ListView数据添加一个实体类Person
,这里我们需要对我们的姓名转化成拼音,我这里使用的是pinyin4j-2.5.0.jar
链接文章末尾给出
/* * 文件名: Person * 创建者: 阿钟 * 创建时间: 2016/11/17 19:07 * 描述: 封装联系人列表信息 */ public class Person { //姓名 private String name; //拼音 private String pinyin; //拼音首字母 private String headerWord; public Person(String name) { this.name = name; this.pinyin = PinYinUtils.getPinyin(name); headerWord = pinyin.substring(0, 1); } public String getPinyin() { return pinyin; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHeaderWord() { return headerWord; } }
2.PinYinUtils将汉子转拼音
/* * 文件名: PinYinUtils * 创建者: ZSY * 创建时间: 2016/11/17 17:51 * 描述: 得到指定汉字的拼音 */ public class PinYinUtils { /** * 将hanzi转成拼音 * * @param hanzi 汉字或字母 * @return 拼音 */ public static String getPinyin(String hanzi) { StringBuilder sb = new StringBuilder(); HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); format.setCaseType(HanyuPinyinCaseType.UPPERCASE); format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); //由于不能直接对多个汉子转换,只能对单个汉子转换 char[] arr = hanzi.toCharArray(); for (int i = 0; i < arr.length; i++) { if (Character.isWhitespace(arr[i])) { continue; } try { String[] pinyinArr = PinyinHelper.toHanyuPinyinStringArray(arr[i], format); if (pinyinArr != null) { sb.append(pinyinArr[0]); } else { sb.append(arr[i]); } } catch (Exception e) { e.printStackTrace(); //不是正确的汉字 sb.append(arr[i]); } } return sb.toString(); } }
3.现在我们就来撸ListView的Item布局了list_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!--显示字母--> <TextView android:id="@+id/tv_word" android:layout_width="match_parent" android:layout_height="20dp" android:background="#ebebeb" android:gravity="center_vertical" android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" /> <!--显示联系人信息--> <TextView android:id="@+id/tv_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_vertical" android:minHeight="?android:attr/listPreferredItemHeightSmall" android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" android:paddingRight="?android:attr/listPreferredItemPaddingRight" android:textAppearance="?android:attr/textAppearanceListItemSmall" /> </LinearLayout>
5.为我们的列表添加测试数据,并对数据进行排序
/** * 初始化联系人列表信息 */ private void initData() { list = new ArrayList<>(); list.add(new Person("Dave")); list.add(new Person("阿钟")); //省略一些.... list.add(new Person("胡继群")); list.add(new Person("隔壁老王")); list.add(new Person("姜宇航")); //对集合排序 Collections.sort(list, new Comparator<Person>() { @Override public int compare(Person lhs, Person rhs) { //根据拼音进行排序 return lhs.getPinyin().compareTo(rhs.getPinyin()); } }); }
6.撸ListView的列表适配器
public class MyAdapter extends BaseAdapter { private List<Person> list; private LayoutInflater inflater; public MyAdapter(Context context, List<Person> list) { inflater = LayoutInflater.from(context); this.list = list; } @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) { ViewHolder holder; if (convertView == null) { holder = new ViewHolder(); convertView = inflater.inflate(R.layout.list_item, null); holder.tv_word = (TextView) convertView.findViewById(R.id.tv_word); holder.tv_name = (TextView) convertView.findViewById(R.id.tv_name); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } String word = list.get(position).getHeaderWord(); holder.tv_word.setText(word); holder.tv_name.setText(list.get(position).getName()); //将相同字母开头的合并在一起 if (position == 0) { //第一个是一定显示的 holder.tv_word.setVisibility(View.VISIBLE); } else { //后一个与前一个对比,判断首字母是否相同,相同则隐藏 String headerWord = list.get(position - 1).getHeaderWord(); if (word.equals(headerWord)) { holder.tv_word.setVisibility(View.GONE); } else { holder.tv_word.setVisibility(View.VISIBLE); } } return convertView; } private class ViewHolder { private TextView tv_word; private TextView tv_name; } }
7.现在我们需要来实现,当滑动列表的时候需要更新ListView的显示,在wordsChange
函数中进行出理,同时滑动列表我们也需要更新右侧字母列表的状态,ok逻辑理清了就好办事了。
1.在wordsChange
调用此函数updateListView
改变ListView的显示
/** * @param words 首字母 */ private void updateListView(String words) { for (int i = 0; i < list.size(); i++) { String headerWord = list.get(i).getHeaderWord(); //将手指按下的字母与列表中相同字母开头的项找出来 if (words.equals(headerWord)) { //将列表选中哪一个 listView.setSelection(i); //找到开头的一个即可 return; } } }
2.为ListView设置滑动监听,来改变右侧字母列表的状态
@Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { //当滑动列表的时候,更新右侧字母列表的选中状态 word.setTouchIndex(list.get(firstVisibleItem).getHeaderWord()); }
3.自定义字母列表View中的函数,这样当我们滑动列表的时候字母状态也就可以时时同步了
/*设置当前按下的是那个字母*/ public void setTouchIndex(String word) { for (int i = 0; i < words.length; i++) { if (words[i].equals(word)) { touchIndex = i; invalidate(); return; } } }
ok大功告成,一个简单的联系字母索引列表就实现了。Demo下载地址,汉子转拼音jar包下载如果有什么疑问可以联系我哦
相关文章推荐
- Android自定义View实现通讯录字母索引(仿微信通讯录)
- 【Android】自定义View快速建立字母索引列表
- Android 自定义 View 实现通讯录字母索引(仿微信通讯录)
- Android 自定义View——联系人右侧字母索引
- Android 仿联系人菜单,带字母索引,顶部挤压动画,recyclerview实现联系人页面
- Android开发之自定义view实现通讯录列表A~Z字母提示效果【附demo源码下载】
- 自定义view实现字母索引列表
- Android 自定义 View 实现通讯录字母索引(仿微信通讯录)
- Android使用RecyclerView实现自定义列表、点击事件以及下拉刷新
- 最近较流行的效果 Android自定义View实现倾斜列表/图片
- Android 自定义View-实现快速索引
- Android实现RecyclerView自定义列表、点击事件以及下拉刷新
- android自定义view 二 联系人分类索引
- Android开发之仿联系人界面(根据文字首字母A、B、C实现快速索引)
- Android自定义View之用观察者模式写自定义监听事件以及常用竖直型字母索引栏的写法
- Android中Spinner下拉列表(使用自定义Adapter实现,显示数据取自联系人数据库)
- android自定义View之仿通讯录侧边栏滑动,实现A-Z字母检索
- Android自定义类似联系人列表的 快速索引控件
- Android实现字母索引查找地名、联系人(二)
- 自定义view例子(【图片移动】【仿联系人边上字母列表】)