Android手機(jī)聯(lián)系人快速索引(手機(jī)通訊錄)
最近需要實(shí)現(xiàn)一個(gè)手機(jī)通訊錄的快速索引功能。根據(jù)姓名首字母快速索引功能。下面是一個(gè)手機(jī)聯(lián)系人快速索引的效果,總體來(lái)說(shuō)代碼不算難,拼音轉(zhuǎn)換的地方略有復(fù)雜。下面上源碼:源碼中有注釋。
下面是效果圖:
MainActivity:
import java.util.ArrayList; import java.util.Collections; import java.util.List; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; /** * 這里是主布局 * @author lxd * */ public class MainActivity extends Activity { private ListView lv_main; private FriendAdapter adapter; private List<Friend> data = new ArrayList<Friend>(); private QuickIndexView qiv_main; private TextView tv_main_word; private Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { //隱藏word tv_main_word.setVisibility(View.GONE); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv_main = (ListView) findViewById(R.id.lv_main); qiv_main = (QuickIndexView) findViewById(R.id.qiv_main); tv_main_word = (TextView) findViewById(R.id.tv_main_word); //設(shè)置監(jiān)聽(tīng) qiv_main.setOnIndexChangedListener(new QuickIndexView.OnIndexChangedListener() { @Override public void onIndexChanged(String word) { tv_main_word.setText(word); tv_main_word.setVisibility(View.VISIBLE); //handler.removeMessages(1); //移除未處理的消息 handler.removeCallbacksAndMessages(null); //發(fā)延遲消息 handler.sendEmptyMessageDelayed(1, 2000); //滑動(dòng)listview //查找對(duì)應(yīng)的item for(int i=0;i<data.size();i++) { String fWord = data.get(i).getPinyin().substring(0, 1); if(word.equals(fWord)) { lv_main.setSelection(i); return; } } } @Override public void onUp() { //tv_main_word.setVisibility(View.GONE); } }); //顯示列表 adapter = new FriendAdapter(); initData(); lv_main.setAdapter(adapter); //lv_main.setSelection(5); } private void initData() { data.add(new Friend("張三")); data.add(new Friend("楊九")); data.add(new Friend("胡繼群")); data.add(new Friend("劉暢")); data.add(new Friend("鐘澤興")); data.add(new Friend("尹革新")); data.add(new Friend("安傳鑫")); data.add(new Friend("張騫壬")); data.add(new Friend("溫松")); data.add(new Friend("李鳳秋")); data.add(new Friend("劉甫")); data.add(new Friend("婁全超")); data.add(new Friend("張猛")); data.add(new Friend("王英杰")); data.add(new Friend("李振南")); data.add(new Friend("孫仁政")); data.add(new Friend("唐春雷")); data.add(new Friend("牛鵬偉")); data.add(new Friend("姜宇航")); data.add(new Friend("劉挺")); data.add(new Friend("張洪瑞")); data.add(new Friend("張建忠")); data.add(new Friend("侯亞帥")); data.add(new Friend("劉帥")); data.add(new Friend("喬競(jìng)飛")); data.add(new Friend("徐雨健")); data.add(new Friend("吳亮")); data.add(new Friend("王兆霖")); data.add(new Friend("阿三")); Collections.sort(data); } class FriendAdapter extends BaseAdapter { @Override public int getCount() { return data.size(); } @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if(convertView==null) { holder = new ViewHolder(); convertView = View.inflate(MainActivity.this, R.layout.item_main, null); holder.wordTV = (TextView) convertView.findViewById(R.id.tv_item_word); holder.nameTV = (TextView) convertView.findViewById(R.id.tv_item_name); convertView.setTag(holder);//***********? } else { holder = (ViewHolder) convertView.getTag(); } Friend friend = data.get(position); String word = friend.getPinyin().substring(0, 1); holder.wordTV.setText(word); holder.nameTV.setText(friend.getName()); //下標(biāo)為0的顯示 if(position==0) { holder.wordTV.setVisibility(View.VISIBLE); } else { //取出上一個(gè)friend, 并得到的第一個(gè)word String preWord = data.get(position-1).getPinyin().substring(0, 1); //判斷是否于當(dāng)前行的word是否相同 //如果相同, 隱藏 if(word.equals(preWord)) { holder.wordTV.setVisibility(View.GONE); } else { //如果不同, 顯示 holder.wordTV.setVisibility(View.VISIBLE); } } return convertView; } class ViewHolder { public TextView wordTV; public TextView nameTV; } } }
主布局:
<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" tools:context="${relativePackage}.${activityClass}" > <ListView android:id="@+id/lv_main" android:layout_width="match_parent" android:layout_height="match_parent" > </ListView> <!-- com.atguigu.quickindex.QuickIndexView --> <com.atguigu.quickindex.QuickIndexView android:id="@+id/qiv_main" android:layout_width="40dp" android:layout_height="match_parent" android:layout_alignParentRight="true" android:background="#ffffff" > </com.atguigu.quickindex.QuickIndexView> <TextView android:id="@+id/tv_main_word" android:layout_width="100dp" android:layout_height="100dp" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:background="#66666666" android:text="A" android:textSize="40sp" android:gravity="center" android:visibility="gone"/> </RelativeLayout>
Item:
<?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="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/tv_item_word" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="A" android:background="#66666666" android:textSize="18sp" android:padding="5dp"/> <TextView android:id="@+id/tv_item_name" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="某人" android:textSize="18sp" android:padding="5dp"/> </LinearLayout>
自定義View:
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; /** * 這里是自定義View * @author lxd * */ public class QuickIndexView extends View { private float itemWidth; private float itemHeight; // private float wordWidth; // private float wordHeight; private String[] indexArr = { "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 Paint paint; public QuickIndexView(Context context, AttributeSet attrs) { super(context, attrs); paint = new Paint(); paint.setColor(Color.WHITE); paint.setTextSize(16); paint.setAntiAlias(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); itemWidth = this.getMeasuredWidth(); itemHeight = this.getMeasuredHeight() / 26f; } @Override protected void onDraw(Canvas canvas) { //當(dāng)每次觸發(fā)重繪的時(shí)候,就把26個(gè)字母循環(huán)一遍 for (int i = 0; i < indexArr.length; i++) { String word = indexArr[i]; // 設(shè)置文字的顏色 if (i == touchIndex) { //這里設(shè)置被點(diǎn)擊的字母變化:顏色變灰色、字體變25sp paint.setColor(Color.GRAY); paint.setTextSize(25); } else { //其他沒(méi)被點(diǎn)擊的字母,保持原有狀態(tài):設(shè)置顏色、字體大小為18sp paint.setColor(Color.BLACK); paint.setTextSize(18); } // 得到word的寬高 Rect bounds = new Rect(); paint.getTextBounds(word, 0, word.length(), bounds); //得到字體的寬 int wordWidth = bounds.width(); //得到字體的高 int wordHeight = bounds.height(); // 計(jì)算word的左上角的坐標(biāo):字母所在的X坐標(biāo)、Y坐標(biāo) float x = itemWidth / 2 - wordWidth / 2; float y = itemHeight / 2 + wordHeight / 2 + i * itemHeight; // 繪制word canvas.drawText(word, x, y, paint); } } // /////////////////////////////////////////////////////////////////////// private int touchIndex = -1;// 觸摸的字母的下標(biāo) @Override public boolean onTouchEvent(MotionEvent event) { // 得到事件坐標(biāo) float eventY = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: // 計(jì)算下標(biāo) int index = (int) (eventY / itemHeight); if (index > 25) { index = 25; } if (index < 0) { index = 0; } // 如果下標(biāo)有改變, 強(qiáng)制重繪 if (index != touchIndex) { // 更新touchIndex touchIndex = index; // 強(qiáng)制重繪 invalidate(); // 通知Activity更新TextView if (onIndexChangedListener != null) { onIndexChangedListener.onIndexChanged(indexArr[index]); } } break; case MotionEvent.ACTION_UP: touchIndex = -1; // 強(qiáng)制重繪 invalidate(); // 通知Activity更新TextView if (onIndexChangedListener != null) { onIndexChangedListener.onUp(); } break; default: break; } return true;// 所有的事件都由當(dāng)前視圖消費(fèi) } private OnIndexChangedListener onIndexChangedListener; /* * 設(shè)置監(jiān)聽(tīng)對(duì)象的方法 這個(gè)方法一般是Activity調(diào)用 */ public void setOnIndexChangedListener( OnIndexChangedListener onIndexChangedListener) { this.onIndexChangedListener = onIndexChangedListener; } interface OnIndexChangedListener { // 當(dāng)操作的下標(biāo)改變時(shí)自動(dòng)調(diào)用 public void onIndexChanged(String word); // 當(dāng)up時(shí)調(diào)用 public void onUp(); } }
聯(lián)系人類:
/** * 聯(lián)系人類 * @author lxd * */ public class Friend implements Comparable<Friend> { private String name; private String pinyin; public Friend(String name) { super(); this.name = name; pinyin = PinYinUtils.getPinYin(name); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPinyin() { return pinyin; } public void setPinyin(String pinyin) { this.pinyin = pinyin; } @Override public String toString() { return "Friend [name=" + name + ", pinyin=" + pinyin + "]"; } @Override public int compareTo(Friend another) { return this.pinyin.compareTo(another.getPinyin()); } }
工具類:用于將漢字轉(zhuǎn)換為拼音
/** * 將漢字轉(zhuǎn)換為拼音 * @author lxd * */ public class PinYinUtils { /** * 得到指定漢字的拼音 * 注意:不應(yīng)該被頻繁調(diào)用,它消耗一定內(nèi)存 * @param hanzi * @return */ public static String getPinYin(String hanzi){ String pinyin = ""; HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();//控制轉(zhuǎn)換是否大小寫(xiě),是否帶音標(biāo) format.setCaseType(HanyuPinyinCaseType.UPPERCASE);//大寫(xiě) format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); //由于不能直接對(duì)多個(gè)漢字轉(zhuǎn)換,只能對(duì)單個(gè)漢字轉(zhuǎn)換 char[] arr = hanzi.toCharArray(); for (int i = 0; i < arr.length; i++) { if(Character.isWhitespace(arr[i]))continue;//如果是空格,則不處理,進(jìn)行下次遍歷 //漢字是2個(gè)字節(jié)存儲(chǔ),肯定大于127,所以大于127就可以當(dāng)為漢字轉(zhuǎn)換 if(arr[i]>127){ try { //由于多音字的存在,單 dan shan String[] pinyinArr = PinyinHelper.toHanyuPinyinStringArray(arr[i], format); if(pinyinArr!=null){ pinyin += pinyinArr[0]; }else { pinyin += arr[i]; } } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); //不是正確的漢字 pinyin += arr[i]; } }else { //不是漢字, pinyin += arr[i]; } } return pinyin; } }
以上代碼是關(guān)于Android手機(jī)聯(lián)系人快速索引(手機(jī)通訊錄)的全部敘述,希望大家喜歡。
- 淺析Android位置權(quán)限以及數(shù)組尋找索引的坑
- android仿微信聯(lián)系人索引列表功能
- Android ItemDecoration 實(shí)現(xiàn)分組索引列表的示例代碼
- android仿微信通訊錄搜索示例(匹配拼音,字母,索引位置)
- Android 實(shí)現(xiàn)帶字母索引的側(cè)邊欄功能
- Android自定義View實(shí)現(xiàn)通訊錄字母索引(仿微信通訊錄)
- Android通用索引欄實(shí)現(xiàn)代碼
- Android手機(jī)聯(lián)系人帶字母索引的快速查找
- android將搜索引擎設(shè)置為中國(guó)雅虎無(wú)法搜索問(wèn)題解決方法
- android 左右滑動(dòng)+索引圖標(biāo)實(shí)現(xiàn)方法與代碼
- Android 解決游戲發(fā)行切包資源索引沖突的問(wèn)題
相關(guān)文章
Android App開(kāi)發(fā)中自定義View和ViewGroup的實(shí)例教程
這篇文章主要介紹了Android App開(kāi)發(fā)中自定義View和ViewGroup的實(shí)例教程,分別介紹了進(jìn)度條和圖片上傳并排列的例子,效果很好很強(qiáng)大,需要的朋友可以參考下2016-05-05Android事件分發(fā)之View事件處理關(guān)鍵及示例分析
這篇文章主要為大家介紹了Android事件分發(fā)之View事件處理關(guān)鍵及示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02基于Viewpager2實(shí)現(xiàn)登錄注冊(cè)引導(dǎo)頁(yè)面
這篇文章主要為大家詳細(xì)介紹了基于Viewpager2實(shí)現(xiàn)登錄注冊(cè)引導(dǎo)頁(yè)面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09Android?手寫(xiě)RecyclerView實(shí)現(xiàn)列表加載
這篇文章主要介紹了Android?手寫(xiě)RecyclerView實(shí)現(xiàn)列表加載,涉及到列表的需求,肯定第一時(shí)間想到RecyclerView,即便是自定義View,那么RecyclerView也會(huì)是首選,為什么會(huì)選擇RecyclerView而不是ListView,主要就是RecyclerView的內(nèi)存復(fù)用機(jī)制,這也是RecyclerView的核心?2022-08-08Android之RecyclerView實(shí)現(xiàn)時(shí)光軸效果示例
本篇文章主要介紹了Android之RecyclerView實(shí)現(xiàn)時(shí)光軸效果,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02Android實(shí)現(xiàn)斷點(diǎn)續(xù)傳功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)斷點(diǎn)續(xù)傳功能,能在上次的斷點(diǎn)處繼續(xù)上傳,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07Android編程實(shí)現(xiàn)震動(dòng)與振鈴的方法詳解
這篇文章主要介紹了Android編程實(shí)現(xiàn)震動(dòng)與振鈴的方法,結(jié)合實(shí)例形式分析了Android實(shí)現(xiàn)震動(dòng)與振鈴的Vibrator類及MediaPlayer類相關(guān)使用技巧,需要的朋友可以參考下2018-03-03Android開(kāi)發(fā)之ContentProvider的使用詳解
本篇文章介紹了Android開(kāi)發(fā)之ContentProvider的使用詳解。需要的朋友參考下2013-04-04簡(jiǎn)單實(shí)現(xiàn)Android本地音樂(lè)播放器
這篇文章主要為大家詳細(xì)介紹了如何簡(jiǎn)單實(shí)現(xiàn)Android本地音樂(lè)播放器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05