Android系統聯絡人全特效實現 下 ,字母表快速滾動
在上一篇文章中,我和大家一起實現了類似於Android系統聯絡人的分組導航和擠壓動畫功能,不過既然文章名叫做《Android系統聯絡人全特效實現》,那麼沒有快速滾動功能顯然是稱不上"全"的。因此本篇文章我將帶領大家在上篇文章的程式碼基礎上改進,加入快速滾動功能。
其實ListView本身是有一個快速滾動屬性的,可以通過在XML中設定android:fastScrollEnabled="true"來啟用。包括以前老版本的Android聯絡人中都是使用這種方式來進行快速滾動的。效果如下圖所示:
不過這種快速滾動方式比較醜陋,到後來很多手機廠商在定製自己ROM的時候都將預設快速滾動改成了類似iPhone上A-Z字母表快速滾動的方式。這裡我們怎麼能落後於時代的潮流呢!我們的快速滾動也要使用A-Z字母表的方式!
下面就來開始實現,首先開啟上次的ContactsDemo工程,修改activity_main.xml佈局檔案。由於我們要在介面上加入字母表,因此我們需要一個Button,將這個Button的背景設為一張A-Z排序的圖片,然後居右對齊。另外還需要一個TextView,用於在彈出式分組佈局上顯示當前的分組,預設是gone掉的,只有手指在字母表上滑動時才讓它顯示出來。修改後的佈局檔案程式碼如下:
<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" android:orientation="vertical" > <ListView android:id="@+id/contacts_list_view" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentTop ="true" android:scrollbars="none" android:fadingEdge="none" > </ListView> <LinearLayout android:id="@+id/title_layout" android:layout_width="fill_parent" android:layout_height="18dip" android:layout_alignParentTop="true" android:background="#303030" > <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginLeft="10dip" android:textColor="#ffffff" android:textSize="13sp" /> </LinearLayout> <Button android:id="@+id/alphabetButton" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_alignParentRight="true" android:background="@drawable/a_z" /> <RelativeLayout android:id="@+id/section_toast_layout" android:layout_width="70dip" android:layout_height="70dip" android:layout_centerInParent="true" android:background="@drawable/section_toast" android:visibility="gone" > <TextView android:id="@+id/section_toast_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textColor="#fff" android:textSize="30sp" /> </RelativeLayout></RelativeLayout>
然後開啟MainActivity進行修改,毫無疑問,我們需要對字母表按鈕的touch事件進行監聽,於是在MainActivity中新增如下程式碼:private void setAlpabetListener() { alphabetButton.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { float alphabetHeight = alphabetButton.getHeight(); float y = event.getY(); int sectionPosition = (int) ((y / alphabetHeight) / (1f / 27f)); if (sectionPosition < 0) { sectionPosition = 0; } else if (sectionPosition > 26) { sectionPosition = 26; } String sectionLetter = String.valueOf(alphabet.charAt(sectionPosition)); int position = indexer.getPositionForSection(sectionPosition); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: alphabetButton.setBackgroundResource(R.drawable.a_z_click); sectionToastLayout.setVisibility(View.VISIBLE); sectionToastText.setText(sectionLetter); contactsListView.setSelection(position); break; case MotionEvent.ACTION_MOVE: sectionToastText.setText(sectionLetter); contactsListView.setSelection(position); break; default: alphabetButton.setBackgroundResource(R.drawable.a_z); sectionToastLayout.setVisibility(View.GONE); } return true; } });}
可以看到,在這個方法中我們註冊了字母表按鈕的onTouch事件,然後在onTouch方法裡做了一些邏輯判斷和處理,下面我來一一詳細說明。首先通過字母表按鈕的getHeight方法獲取到字母表的總高度,然後用event.getY方法獲取到目前手指在字母表上的縱座標,用縱座標除以總高度就可以得到一個用小數表示的當前手指所在位置(0表在#端,1表示在Z端)。由於我們的字母表中一共有27個字元,再用剛剛算出的小數再除以1/27就可以得到一個0到27範圍內的浮點數,之後再把這個浮點數向下取整,就可以算出我們當前按在哪個字母上了。然後再對event的action進行判斷,如果是ACTION_DOWN或ACTION_MOVE,就在彈出式分組上顯示當前手指所按的字母,並呼叫ListView的setSelection方法把列表滾動到相應的分組。如果是其它的action,就將彈出式分組佈局隱藏。
MainActivity的完整程式碼如下:public class MainActivity extends Activity { /** * 分組的佈局 */ private LinearLayout titleLayout; /** * 彈出式分組的佈局 */ private RelativeLayout sectionToastLayout; /** * 右側可滑動字母表 */ private Button alphabetButton; /** * 分組上顯示的字母 */ private TextView title; /** * 彈出式分組上的文字 */ private TextView sectionToastText; /** * 聯絡人ListView */ private ListView contactsListView; /** * 聯絡人列表介面卡 */ private ContactAdapter adapter; /** * 用於進行字母表分組 */ private AlphabetIndexer indexer; /** * 儲存所有手機中的聯絡人 */ private List<Contact> contacts = new ArrayList<Contact>(); /** * 定義字母表的排序規則 */ private String alphabet = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /** * 上次第一個可見元素,用於滾動時記錄標識。 */ private int lastFirstVisibleItem = -1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); adapter = new ContactAdapter(this, R.layout.contact_item, contacts); titleLayout = (LinearLayout) findViewById(R.id.title_layout); sectionToastLayout = (RelativeLayout) findViewById(R.id.section_toast_layout); title = (TextView) findViewById(R.id.title); sectionToastText = (TextView) findViewById(R.id.section_toast_text); alphabetButton = (Button) findViewById(R.id.alphabetButton); contactsListView = (ListView) findViewById(R.id.contacts_list_view); Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; Cursor cursor = getContentResolver().query(uri, new String[] { "display_name", "sort_key" }, null, null, "sort_key"); if (cursor.moveToFirst()) { do { String name = cursor.getString(0); String sortKey = getSortKey(cursor.getString(1)); Contact contact = new Contact(); contact.setName(name); contact.setSortKey(sortKey); contacts.add(contact); } while (cursor.moveToNext()); } startManagingCursor(cursor); indexer = new AlphabetIndexer(cursor, 1, alphabet); adapter.setIndexer(indexer); if (contacts.size() > 0) { setupContactsListView(); setAlpabetListener(); } } /** * 為聯絡人ListView設定監聽事件,根據當前的滑動狀態來改變分組的顯示位置,從而實現擠壓動畫的效果。 */ private void setupContactsListView() { contactsListView.setAdapter(adapter); contactsListView.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { int section = indexer.getSectionForPosition(firstVisibleItem); int nextSecPosition = indexer.getPositionForSection(section + 1); if (firstVisibleItem != lastFirstVisibleItem) { MarginLayoutParams params = (MarginLayoutParams) titleLayout.getLayoutParams(); params.topMargin = 0; titleLayout.setLayoutParams(params); title.setText(String.valueOf(alphabet.charAt(section))); } if (nextSecPosition == firstVisibleItem + 1) { View childView = view.getChildAt(0); if (childView != null) { int titleHeight = titleLayout.getHeight(); int bottom = childView.getBottom(); MarginLayoutParams params = (MarginLayoutParams) titleLayout .getLayoutParams(); if (bottom < titleHeight) { float pushedDistance = bottom - titleHeight; params.topMargin = (int) pushedDistance; titleLayout.setLayoutParams(params); } else { if (params.topMargin != 0) { params.topMargin = 0; titleLayout.setLayoutParams(params); } } } } lastFirstVisibleItem = firstVisibleItem; } }); } /** * 設定字母表上的觸控事件,根據當前觸控的位置結合字母表的高度,計算出當前觸控在哪個字母上。 * 當手指按在字母表上時,展示彈出式分組。手指離開字母表時,將彈出式分組隱藏。 */ private void setAlpabetListener() { alphabetButton.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { float alphabetHeight = alphabetButton.getHeight(); float y = event.getY(); int sectionPosition = (int) ((y / alphabetHeight) / (1f / 27f)); if (sectionPosition < 0) { sectionPosition = 0; } else if (sectionPosition > 26) { sectionPosition = 26; } String sectionLetter = String.valueOf(alphabet.charAt(sectionPosition)); int position = indexer.getPositionForSection(sectionPosition); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: alphabetButton.setBackgroundResource(R.drawable.a_z_click); sectionToastLayout.setVisibility(View.VISIBLE); sectionToastText.setText(sectionLetter); contactsListView.setSelection(position); break; case MotionEvent.ACTION_MOVE: sectionToastText.setText(sectionLetter); contactsListView.setSelection(position); break; default: alphabetButton.setBackgroundResource(R.drawable.a_z); sectionToastLayout.setVisibility(View.GONE); } return true; } }); } /** * 獲取sort key的首個字元,如果是英文字母就直接返回,否則返回#。 * * @param sortKeyString * 資料庫中讀取出的sort key * @return 英文字母或者# */ private String getSortKey(String sortKeyString) { alphabetButton.getHeight(); String key = sortKeyString.substring(0, 1).toUpperCase(); if (key.matches("[A-Z]")) { return key; } return "#"; }}
好了,就改動了以上兩處,其它檔案都保持不變,讓我們來執行一下看看效果:
非常不錯!當你的手指在右側字母表上滑動時,聯絡人的列表也跟著相應的變動,並在螢幕中央顯示一個當前的分組。
現在讓我們回數一下,分組導航、擠壓動畫、字母表快速滾動,Android系統聯絡人全特效都實現了。好了,今天的講解到此結束,有疑問的朋友請在下面留言。
關注我的技術公眾號,每天都有優質技術文章推送。關注我的娛樂公眾號,工作、學習累了的時候放鬆一下自己。
微信掃一掃下方二維碼即可關注: