Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 常用的自定義控件四(QuickBarView)

常用的自定義控件四(QuickBarView)

編輯:關於Android編程

自定義View通訊錄字母快速索引


在Android日常開發中,我們經常在聯系人界面看到一些字母導航欄,點擊字母的時候,會根據漢字的首拼音來查找是否存在相應的item,這種效果很常見,幾乎所有涉及到通訊的都會用到,包括qq,微信,微博等,今天我為大家帶來的就是這種自定義控件

廢話不多說 ,大家先來看一下實際的效果

效果圖一

\


源碼下載地址:https://github.com/gdutxiaoxu/QuickIndex.git

大家先來看一下源碼


代碼 解析

代碼其實不長,加上一些注釋總共才180多行,總體來說,思路分分為以下幾個步驟

  1.  
  2. /**
  3. * 博客地址:http://blog.csdn.net/gdutxiaoxu
  4. * 快速索引,根據字母的索引查找相應的聯系人
  5. *
  6. * @author xujun
  7. * @time 2015/11/1 21:40.
  8. */
  9. public class QuickIndexBar extends View {
  10.  
  11. private static final String[] LETTERS = new String[]{
  12. "A", "B", "C", "D", "E", "F",
  13. "G", "H", "I", "J", "K", "L",
  14. "M", "N", "O", "P", "Q", "R",
  15. "S", "T", "U", "V", "W", "X",
  16. "Y", "Z"};
  17.  
  18. private static final String TAG = "xujun";
  19. private Paint mPaint;
  20. //字母的寬度
  21. private int cellWidth;
  22. //字母的高度
  23. private float cellHeight;
  24. //記錄上一次觸摸的Index
  25. private int mLastTouchIndex = -1;
  26. //字母被選中顯示的顏色
  27. private int mSelectColor = Color.GRAY;
  28. //字母正常顯示的顏色
  29. private int mNormalColor = Color.WHITE;
  30. private Context mContext;
  31.  
  32. /**
  33. * 暴露一個字母的監聽
  34. */
  35. public interface OnLetterUpdateListener {
  36.  
  37. void onLetterUpdate(String letter);
  38. }
  39.  
  40. private OnLetterUpdateListener listener;
  41.  
  42. public OnLetterUpdateListener getListener() {
  43. return listener;
  44. }
  45.  
  46. /**
  47. * 設置字母更新監聽
  48. *
  49. * @param listener
  50. */
  51. public void setListener(OnLetterUpdateListener listener) {
  52. this.listener = listener;
  53. }
  54.  
  55. public QuickIndexBar(Context context) {
  56. this(context, null);
  57. }
  58.  
  59. public QuickIndexBar(Context context, AttributeSet attrs) {
  60. this(context, attrs, 0);
  61. }
  62.  
  63. public QuickIndexBar(Context context, AttributeSet attrs, int defStyle) {
  64. super(context, attrs, defStyle);
  65. mContext = context;
  66. //初始化自定義屬性
  67. obtainAttrs(attrs);
  68.  
  69. // 初始化畫筆
  70. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  71. float textSize = UIUtils.dip2px(15, mContext);
  72. mPaint.setTextSize(textSize);
  73. mPaint.setTypeface(Typeface.DEFAULT_BOLD);
  74. }
  75.  
  76. private void obtainAttrs(AttributeSet attrs) {
  77. TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.QuickIndexBar);
  78. int selectColor = typedArray.getColor(R.styleable.QuickIndexBar_select_color, -1);
  79. if (selectColor != -1) {
  80. mSelectColor = selectColor;
  81. }
  82. int normalColor = typedArray.getColor(R.styleable.QuickIndexBar_normal_color, -1);
  83. if (normalColor != -1) {
  84. mNormalColor = normalColor;
  85. }
  86.  
  87.  
  88. typedArray.recycle();
  89.  
  90. }
  91.  
  92. @Override
  93. protected void onDraw(Canvas canvas) {
  94.  
  95. for (int i = 0; i < LETTERS.length; i++) {
  96. String text = LETTERS[i];
  97. // 計算坐標
  98. int x = (int) (cellWidth / 2.0f - mPaint.measureText(text) / 2.0f);
  99. // 獲取文本的高度
  100. Rect bounds = new Rect();// 矩形
  101. mPaint.getTextBounds(text, 0, text.length(), bounds);
  102. int textHeight = bounds.height();
  103. int y = (int) (cellHeight / 2.0f + textHeight / 2.0f + i * cellHeight);
  104.  
  105. // 根據按下的字母, 設置畫筆顏色
  106.  
  107. mPaint.setColor(mLastTouchIndex == i ? mSelectColor : mNormalColor);
  108.  
  109. // 繪制文本A-Z
  110. canvas.drawText(text, x, y, mPaint);
  111. }
  112. }
  113.  
  114. @Override
  115. public boolean onTouchEvent(MotionEvent event) {
  116. int index = -1;
  117. switch (MotionEventCompat.getActionMasked(event)) {
  118. case MotionEvent.ACTION_DOWN:
  119. // 獲取當前觸摸到的字母索引
  120. index = (int) (event.getY() / cellHeight);
  121. if (index >= 0 && index < LETTERS.length) {
  122. // 判斷是否跟上一次觸摸到的一樣,不一樣才進行回調
  123. if (index != mLastTouchIndex) {
  124. if (listener != null) {
  125. //
  126. listener.onLetterUpdate(LETTERS[index]);
  127. }
  128. Log.d(TAG, "onTouchEvent: " + LETTERS[index]);
  129. //記錄上一次觸摸的Index為當前的index;
  130. mLastTouchIndex = index;
  131. }
  132. }
  133.  
  134. break;
  135. case MotionEvent.ACTION_MOVE:
  136. index = (int) (event.getY() / cellHeight);
  137. if (index >= 0 && index < LETTERS.length) {
  138. // 判斷是否跟上一次觸摸到的一樣
  139. if (index != mLastTouchIndex) {
  140.  
  141. if (listener != null) {
  142. listener.onLetterUpdate(LETTERS[index]);
  143. }
  144. Log.d(TAG, "onTouchEvent: " + LETTERS[index]);
  145.  
  146. mLastTouchIndex = index;
  147. }
  148. }
  149. break;
  150. case MotionEvent.ACTION_UP:
  151. // 手指抬起的時候重置
  152. mLastTouchIndex = -1;
  153. break;
  154.  
  155. default:
  156. break;
  157. }
  158. //調用這個方法會重新調用draw方法,重新繪制
  159. invalidate();
  160.  
  161. return true;
  162. }
  163.  
  164. /**
  165. * 當大小 改變的時候會回調這個方法,
  166. * 這裡我們就不主動調用measure()方法了
  167. *
  168. * @param w
  169. * @param h
  170. * @param oldw
  171. * @param oldh
  172. */
  173. @Override
  174. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  175. super.onSizeChanged(w, h, oldw, oldh);
  176. // 獲取單元格的寬和高
  177. cellWidth = getMeasuredWidth();
  178. int mHeight = getMeasuredHeight();
  179. cellHeight = mHeight * 1.0f / LETTERS.length;
  180.  
  181. }
  182.  
  183.  
  184. }
在構造方法裡面初始化畫筆,同時為了使用方便,我們封裝了自定義屬性

  1. public QuickIndexBar(Context context, AttributeSet attrs, int defStyle) {
  2. super(context, attrs, defStyle);
  3. mContext = context;
  4. //初始化自定義屬性
  5. obtainAttrs(attrs);
  6.  
  7. // 初始化畫筆
  8. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  9. float textSize = UIUtils.dip2px(15, mContext);
  10. mPaint.setTextSize(textSize);
  11. mPaint.setTypeface(Typeface.DEFAULT_BOLD);
  12. }
  13.  
  14. private void obtainAttrs(AttributeSet attrs) {
  15. TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.QuickIndexBar);
  16. int selectColor = typedArray.getColor(R.styleable.QuickIndexBar_select_color, -1);
  17. if (selectColor != -1) {
  18. mSelectColor = selectColor;
  19. }
  20. int normalColor = typedArray.getColor(R.styleable.QuickIndexBar_normal_color, -1);
  21. if (normalColor != -1) {
  22. mNormalColor = normalColor;
  23. }
  24.  
  25. typedArray.recycle();
  26. }
接著我們在onSizeChange 方法裡面拿到我們時間的寬度和高度,有人可能會問了為什麼不在onMeasure裡面獲取了,其實在onMeasure方法裡面獲取是可以的,只不過我們還需要調用一下measure方法而已,在onSizeChnage方法裡面,我們直接調用

即可獲取到我們需要的寬度和高度。用起來比較方便,不過更多的是為了讓大家知道View有這一個方法存在以及怎麼使用它
順便我們來看一下google官方對onSizeChange方法的解釋

This is called during layout when the size of this view has changed. If you were just added to the view hierarchy, you're called with the old values of 0.

從官方的解釋我們可以知道這個方法是在onLayout方法中當大小改變的時候會調用這個方法,因此我們直接調用getMeasuredWidth();是可以獲取得到寬度的,因為onMeasure 是先於onLayout方法調用的。
3. 接著我們重寫onDraw方法,在onDraw方法我們所做的工作就是繪制 我們需要的26個字母


  1. cellWidth = getMeasuredWidth();
  2. protected void onDraw(Canvas canvas) {
  3.  
  4. for (int i = 0; i < LETTERS.length; i++) {
  5. String text = LETTERS[i];
  6. // 計算坐標
  7. int x = (int) (cellWidth / 2.0f - mPaint.measureText(text) / 2.0f);
  8. // 獲取文本的高度
  9. Rect bounds = new Rect();// 矩形
  10. mPaint.getTextBounds(text, 0, text.length(), bounds);
  11. int textHeight = bounds.height();
  12. int y = (int) (cellHeight / 2.0f + textHeight / 2.0f + i * cellHeight);
  13.  
  14. // 根據按下的字母, 設置畫筆顏色
  15.  
  16. mPaint.setColor(mLastTouchIndex == i ? mSelectColor : mNormalColor);
  17.  
  18. // 繪制文本A-Z
  19. canvas.drawText(text, x, y, mPaint);
  20. }
  21. }
講到這裡,我們的工作已經完成一大半了,接著就是處理我們是按下或者一個字母了,我們重寫onTouchEvent方法,並且return true;是為了保證 Action_down動作按下以後,Action_move以後的動作能夠順利接受到,這涉及到View的事件分發機制,有空的話我會嘗試總結一下,這裡就不說了

同時我們記錄下我們當前是觸摸或者按下哪一個字母


  1. // 獲取當前觸摸到的字母索引
  2. index = (int) (event.getY() / cellHeight);
  3. mLastTouchIndex = index;
  4. 知道了我們當前是觸摸或者按下哪一個字母了,那我們要怎樣將這些信息暴露出去了,不難想象就是采用接口回調的方法,為此我們提供了這樣一個接口

並且提供了設置監聽器的方法,這樣我們就成功將我們的按下字母的信息提供給外界了


詳細代碼如下


到此 QuickBarView的源碼分析為止,下面我們來學習一下是怎樣結合ListView使用的


下面我貼出核心代碼,想仔細了解的請點擊源碼下載源碼下載地址


思路解析 如下
1. 在我們的List數據裡面查找是否有有相應的首字母是觸摸的字母,有的話返回相應的index,
2. 然後再調用ListView的setSelection(i)方法選中哪一個Item


至於有些item有顯示字母,有一些沒有顯示字母,其實就是判斷上一個item的首字母是不是跟當前的首字母是不是一樣的,不一樣的話,顯示當前item的字母,不過要注意一點,就是position等於0的時候,我們需要做特殊處理,代碼如下


到此我們的分析為止

源碼下載地址:https://github.com/gdutxiaoxu/QuickIndex.git

 

  1. /**
  2. * 暴露一個字母的監聽
  3. */
  4. public interface OnLetterUpdateListener {
  5.  
  6. void onLetterUpdate(String letter);
  7. }
  8. public void setListener(OnLetterUpdateListener listener) {
  9. this.listener = listener;
  10. }
  11. int index = -1;
  12. switch (MotionEventCompat.getActionMasked(event)) {
  13. case MotionEvent.ACTION_DOWN:
  14. // 獲取當前觸摸到的字母索引
  15. index = (int) (event.getY() / cellHeight);
  16. if (index >= 0 && index < LETTERS.length) {
  17. // 判斷是否跟上一次觸摸到的一樣,不一樣才進行回調
  18. if (index != mLastTouchIndex) {
  19. if (listener != null) {
  20. //
  21. listener.onLetterUpdate(LETTERS[index]);
  22. }
  23. Log.d(TAG, "onTouchEvent: " + LETTERS[index]);
  24. //記錄上一次觸摸的Index為當前的index;
  25. mLastTouchIndex = index;
  26. }
  27. }
  28.  
  29. break;
  30. case MotionEvent.ACTION_MOVE:
  31. index = (int) (event.getY() / cellHeight);
  32. if (index >= 0 && index < LETTERS.length) {
  33. // 判斷是否跟上一次觸摸到的一樣
  34. if (index != mLastTouchIndex) {
  35.  
  36. if (listener != null) {
  37. listener.onLetterUpdate(LETTERS[index]);
  38. }
  39. Log.d(TAG, "onTouchEvent: " + LETTERS[index]);
  40.  
  41. mLastTouchIndex = index;
  42. }
  43. }
  44. break;
  45. case MotionEvent.ACTION_UP:
  46. // 手指抬起的時候重置
  47. mLastTouchIndex = -1;
  48. break;
  49.  
  50. default:
  51. break;
  52. }
  53. //調用這個方法會重新調用draw方法,重新繪制
  54. invalidate();
  55. return true;
  56. mQuickIndexBar.setListener(new OnLetterUpdateListener() {
  57. @Override
  58. public void onLetterUpdate(String letter) {
  59. // UIUtils.showToast(getApplicationContext(), letter);
  60.  
  61. showLetter(letter);
  62. // 根據字母定位ListView, 找到集合中第一個以letter為拼音首字母的對象,得到索引
  63. for (int i = 0; i < persons.size(); i++) {
  64. Person person = persons.get(i);
  65. String l = person.getPinyin().charAt(0) + "";
  66. if (TextUtils.equals(letter, l)) {
  67. // 匹配成功
  68. mListView.setSelection(i);
  69. break;
  70. }
  71. }
  72. }
  73. });
  74. mListView.setSelection(i);
  75. String str = null;
  76. String currentLetter = p.getPinyin().charAt(0) + "";
  77. // 根據上一個首字母,決定當前是否顯示字母
  78. if(position == 0){
  79. str = currentLetter;
  80. }else {
  81. // 上一個人的拼音的首字母
  82. String preLetter = persons.get(position - 1).getPinyin().charAt(0) + "";
  83. if(!TextUtils.equals(preLetter, currentLetter)){
  84. str = currentLetter;
  85. }
  86. }
  87.  
  88. // 根據str是否為空,決定是否顯示索引欄
  89. mViewHolder.mIndex.setVisibility(str == null ? View.GONE : View.VISIBLE);
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved