Android系統聯繫人全特效實現(下),字母表快速滾動

轉載請註明出處:http://blog.csdn.net/guolin_blog/article/details/9050671

在上一篇文章中,我和大家一起實現了類似於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掉的,只有手指在字母表上滑動時才讓它顯示出來。修改後的佈局文件代碼如下:

[html] view plaincopy
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <ListView  
  8.         android:id="@+id/contacts_list_view"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_alignParentTop="true"  
  12.         android:scrollbars="none"  
  13.         android:fadingEdge="none" >  
  14.     </ListView>  
  15.   
  16.     <LinearLayout  
  17.         android:id="@+id/title_layout"  
  18.         android:layout_width="fill_parent"  
  19.         android:layout_height="18dip"  
  20.         android:layout_alignParentTop="true"  
  21.         android:background="#303030" >  
  22.   
  23.         <TextView  
  24.             android:id="@+id/title"  
  25.             android:layout_width="wrap_content"  
  26.             android:layout_height="wrap_content"  
  27.             android:layout_gravity="center_horizontal"  
  28.             android:layout_marginLeft="10dip"  
  29.             android:textColor="#ffffff"  
  30.             android:textSize="13sp" />  
  31.     </LinearLayout>  
  32.       
  33.     <Button   
  34.         android:id="@+id/alphabetButton"  
  35.         android:layout_width="wrap_content"  
  36.         android:layout_height="fill_parent"  
  37.         android:layout_alignParentRight="true"  
  38.         android:background="@drawable/a_z"  
  39.         />  
  40.       
  41.     <RelativeLayout   
  42.         android:id="@+id/section_toast_layout"  
  43.         android:layout_width="70dip"  
  44.         android:layout_height="70dip"  
  45.         android:layout_centerInParent="true"  
  46.         android:background="@drawable/section_toast"  
  47.         android:visibility="gone"  
  48.         >  
  49.         <TextView   
  50.             android:id="@+id/section_toast_text"  
  51.             android:layout_width="wrap_content"  
  52.             android:layout_height="wrap_content"  
  53.             android:layout_centerInParent="true"  
  54.             android:textColor="#fff"  
  55.             android:textSize="30sp"  
  56.             />  
  57.     </RelativeLayout>  
  58.   
  59. </RelativeLayout>  
然後打開MainActivity進行修改,毫無疑問,我們需要對字母表按鈕的touch事件進行監聽,於是在MainActivity中新增如下代碼:
[java] view plaincopy
  1. private void setAlpabetListener() {  
  2.     alphabetButton.setOnTouchListener(new OnTouchListener() {  
  3.         @Override  
  4.         public boolean onTouch(View v, MotionEvent event) {  
  5.             float alphabetHeight = alphabetButton.getHeight();  
  6.             float y = event.getY();  
  7.             int sectionPosition = (int) ((y / alphabetHeight) / (1f / 27f));  
  8.             if (sectionPosition < 0) {  
  9.                 sectionPosition = 0;  
  10.             } else if (sectionPosition > 26) {  
  11.                 sectionPosition = 26;  
  12.             }  
  13.             String sectionLetter = String.valueOf(alphabet.charAt(sectionPosition));  
  14.             int position = indexer.getPositionForSection(sectionPosition);  
  15.             switch (event.getAction()) {  
  16.             case MotionEvent.ACTION_DOWN:  
  17.                 alphabetButton.setBackgroundResource(R.drawable.a_z_click);  
  18.                 sectionToastLayout.setVisibility(View.VISIBLE);  
  19.                 sectionToastText.setText(sectionLetter);  
  20.                 contactsListView.setSelection(position);  
  21.                 break;  
  22.             case MotionEvent.ACTION_MOVE:  
  23.                 sectionToastText.setText(sectionLetter);  
  24.                 contactsListView.setSelection(position);  
  25.                 break;  
  26.             default:  
  27.                 alphabetButton.setBackgroundResource(R.drawable.a_z);  
  28.                 sectionToastLayout.setVisibility(View.GONE);  
  29.             }  
  30.             return true;  
  31.         }  
  32.     });  
  33. }  
可以看到,在這個方法中我們註冊了字母表按鈕的onTouch事件,然後在onTouch方法裏做了一些邏輯判斷和處理,下面我來一一詳細說明。首先通過字母表按鈕的getHeight方法獲取到字母表的總高度,然後用event.getY方法獲取到目前手指在字母表上的縱座標,用縱座標除以總高度就可以得到一個用小數表示的當前手指所在位置(0表在#端,1表示在Z端)。由於我們的字母表中一共有27個字符,再用剛剛算出的小數再除以1/27就可以得到一個0到27範圍內的浮點數,之後再把這個浮點數向下取整,就可以算出我們當前按在哪個字母上了。然後再對event的action進行判斷,如果是ACTION_DOWN或ACTION_MOVE,就在彈出式分組上顯示當前手指所按的字母,並調用ListView的setSelection方法把列表滾動到相應的分組。如果是其它的action,就將彈出式分組佈局隱藏。
MainActivity的完整代碼如下:
[java] view plaincopy
  1. public class MainActivity extends Activity {  
  2.   
  3.     /** 
  4.      * 分組的佈局 
  5.      */  
  6.     private LinearLayout titleLayout;  
  7.   
  8.     /** 
  9.      * 彈出式分組的佈局 
  10.      */  
  11.     private RelativeLayout sectionToastLayout;  
  12.   
  13.     /** 
  14.      * 右側可滑動字母表 
  15.      */  
  16.     private Button alphabetButton;  
  17.   
  18.     /** 
  19.      * 分組上顯示的字母 
  20.      */  
  21.     private TextView title;  
  22.   
  23.     /** 
  24.      * 彈出式分組上的文字 
  25.      */  
  26.     private TextView sectionToastText;  
  27.   
  28.     /** 
  29.      * 聯繫人ListView 
  30.      */  
  31.     private ListView contactsListView;  
  32.   
  33.     /** 
  34.      * 聯繫人列表適配器 
  35.      */  
  36.     private ContactAdapter adapter;  
  37.   
  38.     /** 
  39.      * 用於進行字母表分組 
  40.      */  
  41.     private AlphabetIndexer indexer;  
  42.   
  43.     /** 
  44.      * 存儲所有手機中的聯繫人 
  45.      */  
  46.     private List<Contact> contacts = new ArrayList<Contact>();  
  47.   
  48.     /** 
  49.      * 定義字母表的排序規則 
  50.      */  
  51.     private String alphabet = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";  
  52.   
  53.     /** 
  54.      * 上次第一個可見元素,用於滾動時記錄標識。 
  55.      */  
  56.     private int lastFirstVisibleItem = -1;  
  57.   
  58.     @Override  
  59.     protected void onCreate(Bundle savedInstanceState) {  
  60.         super.onCreate(savedInstanceState);  
  61.         setContentView(R.layout.activity_main);  
  62.         adapter = new ContactAdapter(this, R.layout.contact_item, contacts);  
  63.         titleLayout = (LinearLayout) findViewById(R.id.title_layout);  
  64.         sectionToastLayout = (RelativeLayout) findViewById(R.id.section_toast_layout);  
  65.         title = (TextView) findViewById(R.id.title);  
  66.         sectionToastText = (TextView) findViewById(R.id.section_toast_text);  
  67.         alphabetButton = (Button) findViewById(R.id.alphabetButton);  
  68.         contactsListView = (ListView) findViewById(R.id.contacts_list_view);  
  69.         Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;  
  70.         Cursor cursor = getContentResolver().query(uri,  
  71.                 new String[] { "display_name""sort_key" }, nullnull"sort_key");  
  72.         if (cursor.moveToFirst()) {  
  73.             do {  
  74.                 String name = cursor.getString(0);  
  75.                 String sortKey = getSortKey(cursor.getString(1));  
  76.                 Contact contact = new Contact();  
  77.                 contact.setName(name);  
  78.                 contact.setSortKey(sortKey);  
  79.                 contacts.add(contact);  
  80.             } while (cursor.moveToNext());  
  81.         }  
  82.         startManagingCursor(cursor);  
  83.         indexer = new AlphabetIndexer(cursor, 1, alphabet);  
  84.         adapter.setIndexer(indexer);  
  85.         if (contacts.size() > 0) {  
  86.             setupContactsListView();  
  87.             setAlpabetListener();  
  88.         }  
  89.     }  
  90.   
  91.     /** 
  92.      * 爲聯繫人ListView設置監聽事件,根據當前的滑動狀態來改變分組的顯示位置,從而實現擠壓動畫的效果。 
  93.      */  
  94.     private void setupContactsListView() {  
  95.         contactsListView.setAdapter(adapter);  
  96.         contactsListView.setOnScrollListener(new OnScrollListener() {  
  97.             @Override  
  98.             public void onScrollStateChanged(AbsListView view, int scrollState) {  
  99.             }  
  100.   
  101.             @Override  
  102.             public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,  
  103.                     int totalItemCount) {  
  104.                 int section = indexer.getSectionForPosition(firstVisibleItem);  
  105.                 int nextSecPosition = indexer.getPositionForSection(section + 1);  
  106.                 if (firstVisibleItem != lastFirstVisibleItem) {  
  107.                     MarginLayoutParams params = (MarginLayoutParams) titleLayout.getLayoutParams();  
  108.                     params.topMargin = 0;  
  109.                     titleLayout.setLayoutParams(params);  
  110.                     title.setText(String.valueOf(alphabet.charAt(section)));  
  111.                 }  
  112.                 if (nextSecPosition == firstVisibleItem + 1) {  
  113.                     View childView = view.getChildAt(0);  
  114.                     if (childView != null) {  
  115.                         int titleHeight = titleLayout.getHeight();  
  116.                         int bottom = childView.getBottom();  
  117.                         MarginLayoutParams params = (MarginLayoutParams) titleLayout  
  118.                                 .getLayoutParams();  
  119.                         if (bottom < titleHeight) {  
  120.                             float pushedDistance = bottom - titleHeight;  
  121.                             params.topMargin = (int) pushedDistance;  
  122.                             titleLayout.setLayoutParams(params);  
  123.                         } else {  
  124.                             if (params.topMargin != 0) {  
  125.                                 params.topMargin = 0;  
  126.                                 titleLayout.setLayoutParams(params);  
  127.                             }  
  128.                         }  
  129.                     }  
  130.                 }  
  131.                 lastFirstVisibleItem = firstVisibleItem;  
  132.             }  
  133.         });  
  134.   
  135.     }  
  136.   
  137.     /** 
  138.      * 設置字母表上的觸摸事件,根據當前觸摸的位置結合字母表的高度,計算出當前觸摸在哪個字母上。 
  139.      * 當手指按在字母表上時,展示彈出式分組。手指離開字母表時,將彈出式分組隱藏。 
  140.      */  
  141.     private void setAlpabetListener() {  
  142.         alphabetButton.setOnTouchListener(new OnTouchListener() {  
  143.             @Override  
  144.             public boolean onTouch(View v, MotionEvent event) {  
  145.                 float alphabetHeight = alphabetButton.getHeight();  
  146.                 float y = event.getY();  
  147.                 int sectionPosition = (int) ((y / alphabetHeight) / (1f / 27f));  
  148.                 if (sectionPosition < 0) {  
  149.                     sectionPosition = 0;  
  150.                 } else if (sectionPosition > 26) {  
  151.                     sectionPosition = 26;  
  152.                 }  
  153.                 String sectionLetter = String.valueOf(alphabet.charAt(sectionPosition));  
  154.                 int position = indexer.getPositionForSection(sectionPosition);  
  155.                 switch (event.getAction()) {  
  156.                 case MotionEvent.ACTION_DOWN:  
  157.                     alphabetButton.setBackgroundResource(R.drawable.a_z_click);  
  158.                     sectionToastLayout.setVisibility(View.VISIBLE);  
  159.                     sectionToastText.setText(sectionLetter);  
  160.                     contactsListView.setSelection(position);  
  161.                     break;  
  162.                 case MotionEvent.ACTION_MOVE:  
  163.                     sectionToastText.setText(sectionLetter);  
  164.                     contactsListView.setSelection(position);  
  165.                     break;  
  166.                 default:  
  167.                     alphabetButton.setBackgroundResource(R.drawable.a_z);  
  168.                     sectionToastLayout.setVisibility(View.GONE);  
  169.                 }  
  170.                 return true;  
  171.             }  
  172.         });  
  173.     }  
  174.   
  175.     /** 
  176.      * 獲取sort key的首個字符,如果是英文字母就直接返回,否則返回#。 
  177.      *  
  178.      * @param sortKeyString 
  179.      *            數據庫中讀取出的sort key 
  180.      * @return 英文字母或者# 
  181.      */  
  182.     private String getSortKey(String sortKeyString) {  
  183.         alphabetButton.getHeight();  
  184.         String key = sortKeyString.substring(01).toUpperCase();  
  185.         if (key.matches("[A-Z]")) {  
  186.             return key;  
  187.         }  
  188.         return "#";  
  189.     }  
  190.   
  191. }  
好了,就改動了以上兩處,其它文件都保持不變,讓我們來運行一下看看效果:

                                   

非常不錯!當你的手指在右側字母表上滑動時,聯繫人的列表也跟着相應的變動,並在屏幕中央顯示一個當前的分組。

現在讓我們回數一下,分組導航、擠壓動畫、字母表快速滾動,Android系統聯繫人全特效都實現了!

好了,今天的講解到此結束,有疑問的朋友請在下面留言。

源碼下載,請點擊這裏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章