效果圖
導包:
- 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);
- }
- }