效果:

mPaint.drawText时,x,y指的是文字的左下角

项目结构:

清单文件:

布局文件:

activity_main:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.sg31.indexbar.MainActivity" ><ListViewandroid:id="@+id/lv_main"android:layout_width="match_parent"android:layout_height="match_parent" ></ListView><com.sg31.indexbar.SGIndexBarandroid:id="@+id/bar"android:layout_width="20dp"android:layout_height="match_parent"android:layout_alignParentRight="true"android:background="#ff0000" /><TextViewandroid:id="@+id/tv_center"android:layout_width="160dp"android:layout_height="100dp"android:background="@drawable/big_letter_bg"android:gravity="center"android:textSize="32sp"android:visibility="gone"android:textColor="#ffffff"android:layout_centerInParent="true"android:text="A" /></RelativeLayout>

cell_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" ><TextViewandroid:id="@+id/tv_index"android:layout_width="match_parent"android:layout_height="40dp"android:background="#888888"android:gravity="center_vertical"android:paddingLeft="20dp"android:text="A"android:textColor="#ffffff"android:textSize="20sp" /><TextViewandroid:id="@+id/tv_name"android:layout_width="match_parent"android:layout_height="50dp"android:gravity="center_vertical"android:paddingLeft="20dp"android:text="宋江"android:textSize="24sp" /></LinearLayout>

圆角矩形
res/drawable/big_letter_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"><solid android:color="#6000"/><corners android:radius="29dp"/></shape>

工具类:(弹Toast)

package com.sg31.tool;import android.content.Context;
import android.graphics.Rect;
import android.support.v4.view.MotionEventCompat;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;public class Utils {public static Toast mToast;public static void showToast(Context mContext, String msg) {if (mToast == null) {mToast = Toast.makeText(mContext, "", Toast.LENGTH_LONG);}mToast.setText(msg);mToast.show();}/*** dip 转换成 px* @param dip* @param context* @return*/public static float dip2Dimension(float dip, Context context) {DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);}/*** @param dip* @param context* @param complexUnit {@link TypedValue#COMPLEX_UNIT_DIP} {@link TypedValue#COMPLEX_UNIT_SP}}* @return*/public static float toDimension(float dip, Context context, int complexUnit) {DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();return TypedValue.applyDimension(complexUnit, dip, displayMetrics);}/** 获取状态栏高度* @param v* @return*/public static int getStatusBarHeight(View v) {if (v == null) {return 0;}Rect frame = new Rect();v.getWindowVisibleDisplayFrame(frame);return frame.top;}public static String getActionName(MotionEvent event) {String action = "unknow";switch (MotionEventCompat.getActionMasked(event)) {case MotionEvent.ACTION_DOWN:action = "ACTION_DOWN";break;case MotionEvent.ACTION_MOVE:action = "ACTION_MOVE";break;case MotionEvent.ACTION_UP:action = "ACTION_UP";break;case MotionEvent.ACTION_CANCEL:action = "ACTION_CANCEL";break;case MotionEvent.ACTION_SCROLL:action = "ACTION_SCROLL";break;case MotionEvent.ACTION_OUTSIDE:action = "ACTION_SCROLL";break;default:break;}return action;}
}

工具类:(汉字转拼音)

package com.sg31.tool;import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;public class PingYinUtils {/*** 根据传入的字符串(包含汉字),得到拼音* 帅哥 -> SHUAIGE* @param str 字符串* @return*/public static String getPinyin(String str) {HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();format.setCaseType(HanyuPinyinCaseType.UPPERCASE);format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);StringBuilder sb = new StringBuilder();char[] charArray = str.toCharArray();for (int i = 0; i < charArray.length; i++) {char c = charArray[i];// 如果是空格, 跳过if(Character.isWhitespace(c)){continue;}if(c >= -127 && c < 128){// 肯定不是汉字sb.append(c);}else {String s = "";try {// 通过char得到拼音集合. 单 -> dan, shan s = PinyinHelper.toHanyuPinyinStringArray(c, format)[0];sb.append(s);} catch (BadHanyuPinyinOutputFormatCombination e) {e.printStackTrace();sb.append(s);}}}return sb.toString();}}

模拟的数据源:(108好汉)

package com.sg31.tool;public class Cheeses {public static final String[] NAMES = new String[] { "宋江", "卢俊义", "吴用","公孙胜", "关胜", "林冲", "秦明", "呼延灼", "花荣", "柴进", "李应", "朱仝", "鲁智深","武松", "董平", "张清", "杨志", "徐宁", "索超", "戴宗", "刘唐", "李逵", "史进", "穆弘","雷横", "李俊", "阮小二", "张横", "阮小五", " 张顺", "阮小七", "杨雄", "石秀", "解珍"," 解宝", "燕青", "朱武", "黄信", "孙立", "宣赞", "郝思文", "韩滔", "彭玘", "单廷珪","魏定国", "萧让", "裴宣", "欧鹏", "邓飞", " 燕顺", "杨林", "凌振", "蒋敬", "吕方","郭 盛", "安道全", "皇甫端", "王英", "扈三娘", "鲍旭", "樊瑞", "孔明", "孔亮", "项充","李衮", "金大坚", "马麟", "童威", "童猛", "孟康", "侯健", "陈达", "杨春", "郑天寿","陶宗旺", "宋清", "乐和", "龚旺", "丁得孙", "穆春", "曹正", "宋万", "杜迁", "薛永", "施恩","周通", "李忠", "杜兴", "汤隆", "邹渊", "邹润", "朱富", "朱贵", "蔡福", "蔡庆", "李立","李云", "焦挺", "石勇", "孙新", "顾大嫂", "张青", "孙二娘", " 王定六", "郁保四", "白胜","时迁", "段景柱" };}

模型类:

package com.sg31.model;import com.sg31.tool.PingYinUtils;public class Person implements Comparable<Person>{private String name;private String pinyin;public Person(String name) {super();this.name = name;this.pinyin = PingYinUtils.getPinyin(name);}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPinyin() {return pinyin;}public void setPinyin(String pinyin) {this.pinyin = pinyin;}@Overridepublic int compareTo(Person another) {return this.pinyin.compareTo(another.getPinyin());}}

ListView的适配器

package com.sg31.indexbar;import java.util.ArrayList;import com.sg31.model.Person;import android.content.Context;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;public class PersonAdapter extends BaseAdapter {private Context mContext;private ArrayList<Person> persons;public PersonAdapter(Context mContext, ArrayList<Person> persons) {this.mContext = mContext;this.persons = persons;}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn persons.size();}@Overridepublic Object getItem(int position) {return persons.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {View view = convertView;if(convertView == null){view = view.inflate(mContext, R.layout.cell_item, null);}ViewHolder mViewHolder = ViewHolder.getHolder(view);Person p = persons.get(position);String str = null;String currentLetter = p.getPinyin().charAt(0) + "";// 根据上一个首字母,决定当前是否显示字母if(position == 0){str = currentLetter;}else {// 上一个人的拼音的首字母String preLetter = persons.get(position - 1).getPinyin().charAt(0) + "";if(!TextUtils.equals(preLetter, currentLetter)){str = currentLetter;}}// 根据str是否为空,决定是否显示索引栏mViewHolder.mIndex.setVisibility(str == null ? View.GONE : View.VISIBLE);mViewHolder.mIndex.setText(currentLetter);mViewHolder.mName.setText(p.getName());return view;}static class ViewHolder {TextView mIndex;TextView mName;public static ViewHolder getHolder(View view) {Object tag = view.getTag();if(tag != null){return (ViewHolder)tag;}else {ViewHolder viewHolder = new ViewHolder();viewHolder.mIndex = (TextView) view.findViewById(R.id.tv_index);viewHolder.mName = (TextView) view.findViewById(R.id.tv_name);view.setTag(viewHolder);return viewHolder;}}}}

主控制器:

package com.sg31.indexbar;import java.util.ArrayList;
import java.util.Collections;import com.sg31.indexbar.SGIndexBar.OnLetterUpdateListener;
import com.sg31.model.Person;
import com.sg31.tool.Cheeses;import android.support.v7.app.ActionBarActivity;
import android.text.TextUtils;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;public class MainActivity extends ActionBarActivity {private ListView mMainList;private ArrayList<Person> persons;private TextView tv_center;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);SGIndexBar bar = (SGIndexBar) findViewById(R.id.bar);// 设置监听bar.setListener(new OnLetterUpdateListener() {@Overridepublic void onLetterUpdate(String letter) {// Utils.showToast(getApplicationContext(), letter);showLetter(letter);// 根据字母定位ListView, 找到集合中第一个以letter为拼音首字母的对象,得到索引for (int i = 0; i < persons.size(); i++) {Person person = persons.get(i);String l = person.getPinyin().charAt(0) + "";if (TextUtils.equals(letter, l)) {// 匹配成功mMainList.setSelection(i);break;}}}});mMainList = (ListView) findViewById(R.id.lv_main);persons = new ArrayList<Person>();// 填充数据 , 排序fillAndSortData(persons);mMainList.setAdapter(new PersonAdapter(MainActivity.this, persons));tv_center = (TextView) findViewById(R.id.tv_center);}private Handler mHandler = new Handler();/*** 显示字母* * @param letter*/protected void showLetter(String letter) {tv_center.setVisibility(View.VISIBLE);tv_center.setText(letter);mHandler.removeCallbacksAndMessages(null);mHandler.postDelayed(new Runnable() {@Overridepublic void run() {tv_center.setVisibility(View.GONE);}}, 2000);}private void fillAndSortData(ArrayList<Person> persons) {// 填充数据for (int i = 0; i < Cheeses.NAMES.length; i++) {String name = Cheeses.NAMES[i];persons.add(new Person(name));}// 进行排序Collections.sort(persons);}
}

自定义的IndexBar

package com.sg31.indexbar;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;/*** 快速索引* * 用于根据字母快速定位联系人* @author poplar**/
public class SGIndexBar extends View {private static final String[] LETTERS = 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 static final String TAG = "TAG";private Paint mPaint;private int cellWidth;private float cellHeight;/*** 暴露一个字母的监听*/public interface OnLetterUpdateListener{void onLetterUpdate(String letter);}private OnLetterUpdateListener listener;public OnLetterUpdateListener getListener() {return listener;}/*** 设置字母更新监听* @param listener*/public void setListener(OnLetterUpdateListener listener) {this.listener = listener;}public SGIndexBar(Context context) {this(context, null);}public SGIndexBar(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SGIndexBar(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setColor(Color.WHITE);mPaint.setTextSize(18);// 设置粗体mPaint.setTypeface(Typeface.DEFAULT_BOLD);}@Overrideprotected void onDraw(Canvas canvas) {for (int i = 0; i < LETTERS.length; i++) {String text = LETTERS[i];// 计算坐标int x = (int) (cellWidth / 2.0f - mPaint.measureText(text) / 2.0f);// 获取文本的高度Rect bounds = new Rect();// 矩形mPaint.getTextBounds(text, 0, text.length(), bounds);int textHeight = bounds.height();int y = (int) (cellHeight / 2.0f + textHeight / 2.0f + i * cellHeight);// 根据按下的字母, 设置画笔颜色mPaint.setColor(touchIndex == i ? Color.GRAY : Color.WHITE);// 绘制文本A-Zcanvas.drawText(text, x, y, mPaint);}}int touchIndex = -1;@Overridepublic boolean onTouchEvent(MotionEvent event) {int index = -1;switch (MotionEventCompat.getActionMasked(event)) {case MotionEvent.ACTION_DOWN:// 获取当前触摸到的字母索引index = (int) (event.getY() / cellHeight);if(index >= 0 && index < LETTERS.length){// 判断是否跟上一次触摸到的一样if(index != touchIndex) {if(listener != null){listener.onLetterUpdate(LETTERS[index]);}Log.d(TAG, "onTouchEvent: " + LETTERS[index]);touchIndex = index;}}break;case MotionEvent.ACTION_MOVE:index = (int) (event.getY() / cellHeight);if(index >= 0 && index < LETTERS.length){// 判断是否跟上一次触摸到的一样if(index != touchIndex){if(listener != null){listener.onLetterUpdate(LETTERS[index]);}Log.d(TAG, "onTouchEvent: " + LETTERS[index]);touchIndex = index;}}break;case MotionEvent.ACTION_UP:touchIndex = -1;break;default:break;}invalidate();return true;}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);// 获取单元格的宽和高cellWidth = getMeasuredWidth();int mHeight = getMeasuredHeight();cellHeight = mHeight * 1.0f / LETTERS.length;}}

android_91_快速索引相关推荐

  1. LINUX下用CTRL+R快速搜索HISTORY历史命令,快速索引到之前使用过的命令行语句

    LINUX下用CTRL+R快速搜索HISTORY历史命令,快速索引到之前使用过的命令行语句 前提是,搜索已经使用的命令,否则是查不出来结果的. ctrl+r 用途:反向搜索执行过的命令.(revers ...

  2. 快速索引 (对View的自定义)

    快速索引 (对View的自定义) 快速索引应用场景: 微信好友列表, 联系人通讯录, 应用管理, 文件管理等. 快速索引7步曲: *1. A-Z索引的绘制. * 2. 处理Touch事件. * 3. ...

  3. Elasticsearch是如何做到快速索引的

    最近在参与一个基于Elasticsearch作为底层数据框架提供大数据量(亿级)的实时统计查询的方案设计工作,花了些时间学习Elasticsearch的基础理论知识,整理了一下,希望能对Elastic ...

  4. SEO关键词优化:如何理解被百度快速索引?

    经常会有SEO人员讨论,为什么我的SEO关键词总是不被快速索引,而实际上这里主要指的是,针对特定关键词的核心内容,那么,它主要涉及如下两个指标: ①索引 ②快速收录 这里值得说明的是:索引并不等于收录 ...

  5. Android仿微信实现快速索引选择联系人

    Android仿微信实现快速索引选择联系人 原创 2016年03月05日 13:19:20 1640 3 1 一.概述 先看效果图,然后在给大家慢慢介绍  二.实现 先给大家说说这些城市的数据是怎么来 ...

  6. android 仿微信联系人 首字母分组快速索引

    总结是一种习惯,不能停,一停人就懒了,都快一个月没有写了!该提提神了! 进入正题:android 仿微信联系人 首字母快速索引,先用下美团的索引效果图: 1.自定义View字母索引栏(右边那一列): ...

  7. 通讯录 之 快速索引

    实现步骤于逻辑分析: 首先绘制A-z 的字母列表 响应字母的触摸事件 提供监听回调 获取汉字拼音及首字母 拼音排序 首字母分组 最后把监听回调和listview 结合起来 首先 是布局文件 : < ...

  8. 仿小米相册列表实现自定义带快速索引功能的RecyclerView

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 篇章目标要点 一.实现效果 二.设计布局原理 三.关键代码实现 1.浮标随手势移动 2.浮标随列表移动 3.列表随浮标移动而 ...

  9. 自定义控件:快速索引

    快速索引在应用中很常见,在联系人,微信,省市列表,应用管理,文件管理等应用场景都可以看到快速索引的身影,本篇博客将讲解快速索引的自定义,从中你可以学到获取汉字首字母的方法,绘制字母时,纵坐标的计算方法 ...

  10. IPFS - 可快速索引的版本化的点对点文件系统(草稿3)

    摘要 星际文件系统是一种点对点的分布式文件系统, 旨在连接所有有相同的文件系统的计算机设备.在某些方面, IPFS类似于web, 但web 是中心化的,而IPFS是一个单一的Bittorrent 群集 ...

最新文章

  1. [异常解决] ubuntukylin16.04 LTS中关于flash安装和使用不了的问题解决
  2. grep如何忽略.svn目录,以及如何忽略多个目录
  3. 【ubuntu】ubuntu16.04的虚拟机突然打不开terminal了
  4. 从喧闹与富有中搞懂搜索和拓扑
  5. python在线包安装mysql_python安装mysql的依赖包mysql-python操作
  6. Mac系统搭建java开发环境
  7. IE6丢失COOKIE的问题,令人郁闷的P3P协议
  8. 注册cuteftppro 8.3
  9. 【电信学】【2019.07】基于ATOLL的5G网络规划与优化
  10. 【分享一些自己收集的API接口---欢迎点赞收藏】
  11. 2022年终总结:少年不惧岁月长,彼方尚有荣光在。
  12. emoji表情mysql报错_MySql存储emoji表情报错的处理方法
  13. 安利一波gif录制工具
  14. java程序 部署服务器_Java代码如何布署到云服务器
  15. 在Win10家庭中文版系统安装Hyper-V
  16. Retbleed:针对英特尔和AMD处理器的推断性执行攻击
  17. 实践报告抽象类java_java抽象类不能实例化实践是检验真理的唯一标准
  18. 谷歌内战:高管要利润,员工要价值观,谁来定义谷歌?
  19. 【编译原理】活动记录
  20. 单向Boost PFC+全桥LLC串联谐振开关电源Matlab simulink仿真模型

热门文章

  1. ES6新增特性?20分钟了解....
  2. 微信公众号token认证、关注/取消关注事件
  3. delphi的BPL应用
  4. 真牛!从外包月薪5K到阿里月薪15K,先收藏了
  5. 5个免费的3D钣金CAD软件
  6. 编程零基础应该如何开始学习Python?
  7. Android Studio 从历史记录找回代码
  8. 《老路用得上的商学课》51-60学习笔记
  9. jdk1.8日期转换(Date、LocalDate、LocalDateTime、Timestamp之间转换)
  10. Linux虚拟机无法连接网络,网卡ensXX的inet消失的解决方法