Android自定義控制元件---聯絡人列表A-Z排序
阿新 • • 發佈:2019-01-05
這幾天在做IM模組,設計圖要求做一個類似下圖所示的自定義控制元件。
我百度了一下,發現類似的Ddmo有很多,但是還不能完全滿足設計圖的需求。 參考了幾個比較有價值的demo琢磨了一天總算做出來了,現在發出來和大家分享。 分析一下這個需求的難點。 1、右邊側滑欄(SideBar)控制元件繪製。 2、將列表中的中文暱稱轉化為拼音列表。(這個問題用jpinyin解決。) 3、滑動側滑欄(SideBar)的過程中如何與ListView列表建立對應的聯絡? 如果能夠解決這三個問題那麼這個需求也就迎刃而解了。 接下來看看整個Demo的結構圖 一、SideBar控制元件的繪製 1、首先建立一個名為SideBar的類並且繼承View。在這個類中分別重寫onMeasure,onDraw,onTouchEvent三個方法。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec. getMode(heightMeasureSpec) ; int finalWidth = 0, finalHeight = 0; // 測量單個字元的寬度和高度 float charWidthAndHeight = paint.measureText( letter.get(0 )) + letterSpace; if (widthMode == MeasureSpec. EXACTLY) { finalWidth = MeasureSpec.getSize (widthMeasureSpec); } else if (widthMode == MeasureSpec.AT_MOST) { finalWidth = (int ) charWidthAndHeight + getPaddingLeft() + getPaddingRight(); } if (heightMode == MeasureSpec.EXACTLY) { finalHeight = MeasureSpec.getSize (heightMeasureSpec); } else if (heightMode == MeasureSpec.AT_MOST) { // 注意measureText的值與 paint.setTextSize的值有關 finalHeight = ( int) charWidthAndHeight * letter .size() + getPaddingBottom() + getPaddingTop(); } setMeasuredDimension(finalWidth, finalHeight); }
(2)在測量好這個檢視的大小之後,接下來就是在onDraw方法中繪製自己想要的檢視。
@Override
protected void onDraw(Canvas canvas) {
//27 個字元(包含 #)均分整個檢視的高度,例如檢視高度為 270,270/27 均分之後,每個字元的y座標為10。
y = getHeight() / letter.size() ;
for ( int i = 0 ; i < letter .size(); i++) {
Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
// 計算繪製字元 X的座標,整個檢視寬度的一半減去字元寬度的一半
x = getWidth() / 2 - ( int) paint.measureText(letter .get(i)) / 2;
// int correctY=y*i+y;
// int correctY = y * i + y / 2;
int correctY = ((y * i + y) + (y * i) - fontMetricsInt.bottom - fontMetricsInt.top) / 2;
String tempString = isLetterUpper ? letter.get(i).toUpperCase() : letter .get(i).toLowerCase();
canvas.drawText(tempString , x, correctY, paint );
// canvas.drawLine(0, y * i + y, 100, y * i + y, paint);
}
}
關於繪製字元X座標的求法比較簡單,註釋已經解釋得很清楚就不再分析。
主要分析y座標的求法。我百度了一些demo總結髮現有兩種關於字元y座標的求法。
(為了便於觀察,我暫時將SideBar的背景改為了淺藍色,並且每個字元的都用橫線分隔)
第一種是int correctY=y*i+y。執行一下程式發現字元的繪製明顯向下偏移(如下圖所示)
第二種是int correctY=y*i+y/2。再執行程式,發現還是有點偏移。我是處女座有強迫症,果斷不能忍啊。
我把計算公式改為int correctY = ((y * i + y) + (y * i) - fontMetricsInt.bottom - fontMetricsInt.top) / 2; 執行程式,完美解決問題。
剛看到這個式子可能不是很理解。我給大家解釋一下,假設我們要繪製D這個字元。如圖所示
這時包含D字元的這個矩形上下邊的縱座標分別是y*i,y*i+y。那麼這個矩形的中間縱座標怎麼算呢?答案就是((y*i)+(y*i+y))/2 得到這個中間值之後,我們直接呼叫繪製方法canvas.drawText(tempString, x,((y*i)+(y*i+y))/2 , paint);發現還是出問題了 字元還是畫在矩形偏上位置(如下圖) 為什麼會這樣?這是因為canvas.drawText方法在繪製文字的時候並不是從文字的中間(下圖的黃線)開始 ,而且是從文字的中間偏下位置,即圖中綠色的baseLine這條線開始繪製。 明白了這些知識點之後就好處理了。(FontMetricInt.top+FontMetricInt.bottom)/2就是這邊黃線的縱座標(注意FontMetricInt.top是負值)。 綜合起來:矩形縱座標的值減去黃線縱座標的值除以2整理得到 ((y * i + y) + (y * i) - fontMetricsInt.bottom - fontMetricsInt.top) / 2 再呼叫Canvas.drawText方法繪製字元。 (3)整個檢視都測量,繪製好之後。我們還需要新增控制元件的觸控事件。所以還需要重寫onTouchEvent方法。 首先設定回撥介面,向用戶提供兩個方法,分別是showCurrentLetter和hideCurrentLetter。/**
* 回撥介面。
*/
public interface OnCurrentLetterListener {
void showCurrentLetter(String currentLetter);
void hideCurrentLetter ();
}
重寫onTouchEvent方法,首先獲取使用者在點選,移動SideBar檢視時的y座標。 y/getHeight()*letter.size()表示y座標佔整個檢視高度的比例(例如50%) 乘以字元陣列(長度為27)的長度等於下標(約等於13,) switch(event.getAction())是用來判斷使用者觸控手機螢幕的手勢。 當用戶在手機螢幕按壓,移動時MotionEvent.ACTION_DOWN(按壓手勢), MotionEvent.ACTION_MOVE(移動手勢)回撥上面回撥介面的showCurrentLetter方法。 當用戶在手機螢幕擡起時MotionEvent.ACTION_UP(擡起手勢)回撥介面中的hideCurrentLetter方法。
@Override
public boolean onTouchEvent(MotionEvent event) {
float y = event.getY();
// 獲取當前側滑欄字母的下標
float currentLetterIndex = y / getHeight() * letter.size();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if (onCurrentLetterListener != null) {
//對上下邊界對限制
if (currentLetterIndex >= letter.size()) {
currentLetterIndex = letter.size() - 1 ;
} else if (currentLetterIndex < 0) {
currentLetterIndex = 0;
}
onCurrentLetterListener .showCurrentLetter(letter.get(( int) currentLetterIndex));
}
return true;
case MotionEvent. ACTION_UP:
if (onCurrentLetterListener != null) {
onCurrentLetterListener.hideCurrentLetter() ;
}
return true;
default:
return true;
}
}
最後整個方法return true;表示這個觸控事件被SideBar檢視消費了,不再向其它地方傳遞事件。
2、最後貼個SideBar類的程式碼
package per.edward.ui;
import android.content.Context ;
import android.content.res.TypedArray ;
import android.graphics.Canvas ;
import android.graphics.Color ;
import android.graphics.Paint ;
import android.util.AttributeSet ;
import android.view.MotionEvent ;
import android.view.View ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.List ;
/**
* 側滑欄檢視
* Created by Edward on 2016/4/27.
*/
public class SideBar extends View {
private String[] letterStrings = { "#", "A" , "B", "C", "D" , "E", "F" , "G", "H", "I" , "J", "K", "L" , "M", "N", "O" , "P", "Q", "R", "S" , "T", "U", "V" , "W", "S", "Y" , "Z"} ;
private Paint paint;
// 字母列表
private List<String> letter;
// 繪製字母的 x,y座標
private int x, y ;
// 字母的間距
private int letterSpace = 0 ;
// 字母是否大寫
private boolean isLetterUpper = true;
private OnCurrentLetterListener onCurrentLetterListener;
public void setLetter (List<String> letter) {
this .letter = letter ;
}
// 設定回撥介面
public void setOnCurrentLetterListener(OnCurrentLetterListener onCurrentLetterListener) {
this .onCurrentLetterListener = onCurrentLetterListener ;
}
public SideBar(Context context) {
this (context, null) ;
}
public SideBar(Context context, AttributeSet attrs) {
super (context, attrs) ;
if ( letter == null ) {
letter = new ArrayList<>() ;
letter = Arrays.asList(letterStrings );
}
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SideBar) ;
letterSpace = typedArray.getInt(R.styleable.SideBar_SB_Letter_Space, 0);
isLetterUpper = typedArray.getBoolean(R.styleable.SideBar_SB_Is_Letter_Upper, true);
typedArray.recycle() ;
paint = new Paint() ;
paint .setColor(Color.BLACK) ;
paint .setAntiAlias(true) ;
paint .setTextSize(30) ;//3CAC48
paint .setColor(Color.parseColor("#ffffff" ));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec. getMode(heightMeasureSpec) ;
int finalWidth = 0, finalHeight = 0;
// 測量單個字元的寬度和高度
float charWidthAndHeight = paint.measureText( letter.get(0 )) + letterSpace;
if (widthMode == MeasureSpec. EXACTLY) {
finalWidth = MeasureSpec.getSize(widthMeasureSpec);
} else if (widthMode == MeasureSpec.AT_MOST) {
finalWidth = (int) charWidthAndHeight + getPaddingLeft() + getPaddingRight();
}
if (heightMode == MeasureSpec.EXACTLY) {
finalHeight = MeasureSpec.getSize(heightMeasureSpec) ;
} else if (heightMode == MeasureSpec.AT_MOST) {
// 注意measureText的值與 paint.setTextSize的值有關
finalHeight = ( int) charWidthAndHeight * letter .size() + getPaddingBottom() + getPaddingTop();
}
// Log.e("--------------->", MeasureSpec.getSize(widthMeasureSpec) + " " + MeasureSpec.getSize(heightMeasureSpec));
setMeasuredDimension(finalWidth , finalHeight);
}
@Override
protected void onDraw(Canvas canvas) {
setBackgroundColor(Color.parseColor ("#31b2f7" ));
//27 個字元(包含 #)均分整個檢視的高度,例如檢視高度為 270,270/27 均分之後,每個字元的 y座標為10
y = getHeight() / letter.size() ;
Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
for ( int i = 0 ; i < letter .size(); i++) {
// 計算繪製字元 X的座標,整個檢視寬度的一半減去字元寬度的一半
x = getWidth() / 2 - ( int) paint.measureText(letter .get(i)) / 2;
// int correctY=y*i+y;
// int correctY = y * i + y / 2;
int correctY = ((y * i + y) + (y * i) - fontMetricsInt.bottom - fontMetricsInt.top) / 2;
String tempString = isLetterUpper ? letter.get(i).toUpperCase() : letter .get(i).toLowerCase();
canvas.drawText(tempString , x, ((y * i) + ( y * i + y)) / 2 , paint) ;
canvas.drawLine( 0, y * i + y, 100, y * i + y, paint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float y = event.getY();
// 獲取當前側滑欄字母的下標
float currentLetterIndex = y / getHeight() * letter.size();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if ( onCurrentLetterListener != null ) {
//對上下邊界對限制
if (currentLetterIndex >= letter.size()) {
currentLetterIndex = letter.size() - 1 ;
} else if (currentLetterIndex < 0) {
currentLetterIndex = 0;
}
onCurrentLetterListener .showCurrentLetter(letter.get(( int) currentLetterIndex));
}
return true;
case MotionEvent. ACTION_UP:
if ( onCurrentLetterListener != null ) {
onCurrentLetterListener.hideCurrentLetter() ;
}
return true;
default:
return true;
}
}
/**
* 回撥介面。
*/
public interface OnCurrentLetterListener {
void showCurrentLetter(String currentLetter);
void hideCurrentLetter ();
}
}
3、SideBar類寫完之後,我們就可以建立一個XML檔案命名為activity_main, 在裡面用上我們剛才寫的自定義控制元件。整個XML程式碼佈局很簡單。 最外層是一個RelativeLayout容器。 中間放了一個TextView控制元件,預設為隱藏狀態。 將SideBar右置垂直居中。
<? xml version="1.0" encoding= "utf-8"?>
<RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android"
xmlns: app="http://schemas.android.com/apk/res-auto"
android :layout_width="match_parent"
android :layout_height="match_parent">
<TextView
android :id="@+id/txt_show_current_letter"
android :layout_width="100dp"
android :layout_height="wrap_content"
android :layout_centerInParent="true"
android :background="#3CAC48"
android :gravity="center_vertical|center_horizontal"
android :text="A"
android :textColor="#ffffff"
android :textSize="50dp"
android :visibility="gone" />
<per.edward.ui.SideBar
android :id="@+id/side_bar"
android :layout_width="30dp"
android :layout_height="150dp"
android :layout_alignParentRight="true"
android :layout_centerVertical="true" />
</RelativeLayout>
4、最後再建立一個主體類,命名為MainActivity並且繼承Activity。首先例項化TextView和SideBar兩個控制元件。 之後用SideBar的例項。設定setOnCurrentLetterListener的回撥介面,在這個介面需要實現兩個方法分別是showCurrentLetter,hideCurrentLetter。前者用來顯示TextView,並且設定當前字母值。後者用來隱藏TextView。具體操作看下面程式碼。簡單吧?
package per.edward.ui;
import android.app.Activity ;
import android.os.Bundle ;
import android.view.View ;
import android.widget.TextView ;
/**
* author:Edward
*/
public class MainActivity extends Activity {
private TextView txtShowCurrentLetter;
private SideBar sideBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState) ;
setContentView(R.layout. activity_main);
txtShowCurrentLetter = (TextView) findViewById(R.id.txt_show_current_letter );
sideBar = (SideBar) findViewById(R.id. side_bar);
setCallbackInterface() ;
}
/**
* 設定回撥介面
*/
public void setCallbackInterface() {
// 回撥介面
sideBar .setOnCurrentLetterListener(new SideBar.OnCurrentLetterListener() {
@Override
public void showCurrentLetter(String currentLetter) {
txtShowCurrentLetter .setVisibility(View.VISIBLE) ;
txtShowCurrentLetter.setText(currentLetter) ;
}
@Override
public void hideCurrentLetter() {
txtShowCurrentLetter.setVisibility(View. GONE);
}
});
}
}
5、最後實現的效果圖如下。
分享到這裡整個demo的流程已經走到一半了。 下面會講講ListView與SideBar控制元件的聯動處理。 二、建立側滑欄SideBar與ListView的對應關係 1、首先建立一個聯絡人的實體類,在這個實體類中,定義了兩個變數firstLetter(用來儲存聯絡人拼音的第一個字母)和name(聯絡人的名字)。
package per.edward.ui;
/**
* 聯絡人列表實體類
* Created by Edward on 2016/4/26.
*/
public class ContactsModel {
private String firstLetter;
private String name;
public String getFirstLetter() {
return firstLetter;
}
public void setFirstLetter(String firstLetter) {
this .firstLetter = firstLetter ;
}
public String getName() {
return name;
}
public void setName(String name) {
this .name = name ;
}
}
2、建立一個名為adapter_side_bar的XML佈局。 這個佈局將在MainActivity類中通過例項化myAdapter = new SideBarAdapter(this, list, R.layout.adapter_side_bar); 的方法傳遞給SideBarAdapter類。
<? xml version="1.0" encoding= "utf-8"?>
<LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android"
android :layout_width="match_parent"
android :layout_height="match_parent"
android :orientation="vertical">
<TextView
android :id="@+id/txt_letter_category"
android :layout_width="match_parent"
android :layout_height="30dp"
android :background="#d7d6d6"
android :padding="5dp"
android :text="A"
android :textColor="#000000"
android :textSize="16dp" />
<LinearLayout
android :layout_width="match_parent"
android :layout_height="wrap_content"
android :background="#ffffff"
android :orientation="horizontal">
<ImageView
android :id="@+id/image"
android :layout_width="50dp"
android :layout_height="50dp"
android :padding="5dp"
android :src="@mipmap/ic_launcher" />
<TextView
android :id="@+id/txt_name"
android :layout_width="wrap_content"
android :layout_height="wrap_content"
android :layout_gravity="center_vertical"
android :text="姓名 "
android :textSize="14dp" />
</LinearLayout>
</LinearLayout>
這個佈局的預覽圖是這樣滴。 SideBarAdapter接收到此佈局之後,會通過convertView = LayoutInflater.from(context).inflate(mItemLayoutId, null);載入佈局。 並且在getView方法中動態在控制字母標題的顯示或隱藏。 3、接著建立一個名為SideBarAdapter類的介面卡並且繼承BaseAdapter。 (1)構造方法中呼叫traverseList方法。這個方法的演算法是這樣的。此時mDatas列表所有的getFirstLetter 欄位已經排序成 ###ABCDEFLLLLMMMQTTZZ。#有三個,for迴圈。 第一次currentd的值是#把current的值作為鍵並且記錄它的下標,即map.put(current, i)。 第二次current的值是#不記錄,第三次current的值還是#不記錄。 到第四次此時current更新為A,把current作為鍵並且記錄下標。 第五次此時current更新為B,把current作為鍵並且記錄下標....一直到for迴圈結束為止。 這時map的集合已經記錄了整個mDatas列表的字母標題和下標。我們只需要在getView方法(這個方法會傳進來一個position)中判斷當前的position是否等於map的下標值。如果相等就將字母標題顯示出來。否則隱藏
package per.edward.ui;
import android.content.Context ;
import android.view.LayoutInflater ;
import android.view.View ;
import android.view.ViewGroup ;
import android.widget.BaseAdapter ;
import android.widget.TextView ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Map ;
/**
* 側欄介面卡
* Created by Edward on 2016/4/27.
*/
public class SideBarAdapter extends BaseAdapter {
// 是否第一個 Item的
private boolean isFirstItemLetter = true;
// 記錄是否顯示字母標題,鍵為字母,值為下標
private Map<String, Integer> map;
private List<ContactsModel> mDatas;
private int mItemLayoutId ;
private Context context;
public SideBarAdapter(Context mContext , List<ContactsModel> mDatas, int mItemLayoutId) {
this .mDatas = mDatas ;
map = new HashMap<>() ;
this. mItemLayoutId = mItemLayoutId;
this. context = mContext;
traverseList() ;
}
/**
* 遍歷列表
* 由於傳進來的 mDatas是一個已排好序的列表,遍歷整個列表,每遇到分類的第一個字母就把下標記錄下來
*/
public void traverseList() {
// 獲取初始值
String current = mDatas.get(0 ).getFirstLetter();
for ( int i = 0 ; i < mDatas .size(); i++) {
char tempChar = mDatas.get(i).getFirstLetter().charAt(0) ;
String tempFirstLetter = mDatas.get(i).getFirstLetter();
if (tempFirstLetter.equals(current) || (tempChar < 'A' || tempChar > 'Z' )) {
if ( isFirstItemLetter) {
map.put(current , i);
}
} else {
//更新初始值
current = mDatas .get(i).getFirstLetter();
map.put(current , i);
}
isFirstItemLetter = false;
}
}
/**
* 獲取當前字母的下標
*
* @return
*/
public int getCurrentLetterPosition(String currentLetter) {
if (map.get(currentLetter) != null) {
return map.get(currentLetter);
} else
return - 1;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem( int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView( int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder() ;
convertView = LayoutInflater.from( context).inflate(mItemLayoutId , null);
viewHolder. txtFirstLetter = (TextView) convertView.findViewById(R.id.txt_letter_category) ;
viewHolder. txtName = (TextView) convertView.findViewById(R.id.txt_name) ;
convertView.setTag(viewHolder) ;
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
// 判斷是否顯示字母標題
if (map.get( mDatas.get(position).getFirstLetter()) != null && map.get(mDatas .get(position).getFirstLetter()).equals(position)) {
viewHolder.txtFirstLetter .setVisibility(View.VISIBLE) ;
viewHolder.txtFirstLetter.setText( mDatas.get(position).getFirstLetter());
} else {
viewHolder.txtFirstLetter .setVisibility(View.GONE) ;
}
viewHolder.txtName .setText(mDatas.get(position).getName()) ;
return convertView ;
}
public class ViewHolder {
TextView txtFirstLetter , txtName;
}
}
4、記得在倒回剛才的activity_main的XML檔案新增ListView控制元件
<? xml version="1.0" encoding= "utf-8"?>
<RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android"
xmlns: app="http://schemas.android.com/apk/res-auto"
android :layout_width="match_parent"
android :layout_height="match_parent"
android :background="#ffffff">
<ListView
android :id="@+id/list_view"
android :layout_width="match_parent"
android :layout_height="match_parent" />
<TextView
android :id="@+id/txt_show_current_letter"
android :layout_width="100dp"
android :layout_height="wrap_content"
android :layout_centerInParent="true"
android :background="#3CAC48"
android :gravity="center_vertical|center_horizontal"
android :text="A"
android :textColor="#ffffff"
android :textSize="50dp"
android :visibility="gone" />
<per.edward.ui.SideBar
android :id="@+id/side_bar"
android :layout_width="30dp"
android :layout_height="match_parent"
android :layout_alignParentRight="true"
android :layout_centerVertical="true" />
</RelativeLayout>
5、寫完了SideBarAdapter,我們再回到剛才寫的MainActivity類進行相應的設定,包括初始化值聯絡人列表, 聯絡人列表排序,聯絡人暱稱轉化為拼音等。具體程式碼如下所示,比較簡單。就不一一分析了。
package per.edward.ui;
import android.os.Bundle ;
import android.support.v7.app.AppCompatActivity ;
import android.view.View ;
import android.widget.ListView ;
import android.widget.TextView ;
import java.util.ArrayList ;
import java.util.Collections ;
import java.util.Comparator ;
import java.util.HashSet ;
import java.util.List ;
import java.util.Set ;
import opensource.jpinyin.PinyinHelper ;
/**
* author:Edward
* 此 demo的部落格地址:http://blog.csdn.net/u012814441
*/
public class MainActivity extends AppCompatActivity {
private ListView listView;
private TextView txtShowCurrentLetter;
private SideBar sideBar;
private SideBarAdapter myAdapter;
// private List<ContactsModel> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState) ;
setContentView(R.layout. activity_main);
txtShowCurrentLetter = (TextView) findViewById(R.id.txt_show_current_letter );
sideBar = (SideBar) findViewById(R.id. side_bar);
listView = (ListView) findViewById(R.id. list_view);
setCallbackInterface() ;
List<ContactsModel> list = initData() ;
chineseToPinyin(list) ;
// 將聯絡人列表的標題字母排序
Collections. sort(list , new Comparator<ContactsModel>() {
@Override
public int compare(ContactsModel lhs, ContactsModel rhs) {
return lhs.getFirstLetter().compareTo(rhs.getFirstLetter()) ;
}
});
// 將聯絡人列表的標題字母放到 List<String>列表中,準備資料去重
List<String> getLetter = new ArrayList<>();
for ( int i = 0 ; i < list.size(); i++) {
getLetter.add(list.get(i).getFirstLetter());
}
// 資料去重
getLetter = removeDuplicate(getLetter) ;
// 將聯絡人列表的字母標題排序
Collections. sort(getLetter , new Comparator<String>() {
@Override
public int compare(String lhs, String rhs) {
return lhs.compareTo(rhs) ;
}
});
// 設定已排序好的標題
sideBar .setLetter(getLetter);
myAdapter = new SideBarAdapter( this, list, R.layout.adapter_side_bar) ;
listView .setAdapter(myAdapter) ;
}
/**
* 將中文轉化為拼音
*/
public void chineseToPinyin(List<ContactsModel> list) {
for (int i = 0; i < list.size() ; i++) {
ContactsModel contactsModel1 = list.get(i);
// 將漢字轉換為拼音
String pinyinString = PinyinHelper.getShortPinyin(list.get(i).getName()) ;
// 將拼音字串轉換為大寫拼音
String upperCasePinyinString = String.valueOf(pinyinString.charAt( 0)).toUpperCase();
// 獲取大寫拼音字串的第一個字元
char tempChar = upperCasePinyinString.charAt( 0);
if (tempChar < 'A' || tempChar > 'Z' ) {
contactsModel1.setFirstLetter( "#");
} else {
contactsModel1.setFirstLetter(String.valueOf(tempChar)) ;
}
}
}
/**
* 設定回撥介面
*/
public void setCallbackInterface() {
// 回撥介面
sideBar .setOnCurrentLetterListener(new SideBar.OnCurrentLetterListener() {
@Override
public void showCurrentLetter(String currentLetter) {
txtShowCurrentLetter .setVisibility(View.VISIBLE) ;
txtShowCurrentLetter.setText(currentLetter) ;
int position = myAdapter.getCurrentItemPosition(currentLetter);
if (position != -1 )
listView.setSelection(position) ;
}
@Override
public void hideCurrentLetter() {
txtShowCurrentLetter.setVisibility(View. GONE);
}
});
}
/**
* 初始化資料
*/
public List<ContactsModel> initData() {
List<ContactsModel> list = new ArrayList<>();
ContactsModel contactsModel ;
String[] nameStrings = { "覃" , "岑 ", "$ 來啊,來互相傷害啊 ", "疍姬" , "梵蒂岡 ", " 亳州", "佟" , "郄 ", " 張三", "Edward", " 李四", "萌萌噠" , "霾耷 ", " 離散", "趙信" , "啦啦 ", " 辣妹子", "嗷嗷" , "妹妹 ", "']asd" , "%Hello"} ;
for ( int i = 0 ; i < nameStrings.length ; i++) {
contactsModel = new ContactsModel() ;
contactsModel.setName(nameStrings[i]) ;
list.add(contactsModel) ;
}
return list;
}
/**
* 去重資料
*
* @param list
* @param <T>
* @return
*/
public <T> List< T> removeDuplicate (List<T> list) {
Set<T > h = new HashSet<>(list) ;
list.clear() ;
list.addAll(h) ;
return list ;
}
}
6、最後特別注意的是。在專案工程的libs資料夾裡面需要新增jpinyin.jar包(這個jar包可以在下面的demo原始碼中copy出來)。
7、 最終效果圖
三、結束 好累啊,終於寫完了。這是我目前寫過最詳細的部落格了。鼓勵一下唄。如果有不足的地方,歡迎指出。謝謝。