Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android自定義View實現通訊錄字母索引(仿微信通訊錄)

Android自定義View實現通訊錄字母索引(仿微信通訊錄)

編輯:關於Android編程

一、效果:我們看到很多軟件的通訊錄在右側都有一個字母索引功能,像微信,小米通訊錄,QQ,還有美團選擇地區等等。這裡我截了一張美團選擇城市的圖片來看看;

我們今天就來實現圖片中右側模塊的索引功能,包括觸摸顯示以選中的索引字母。這裡我的UI界面主要是參照微信的界面來實現,所以各位也可以對照微信來看看效果,什麼都不說了,只有效果圖最具有說服力!

二、分析:

我們看到這樣的效果我們心理都回去琢磨,他是如何實現的;

首先,它肯定是通過自定義 View 來實現的,因為 Android 沒有提供類似這樣的控件、那麼接下來就是如何自定義我們的 View ,我們知道自定義 View 最最主要的兩個方法就是 onDraw(Canvas canvas)和

onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,當然,如果是自定義 ViewGroup 的話就必須實現

onLayout(boolean changed, int left, int top, int right, int bottom) 方法,這裡我們顯然用自定義 View 就能夠實現此功能,通過效果圖可以看帶,當觸摸這塊區域的時候,會彈出一個懸浮類似 Toast 的框來顯示已經選中的索引內容,所以這裡還需要重寫View 的onTouchEvent(MotionEvent event)事件,最後就是懸浮框的實現。那麼接下來就開始我們編碼。

三、編碼實現:

我們就按照 View 的執行順序來實現

1、實現onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,這個方法的功能是測量出我們的寬和高,具體實現看代碼

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
 }

這裡定義了兩個方法measureWidth( int) 和 measureHeight(int) ,通過方法名可以很清楚的知道,其功能分別是測量寬和高,進去看看是如何測量的。

 /**
  * 測量本身的大小,這裡只是測量寬度
  * @param widthMeaSpec 傳入父View的測量標准
  * @return 測量的寬度
  */
 private int measureWidth(int widthMeaSpec){
  /*定義view的寬度*/
  int width ;
  /*獲取當前 View的測量模式*/
  int mode = MeasureSpec.getMode(widthMeaSpec) ;
  /*
  * 獲取當前View的測量值,這裡得到的只是初步的值,
  * 我們還需根據測量模式來確定我們期望的大小
  * */
  int size = MeasureSpec.getSize(widthMeaSpec) ;
  /*
  * 如果,模式為精確模式
  * 當前View的寬度,就是我們
  * 的size ;
  * */
  if(mode == MeasureSpec.EXACTLY){
   width = size ;
  }else {
   /*否則的話我們就需要結合padding的值來確定*/
   int desire = size + getPaddingLeft() + getPaddingRight() ;
   if(mode == MeasureSpec.AT_MOST){
    width = Math.min(desire,size) ;
   }else {
    width = desire ;
   }
  }
  mViewWidth = width ;
  return width ;
 }

以上是測量寬度的代碼,其測量高度的代碼,跟測量寬度的代碼大致雷同,就不貼出來了,我會在最後附上源碼。

2、實現onDraw(Canvas c)方法,這個方法相信大家都非常熟悉,就是把這些索引的內容繪制到 View 上顯示出來,包括選中的時候背景顏色的變化;

 @Override
 protected void onDraw(Canvas canvas) {
  if(mTouched){
   canvas.drawColor(0x30000000);
  }
  for (int i = 0 ; i < mIndex.length ; i ++){
   mPaint.setColor(0xff000000);
   mPaint.setTextSize(mTextSize * 3.0f / 4.0f);
   mPaint.setTypeface(Typeface.DEFAULT) ;
   mPaint.getTextBounds(mIndex[i],0,mIndex[i].length(),mTextBound);
   float formX = mViewWidth/2.0f - mTextBound.width()/2.0f ;
   float formY = mTextSize*i + mTextSize/2.0f + mTextBound.height()/2.0f ;
   canvas.drawText(mIndex[i],formX,formY,mPaint);
   mPaint.reset();
  }
 }

我來講一下 onDraw 方法中大致做了什麼事,第一,繪制背景顏色,注意不是一上來就繪制,而是等到有手指觸摸的時候就繪制背景顏色,第二,就是繪制索引的內容,這裡需要根據當前 View 的寬和高來決定繪制內容的大小,和位置。

3、onTouchEvent(MotionEvent event)方法的實現

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  float y = event.getY() ;
  int index = (int) (y / mTextSize);
  if(index >= 0 && index < mIndex.length){
   Log.v("zgy","======index======="+index) ;
   selectItem(index);
  }
  if(event.getAction() == MotionEvent.ACTION_MOVE){
   mTouched = true ;
  }else if (event.getAction() == MotionEvent.ACTION_MOVE){
  }else {
   mFloatView.setVisibility(INVISIBLE);
   mTouched = false ;
  }
  invalidate();
  /*過濾點其他觸摸事件*/
  return true;
 }

代碼也相對比較簡單,首先獲取當前觸摸的點,根據點的坐標來獲取索引的位置,從而拿到索引的位置。

4、到這裡其實就已經實現了我們想要的效果,但是這樣我們還是無法運用它,這裡就需要定義一個回調接口

 /*定義一個回調接口*/
 public interface OnIndexSelectListener{
  /*返回選中的位置,和對應的索引名*/
  void onItemSelect(int position, String value) ;
 }

回調接口我們放在哪裡調用呢,當我們手指按下的時候,這時候其實我們需要確定我們按下的是哪個索引,滑動的時候也是一樣,所以,這個沒什麼好商量的,直接放在onTouchEvent(MotionEvent event)中就可以,

  float y = event.getY() ;
  int index = (int) (y / mTextSize);
  if(index >= 0 && index < mIndex.length){
   Log.v("zgy","======index======="+index) ;
   selectItem(index);
  }

selectItem(int)方法中就是執行的回調方法。

5、實現懸浮框顯示已經選中的索引內容

這裡需要用到 WindowManager 容器,然需要現實的 View 附在這上面的就行,當手指按下的時候,讓 View 顯示出來,松開不顯示就行了

  /*設置浮動選中的索引*/
  /*獲取windowManager*/
  mWindowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
  /*overly 視圖,通過LayoutInflater 獲取*/
  mFloatView = LayoutInflater.from(getContext()).inflate(R.layout.overlay_indexview,null) ;
  /*開始讓其不可見*/
  mFloatView.setVisibility(INVISIBLE);
  /*轉換 高度 和寬度為Sp*/
  mOverlyWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,70,getResources().getDisplayMetrics()) ;
  mOverlyHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,70,getResources().getDisplayMetrics()) ;
  post(new Runnable() {
   @Override
   public void run() {
    WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(mOverlyWidth,mOverlyHeight,
      WindowManager.LayoutParams.TYPE_APPLICATION,
      WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
      PixelFormat.TRANSLUCENT) ;
    mWindowManager.addView(mFloatView,layoutParams);
   }
  }) ;

同樣的道理,如果需要改變顯示的內容,就需要在調用回調的位置,為 View 中的 TextView 設置當前的索引內容。

好了此 View 的代碼就這麼多,

接下來就把引用他的 Xml 和浮動 View 的 Xml 也貼出來,

引用的布局文件

 <moon.wechat.view.IndexView
  android:layout_width="25dp"
  android:layout_height="match_parent"
  android:layout_alignParentRight="true"/>

浮動 View 的布局文件

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/overly_text"
 android:layout_width="70dp"
 android:layout_height="70dp"
 android:text="A"
 android:gravity="center"
 android:background="@drawable/bg_overly_text"
 android:textSize="40sp"
 android:textColor="#ffffffff"
 android:layout_gravity="center">
</TextView>

浮動 View 的背景

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
 <item>
  <shape>
   <solid android:color="#88000000"/>
   <corners android:radius="5dp"/>
  </shape>
 </item>
</layer-list>

以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持本站!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved