效果图
导包:
- compile 'com.android.support:recyclerview-v7:23.1.1'
- compile 'ca.barrenechea.header-decor:header-decor:0.2.6'
这里还用到一个Jar包
- 链接: https://pan.baidu.com/s/1X-dcGEomFlmMWu3QYx_iOg 密码: xi73
1.直接复制拼音工具类
- public class PinYinUtils {
- public static String getPinYin(String text){
- char[] chars = text.toCharArray();
- StringBuilder sb = new StringBuilder();
- HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
- //取消音调
- format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
- //大写
- format.setCaseType(HanyuPinyinCaseType.UPPERCASE);
- for ( char ch : chars ) {
- if(Character.isWhitespace(ch)){
- //如果是空格
- continue;
- }
- if(ch > 128 || ch < -127){
- try{
- //数组是有多音字
- String[] array = PinyinHelper.toHanyuPinyinStringArray(ch, format);
- sb.append(array[0]);
- }catch (BadHanyuPinyinOutputFormatCombination e){
- e.getMessage();
- }
- }else{
- //#$%^
- return "#";
- }
- }
- return sb.toString();
- }
- }
2.ToastUtils 这个随意可以不复制
- public class Utils {
- private static Toast toast;
- public static void showToast(Context context, String text){
- if (toast == null)
- toast = Toast.makeText(context, text, Toast.LENGTH_SHORT);
- toast.setText(text);
- toast.show();
- }
- }
3.我们先自定义一个右边的字母索引
- public class QuickIndexBar extends View {
- private Paint paint;
- private float mCellHeight;
- private int mWidth;
- //26英文字母
- 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 int mHeight;
- private float mTextHeight;
- private int currentIndex = -1;
- private OnLetterChangeListener onLetterChangeListener;
- public OnLetterChangeListener getOnLetterChangeListener() {
- return onLetterChangeListener;
- }
- public void setOnLetterChangeListener(OnLetterChangeListener onLetterChangeListener) {
- this.onLetterChangeListener = onLetterChangeListener;
- }
- //暴露接口
- public interface OnLetterChangeListener{
- void onLetterChange(String letter);
- void onReset();
- }
- public QuickIndexBar(Context context) {
- this(context, null);
- }
- public QuickIndexBar(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public QuickIndexBar(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- paint = new Paint();
- // 画笔默认是 黑色 设置为白色
- //设置字体大小
- paint.setTextSize(dip2px(context, 14));
- //抗锯齿
- paint.setAntiAlias(true);
- // 获取字体的高度
- Paint.FontMetrics fontMetrics = paint.getFontMetrics();
- // 下边界 - 上边界
- //ceil 天花板 0.1 1
- mTextHeight = (float) Math.ceil(fontMetrics.descent - fontMetrics.ascent);
- }
- // 测量完成 改变的时候调用
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- //获取测量后的宽度和高度
- mWidth = getMeasuredWidth();
- mHeight = getMeasuredHeight();
- //每个字母的高度
- mCellHeight = mHeight * 1.0f / LETTERS.length;
- }
- // 怎么画
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- //遍历并绘制26英文字母
- for (int i = 0; i < LETTERS.length; i++) {
- String text = LETTERS[i];
- //测量字体宽度
- float mTextWidth = paint.measureText(text);
- //获取字母的xy坐标,坐标默认为字母左下角
- float x = mWidth / 2 - mTextWidth / 2;
- float y = mCellHeight / 2 + mTextHeight / 2 + mCellHeight * i;
- //判断当前索引并绘制相应的颜色
- if (currentIndex == i){
- //当索引为当前的字母时绘制的颜色
- paint.setColor(Color.parseColor("#000000"));
- }else{
- paint.setColor(Color.parseColor("#FF9696"));
- }
- // 字.画字();
- canvas.drawText(text, x, y, paint);
- }
- }
- //触摸事件
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // 计算当前点击的 字母
- float downY = event.getY();
- // 1.1 --- 1 1.4 --- 1 1.5 --- 1
- currentIndex = (int) (downY / mCellHeight);
- if (currentIndex < 0 || currentIndex > LETTERS.length - 1) {
- } else {
- // Utils.showToast(getContext(), LETTERS[currentIndex]);
- if (onLetterChangeListener != null){
- onLetterChangeListener.onLetterChange(LETTERS[currentIndex]);
- }
- }
- //重新绘制
- // invalidate();
- break;
- case MotionEvent.ACTION_MOVE:
- // 计算当前点击的 字母
- float moveY = event.getY();
- currentIndex = (int) (moveY / mCellHeight); // 1.1 --- 1 1.4 --- 1 1.5 --- 1
- if (currentIndex < 0 || currentIndex > LETTERS.length - 1) {
- } else {
- if (onLetterChangeListener != null){
- onLetterChangeListener.onLetterChange(LETTERS[currentIndex]);
- }
- }
- //重新绘制
- // invalidate();
- break;
- case MotionEvent.ACTION_UP:
- currentIndex = -1;
- if (onLetterChangeListener != null){
- onLetterChangeListener.onReset();
- }
- break;
- }
- //重新绘制
- invalidate();
- // 返回true 为了收到 move & up 事件
- return true;
- }
- /**
- * 根据手机的分辨率从 dip 的单位 转成为 px(像素)
- */
- public static int dip2px(Context context, float dpValue) {
- final float scale = context.getResources().getDisplayMetrics().density;
- return (int) (dpValue * scale + 0.5f);
- }
- }
4.然后到主函数
- public class MainActivity extends AppCompatActivity {
- private android.support.v7.widget.RecyclerView rv;
- private QuickIndexBar QIBar;
- public static final String[] NAMES = new String[] { "宋江", "卢俊义", "吴用",
- "公孙胜", "关胜", "林冲", "秦明", "呼延灼", "花荣", "柴进", "李应", "朱仝", "鲁智深",
- "武松", "董平", "张清", "杨志", "徐宁", "索超", "戴宗", "刘唐", "李逵", "史进", "穆弘",
- "雷横", "李俊", "阮小二", "张横", "阮小五", " 张顺", "阮小七", "杨雄", "石秀", "解珍",
- " 解宝", "燕青", "朱武", "黄信", "孙立", "宣赞", "郝思文", "韩滔", "彭玘", "单廷珪",
- "魏定国", "萧让", "裴宣", "欧鹏", "邓飞", " 燕顺", "杨林", "凌振", "蒋敬", "吕方",
- "郭 盛", "安道全", "皇甫端", "王英", "扈三娘", "鲍旭", "樊瑞", "孔明", "孔亮", "项充",
- "李衮", "金大坚", "马麟", "童威", "童猛", "孟康", "侯健", "陈达", "杨春", "郑天寿",
- "陶宗旺", "宋清", "乐和", "龚旺", "丁得孙", "穆春", "曹正", "宋万", "杜迁", "薛永", "施恩",
- "周通", "李忠", "杜兴", "汤隆", "邹渊", "邹润", "朱富", "朱贵", "蔡福", "蔡庆", "李立",
- "李云", "焦挺", "石勇", "孙新", "顾大嫂", "张青", "孙二娘", " 王定六", "郁保四", "白胜",
- "时迁", "段景柱" };
- private List<ContactsBean> namelist = new ArrayList<>();
- private LinearLayoutManager manager;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- this.rv = (RecyclerView) findViewById(R.id.id_recyclerview);
- QIBar = (QuickIndexBar) findViewById(R.id.qib);
- ContactsBean bean;
- for (int i = 0; i < NAMES.length; i++) {
- bean = new ContactsBean(NAMES[i]);
- namelist.add(bean);
- }
- //对集合进行排序
- Collections.sort(namelist);
- //条目间的间隔线
- DividerDecoration divider = new DividerDecoration.Builder(MainActivity.this)
- .setHeight(R.dimen.default_divider_height)
- .setColorResource(R.color.colorAccent)
- .build();
- manager = new LinearLayoutManager(MainActivity.this);
- rv.setHasFixedSize(true);
- rv.setLayoutManager(manager);
- rv.addItemDecoration(divider);
- final ContactsAdapter adapter = new ContactsAdapter(MainActivity.this, namelist);
- adapter.setOnItemClickListener(new ContactsAdapter.OnItemClickListener() {
- @Override
- public void onClick(int position,String name) {
- Toast.makeText(MainActivity.this,"您点击了"+position+"行"+name,Toast.LENGTH_SHORT).show();
- }
- @Override
- public void onLongClick(int position,String name) {
- Toast.makeText(MainActivity.this,"您长按点击了"+position+"行"+name,Toast.LENGTH_SHORT).show();
- }
- });
- //设置悬浮索引
- StickyHeaderDecoration decor = new StickyHeaderDecoration(adapter);
- rv.setAdapter(adapter);
- rv.addItemDecoration(decor, 1);
- //侧拉索引改变监听
- QIBar.setOnLetterChangeListener(new QuickIndexBar.OnLetterChangeListener() {
- @Override
- public void onLetterChange(String letter) {
- for (int i = 0; i < namelist.size(); i++) {
- if(letter.equals(namelist.get(i).pinyin.charAt(0) + "")) {
- int position = adapter.getPositionForSection(namelist.get(i).pinyin.charAt(0));
- if(position != -1){
- //滑动到指定位置
- manager.scrollToPositionWithOffset(position,0);
- }
- break;
- }
- }
- }
- @Override
- public void onReset() {
- }
- });
- QIBar.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- QIBar.setBackgroundColor(Color.parseColor("#ffe4e4"));
- break;
- case MotionEvent.ACTION_UP:
- QIBar.setBackgroundColor(Color.argb(0,0,0,0));
- break;
- }
- return false;
- }
- });
- }
- }
5.然后到到主布局
- <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" >
- <android.support.v7.widget.RecyclerView
- android:id="@+id/id_recyclerview"
- android:divider="#ffff0000"
- android:dividerHeight="10dp"
- android:background="#ffffff"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- <fan.recyclerviewdemo.QuickIndexBar
- android:layout_alignParentRight="true"
- android:layout_width="23dp"
- android:layout_height="match_parent"
- android:padding="6dp"
- android:id="@+id/qib" />
- </RelativeLayout>
6.再写一个Recycler适配器
- public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.ViewHolder> implements
- StickyHeaderAdapter<ContactsAdapter.HeaderHolder> {
- private LayoutInflater mInflater;
- private List<ContactsBean> namelist;
- private OnItemClickListener mOnItemClickListener;
- private char lastChar = '\u0000';
- private int DisplayIndex = 0;
- public ContactsAdapter(Context context, List<ContactsBean> namelist) {
- mInflater = LayoutInflater.from(context);
- this.namelist = namelist;
- }
- @Override
- public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
- final View view = mInflater.inflate(R.layout.item_contacts_item, viewGroup, false);
- return new ViewHolder(view);
- }
- //条目文本填充
- @Override
- public void onBindViewHolder(ViewHolder viewHolder, final int i) {
- viewHolder.text_item.setText(namelist.get(i).name);
- if( mOnItemClickListener!= null){
- viewHolder.itemView.setOnClickListener( new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mOnItemClickListener.onClick(i,namelist.get(i).name);
- }
- });
- viewHolder. itemView.setOnLongClickListener( new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- mOnItemClickListener.onLongClick(i,namelist.get(i).name);
- return false;
- }
- });
- }
- }
- @Override
- public int getItemCount() {
- return namelist.size();
- }
- public long getHeaderId(int position) {
- //这里面的是如果当前position与之前position重复(内部判断) 则不显示悬浮标题栏 如果不一样则显示标题栏
- char ch = namelist.get(position).pinyin.charAt(0);
- if(lastChar == '\u0000'){
- lastChar = ch;
- return DisplayIndex;
- }else{
- if(lastChar == ch){
- return DisplayIndex;
- }else{
- lastChar = ch;
- DisplayIndex ++ ;
- return DisplayIndex;
- }
- }
- }
- public HeaderHolder onCreateHeaderViewHolder(ViewGroup parent) {
- final View view = mInflater.inflate(R.layout.item_contacts_head, parent, false);
- return new HeaderHolder(view);
- }
- //悬浮标题栏填充文本
- public void onBindHeaderViewHolder(HeaderHolder viewholder, int position) {
- viewholder.header.setText(namelist.get(position).pinyin.charAt(0) + "");
- }
- static class ViewHolder extends RecyclerView.ViewHolder {
- public TextView text_item;
- public ImageView imageView_item;
- public ViewHolder(View itemView) {
- super(itemView);
- text_item = (TextView) itemView.findViewById(R.id.text_item);
- imageView_item= (ImageView) itemView.findViewById(R.id.image_item);
- }
- }
- static class HeaderHolder extends RecyclerView.ViewHolder {
- public TextView header;
- public HeaderHolder(View itemView) {
- super(itemView);
- header = (TextView) itemView;
- }
- }
- /**
- * 获得指定首字母的位置
- * @param ch
- * @return
- */
- public int getPositionForSection(char ch){
- for (int i = 0; i < getItemCount(); i++) {
- char firstChar = namelist.get(i).pinyin.charAt(0);
- if (firstChar == ch) {
- return i;
- }
- }
- return -1;
- }
- public interface OnItemClickListener{
- void onClick( int position,String name);
- void onLongClick( int position,String name);
- }
- public void setOnItemClickListener(OnItemClickListener onItemClickListener ){
- this. mOnItemClickListener=onItemClickListener;
- }
- }
7.我们还需要头部和中间部分 然后创建这两个布局
item_contacts_head
- <TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="20dp"
- android:background="#F4F4F4"
- android:textSize="12sp"
- android:gravity="center_vertical"
- android:paddingLeft="15dp"
- android:paddingRight="16dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="#333333"
- tools:text="Sample text">
- </TextView>
item_contacts_item
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:background="#ffffff">
- <ImageView
- android:id="@+id/image_item"
- android:layout_width="38dp"
- android:layout_height="wrap_content"
- android:src="@mipmap/ic_launcher"
- android:layout_marginLeft="15dp"/>
- <TextView
- android:id="@+id/text_item"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="35dp"
- android:gravity="center_vertical"
- android:textSize="13sp"
- tools:text="Sample text"
- />
- </LinearLayout>
8.最后需要一个排序类
- public class ContactsBean implements Comparable<ContactsBean>{
- public String name;
- public String pinyin;
- private ImageView image;
- public ContactsBean(String name){
- this.name = name;
- this.pinyin = PinYinUtils.getPinYin(name);
- this.image = image;
- }
- @Override
- public int compareTo(ContactsBean another) {
- return this.pinyin.compareTo(another.pinyin);
- }
- }