其实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() {@Overridepublic 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;@Overrideprotected 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() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}@Overridepublic 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() {@Overridepublic 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 "#";}}

好了,就改动了以上两处,其它文件都保持不变,让我们来运行一下看看效果:


非常不错!当你的手指在右侧字母表上滑动时,联系人的列表也跟着相应的变动,并在屏幕中央显示一个当前的分组。

源码下载,请点击这里

更多相关文章

  1. Android(安卓)购物车的功能逻辑
  2. Android-section-list分组列表
  3. Android(安卓)第三方 Jar 的 Link 与问题:Unable to execute dex
  4. ARouter使用及源码简析
  5. Android(安卓)Preference初探,另一种布局方式
  6. android之Listview的分组实现
  7. Android(安卓)6.0权限请求相关及权限分组
  8. Android(安卓)Listview分组特效:滑动分组标题当前固定,并随内容滑
  9. Android:将activity设置为弹出式的并设置为透明的

随机推荐

  1. android 使用 surfaceView 获取 camera
  2. android 自定义输入法研究
  3. 深入理解Android写作背后的故事
  4. Android(安卓)PC投屏简单尝试—最终章1
  5. Android预置Apk方法
  6. 使用内容提供器跨应用分享你的数据(1)
  7. Android中View的绘制
  8. [Android]如何做一个崩溃率少于千分之三
  9. Android(安卓)基于Socket的聊天应用实例(
  10. android中软键盘enter键更改图标