效果圖以下:android
首先看看佈局文件,自定義的控件中包含一個 ListView,用於顯示具體的數據內容:canvas
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginTop="20dp" android:background="#fffafafa" android:orientation="vertical" > <com.common.myletterview.LetterFilterListView android:id="@+id/letterView" android:layout_width="wrap_content" android:layout_height="fill_parent" > <ListView android:id="@+id/listView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:cacheColorHint="#00000000" android:divider="@null"/> </com.common.myletterview.LetterFilterListView> </LinearLayout>
加載完 xml 中的 View 後,在自定義控件中添加靠右顯示的一列字母控件、點擊那列字母后的顯示字母控件,這兩個子控件均繼承自 View,均採用自繪。ide
右邊一列字母顯示控件的繪製以下:佈局
/** * 初始化. */ private void init() { mLetters = new char[] { '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', '#' }; mPaint = new Paint(); mPaint.setColor(Color.parseColor("#949494")); mPaint.setTypeface(Typeface.DEFAULT_BOLD); mPaint.setTextSize(22); mPaint.setAntiAlias(true); mPaint.setTextAlign(Paint.Align.CENTER); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float height = getHeight(); mSingleHeight = height / mLetters.length; mWidthCenter = getMeasuredWidth() / (float) 2; for (int i = 0; i < mLetters.length; i++) { canvas.drawText(String.valueOf(mLetters[i]), mWidthCenter, mSingleHeight + (i * mSingleHeight), mPaint); } }
自定義實體實現每項內容與內容首字母的捆綁:post
public class Letter implements Comparable<Letter> { private int id; private String name;//名字 private String firstLetter;//名字首字母 public Letter() { super(); } public Letter(int id, String name, String firstLetter) { super(); this.id = id; this.name = name; this.firstLetter = firstLetter; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFirstLetter() { return firstLetter; } public void setFirstLetter(String firstLetter) { this.firstLetter = firstLetter; } @Override public int compareTo(Letter another) { if (this.getFirstLetter().equals("@") || another.getFirstLetter().equals("#")) { return -1; } else if (this.getFirstLetter().equals("#") || another.getFirstLetter().equals("@")) { return 1; } else { return this.getFirstLetter().compareTo(another.getFirstLetter()); } } }
適配器實現 SectionIndexer 接口,接口中的兩個方法實現是關鍵,具體以下:this
/** * 根據ListView的當前位置獲取分類的首字母的Char ascii值 */ public int getSectionForPosition(int position) { return mList.get(position).getFirstLetter().charAt(0); } /** * 根據分類的首字母的Char ascii值獲取其第一次出現該首字母的位置 */ public int getPositionForSection(int section) { for (int i = 0; i < getCount(); i++) { String sortStr = mList.get(i).getFirstLetter(); char firstChar = sortStr.toUpperCase().charAt(0); if (firstChar == section) { return i; } } return -1; }
適配器每一個 Item 包括字母行跟內容行,若是當前位置等於該分類首字母的Char的位置 ,則顯示字母行跟內容行,不然只顯示出內容行,具體代碼以下:.net
//根據position獲取分類的首字母的Char ascii值 int section = getSectionForPosition(position); //若是當前位置等於該分類首字母的Char的位置 ,則認爲是第一次出現 if(position == getPositionForSection(section)){ viewHolder.tvLetter.setVisibility(View.VISIBLE); viewHolder.tvLetter.setText(letter.getFirstLetter()); }else{ viewHolder.tvLetter.setVisibility(View.GONE); }
在點擊右邊字母列表的時候,根據點擊的位置從新計算點擊了那個字母,從而做相應的定位,並顯示選中的字母:code
public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); int index = 0;//點擊的位置在 mLetters 中的索引 int i = (int) event.getY(); int div = (int) mSingleHeight; /** 從新計算出索引 */ if (div != 0) { index = i / div; } if (index >= mLetters.length) { index = mLetters.length - 1; } else if (index < 0) { index = 0; } switch (event.getAction()) { case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: mLetterSelectedView.setViewY(mSingleHeight + (index * mSingleHeight));//設置選中字母顯示的y軸位置 mLetterSelectedView.setSelectedLetter(mLetters[index]);//設置選中的字母 if (mLetterSelectedView.getVisibility() == View.GONE)//若控件爲隱藏狀態,則顯示 mLetterSelectedView.setVisibility(View.VISIBLE); /** 顯示1s後消失*/ mLetterSelectedView.postDelayed(new Runnable() { @Override public void run() { // TODO Auto-generated method stub mLetterSelectedView.setVisibility(View.GONE); } }, 1000); if (mListView.getAdapter() != null) { ListAdapter listAdapter = (ListAdapter) mListView.getAdapter(); if (mSectionIndexter == null) { mSectionIndexter = (SectionIndexer) listAdapter; } int position = mSectionIndexter.getPositionForSection(mLetters[index]); if (position == -1) {//列表中沒有首字母爲選中字母的的項 return true; } mListView.setSelection(position); } } return true; }
僅僅看此博客可能很難理解具體的實現,你們能夠結合完整的代碼查看,完整代碼:自定義快速查找字母控件xml
最近在運營一個有關反脆弱成長的我的公衆號,歡迎關注繼承