在上一篇文章中,我和大家一起实现了类似于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
plain
copy

  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. <ListView
  7. android:id="@+id/contacts_list_view"
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:layout_alignParentTop="true"
  11. android:scrollbars="none"
  12. android:fadingEdge="none" >
  13. </ListView>
  14. <LinearLayout
  15. android:id="@+id/title_layout"
  16. android:layout_width="fill_parent"
  17. android:layout_height="18dip"
  18. android:layout_alignParentTop="true"
  19. android:background="#303030" >
  20. <TextView
  21. android:id="@+id/title"
  22. android:layout_width="wrap_content"
  23. android:layout_height="wrap_content"
  24. android:layout_gravity="center_horizontal"
  25. android:layout_marginLeft="10dip"
  26. android:textColor="#ffffff"
  27. android:textSize="13sp" />
  28. </LinearLayout>
  29. <Button
  30. android:id="@+id/alphabetButton"
  31. android:layout_width="wrap_content"
  32. android:layout_height="fill_parent"
  33. android:layout_alignParentRight="true"
  34. android:background="@drawable/a_z"
  35. />
  36. <RelativeLayout
  37. android:id="@+id/section_toast_layout"
  38. android:layout_width="70dip"
  39. android:layout_height="70dip"
  40. android:layout_centerInParent="true"
  41. android:background="@drawable/section_toast"
  42. android:visibility="gone"
  43. >
  44. <TextView
  45. android:id="@+id/section_toast_text"
  46. android:layout_width="wrap_content"
  47. android:layout_height="wrap_content"
  48. android:layout_centerInParent="true"
  49. android:textColor="#fff"
  50. android:textSize="30sp"
  51. />
  52. </RelativeLayout>
  53. </RelativeLayout>

然后打开MainActivity进行修改,毫无疑问,我们需要对字母表按钮的touch事件进行监听,于是在MainActivity中新增如下代码:

[java] view
plain
copy

  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
plain
copy

  1. public class MainActivity extends Activity {
  2. /**
  3. * 分组的布局
  4. */
  5. private LinearLayout titleLayout;
  6. /**
  7. * 弹出式分组的布局
  8. */
  9. private RelativeLayout sectionToastLayout;
  10. /**
  11. * 右侧可滑动字母表
  12. */
  13. private Button alphabetButton;
  14. /**
  15. * 分组上显示的字母
  16. */
  17. private TextView title;
  18. /**
  19. * 弹出式分组上的文字
  20. */
  21. private TextView sectionToastText;
  22. /**
  23. * 联系人ListView
  24. */
  25. private ListView contactsListView;
  26. /**
  27. * 联系人列表适配器
  28. */
  29. private ContactAdapter adapter;
  30. /**
  31. * 用于进行字母表分组
  32. */
  33. private AlphabetIndexer indexer;
  34. /**
  35. * 存储所有手机中的联系人
  36. */
  37. private List<Contact> contacts = new ArrayList<Contact>();
  38. /**
  39. * 定义字母表的排序规则
  40. */
  41. private String alphabet = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  42. /**
  43. * 上次第一个可见元素,用于滚动时记录标识。
  44. */
  45. private int lastFirstVisibleItem = -1;
  46. @Override
  47. protected void onCreate(Bundle savedInstanceState) {
  48. super.onCreate(savedInstanceState);
  49. setContentView(R.layout.activity_main);
  50. adapter = new ContactAdapter(this, R.layout.contact_item, contacts);
  51. titleLayout = (LinearLayout) findViewById(R.id.title_layout);
  52. sectionToastLayout = (RelativeLayout) findViewById(R.id.section_toast_layout);
  53. title = (TextView) findViewById(R.id.title);
  54. sectionToastText = (TextView) findViewById(R.id.section_toast_text);
  55. alphabetButton = (Button) findViewById(R.id.alphabetButton);
  56. contactsListView = (ListView) findViewById(R.id.contacts_list_view);
  57. Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
  58. Cursor cursor = getContentResolver().query(uri,
  59. new String[] { "display_name", "sort_key" }, null, null, "sort_key");
  60. if (cursor.moveToFirst()) {
  61. do {
  62. String name = cursor.getString(0);
  63. String sortKey = getSortKey(cursor.getString(1));
  64. Contact contact = new Contact();
  65. contact.setName(name);
  66. contact.setSortKey(sortKey);
  67. contacts.add(contact);
  68. } while (cursor.moveToNext());
  69. }
  70. startManagingCursor(cursor);
  71. indexer = new AlphabetIndexer(cursor, 1, alphabet);
  72. adapter.setIndexer(indexer);
  73. if (contacts.size() > 0) {
  74. setupContactsListView();
  75. setAlpabetListener();
  76. }
  77. }
  78. /**
  79. * 为联系人ListView设置监听事件,根据当前的滑动状态来改变分组的显示位置,从而实现挤压动画的效果。
  80. */
  81. private void setupContactsListView() {
  82. contactsListView.setAdapter(adapter);
  83. contactsListView.setOnScrollListener(new OnScrollListener() {
  84. @Override
  85. public void onScrollStateChanged(AbsListView view, int scrollState) {
  86. }
  87. @Override
  88. public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
  89. int totalItemCount) {
  90. int section = indexer.getSectionForPosition(firstVisibleItem);
  91. int nextSecPosition = indexer.getPositionForSection(section + 1);
  92. if (firstVisibleItem != lastFirstVisibleItem) {
  93. MarginLayoutParams params = (MarginLayoutParams) titleLayout.getLayoutParams();
  94. params.topMargin = 0;
  95. titleLayout.setLayoutParams(params);
  96. title.setText(String.valueOf(alphabet.charAt(section)));
  97. }
  98. if (nextSecPosition == firstVisibleItem + 1) {
  99. View childView = view.getChildAt(0);
  100. if (childView != null) {
  101. int titleHeight = titleLayout.getHeight();
  102. int bottom = childView.getBottom();
  103. MarginLayoutParams params = (MarginLayoutParams) titleLayout
  104. .getLayoutParams();
  105. if (bottom < titleHeight) {
  106. float pushedDistance = bottom - titleHeight;
  107. params.topMargin = (int) pushedDistance;
  108. titleLayout.setLayoutParams(params);
  109. } else {
  110. if (params.topMargin != 0) {
  111. params.topMargin = 0;
  112. titleLayout.setLayoutParams(params);
  113. }
  114. }
  115. }
  116. }
  117. lastFirstVisibleItem = firstVisibleItem;
  118. }
  119. });
  120. }
  121. /**
  122. * 设置字母表上的触摸事件,根据当前触摸的位置结合字母表的高度,计算出当前触摸在哪个字母上。
  123. * 当手指按在字母表上时,展示弹出式分组。手指离开字母表时,将弹出式分组隐藏。
  124. */
  125. private void setAlpabetListener() {
  126. alphabetButton.setOnTouchListener(new OnTouchListener() {
  127. @Override
  128. public boolean onTouch(View v, MotionEvent event) {
  129. float alphabetHeight = alphabetButton.getHeight();
  130. float y = event.getY();
  131. int sectionPosition = (int) ((y / alphabetHeight) / (1f / 27f));
  132. if (sectionPosition < 0) {
  133. sectionPosition = 0;
  134. } else if (sectionPosition > 26) {
  135. sectionPosition = 26;
  136. }
  137. String sectionLetter = String.valueOf(alphabet.charAt(sectionPosition));
  138. int position = indexer.getPositionForSection(sectionPosition);
  139. switch (event.getAction()) {
  140. case MotionEvent.ACTION_DOWN:
  141. alphabetButton.setBackgroundResource(R.drawable.a_z_click);
  142. sectionToastLayout.setVisibility(View.VISIBLE);
  143. sectionToastText.setText(sectionLetter);
  144. contactsListView.setSelection(position);
  145. break;
  146. case MotionEvent.ACTION_MOVE:
  147. sectionToastText.setText(sectionLetter);
  148. contactsListView.setSelection(position);
  149. break;
  150. default:
  151. alphabetButton.setBackgroundResource(R.drawable.a_z);
  152. sectionToastLayout.setVisibility(View.GONE);
  153. }
  154. return true;
  155. }
  156. });
  157. }
  158. /**
  159. * 获取sort key的首个字符,如果是英文字母就直接返回,否则返回#。
  160. *
  161. * @param sortKeyString
  162. *            数据库中读取出的sort key
  163. * @return 英文字母或者#
  164. */
  165. private String getSortKey(String sortKeyString) {
  166. alphabetButton.getHeight();
  167. String key = sortKeyString.substring(0, 1).toUpperCase();
  168. if (key.matches("[A-Z]")) {
  169. return key;
  170. }
  171. return "#";
  172. }
  173. }

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

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

现在让我们回数一下,分组导航、挤压动画、字母表快速滚动,Android系统联系人全特效都实现了!

好了,今天的讲解到此结束,有疑问的朋友请在下面留言。

源码下载,请点击这里

Android系统联系人全特效实现(下),字母表快速滚动的更多相关文章

  1. android系统联系人分组特效实现(2)---字母表快速滚动

    要实现这种功能,只需要在   android系统联系人分组特效实现(1)---分组导航和挤压动画  的基础上再加上一个自定义控件即可完成. 1.新建项目,继续新建一个java类,BladeView,用 ...

  2. Android系统联系人全特效实现(上),分组导航和挤压动画

    记得在我刚接触Android的时候对系统联系人中的特效很感兴趣,它会根据手机中联系人姓氏的首字母进行分组,并在界面的最顶端始终显示一个当前的分组.如下图所示: 最让我感兴趣的是,当后一个分组和前一个分 ...

  3. android系统联系人分组特效实现(1)---分组导航和挤压动画

    1.打开activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/andr ...

  4. [置顶] android系统如何在静音模式下关闭camera拍照声音(2)

    之前写过一篇“android系统如何在静音模式下关闭camera拍照声音”的博客,今天来写他的续篇,继续探讨这个问题. 公司新需求,要求在camera应用中添加一个开关,可以进行拍照声音的关闭和开启. ...

  5. Android零基础入门第2节:Android 系统架构和应用组件那些事

    原文:Android零基础入门第2节:Android 系统架构和应用组件那些事 继上一期浅谈了Android的前世今生,这一期一起来大致回顾一下Android 系统架构和应用组件. 一.Android ...

  6. 如何学习Android系统源码(转)

    一. Android系统的源代码非常庞大和复杂,我们不能贸然进入,否则很容易在里面迷入方向,进而失去研究它的信心.我们应该在分析它的源代码之前学习好一些理论知识,下面就介绍一些与Android系统相关 ...

  7. Android入门学习:Android 系统框架及应用程序执行过程

    Android基础知识学习 新手上路,还请多多帮助.由于初学,博客内容难免有不正确的地方,还请各位多多指教,相互学习! 主要内容: 1.Android层次架构及主要功能 2.Android编程模型,程 ...

  8. Android系统权限和root权限大全

    tyle="margin:20px 0px 0px; font-size:14.285714149475098px; line-height:26px; font-family:Arial; ...

  9. Android系统关机或几种方式重启

    ---------------------------------------------------------------------------------------------------- ...

随机推荐

  1. [React] Create an Auto Resizing Virtualized List with react-virtualized

    In this lesson we'll show how to use the AutoSizer component from react-virtualized to automatically ...

  2. 4、linux下应用创建线程

    1.linux创建线程之pthread_create 函数简介 pthread_create是UNIX环境创建线程函数 头文件 #include<pthread.h> 函数声明 int p ...

  3. [TypeStyle] Use fallback values in TypeStyle for better browser support

    You can increase the browser support of your CSS using fallback values and vendor prefixes. This les ...

  4. python 升级pip

    废话少说,直接上图,希望谅解我的懒惰!:)

  5. ios开发网络学习四:NSURLConnection大文件断点下载

    #import "ViewController.h" @interface ViewController ()<NSURLConnectionDataDelegate> ...

  6. 一个自己主动依据xcode中的objective-c代码生成类关系图的神器

    https://github.com/kimsungwhee/KSHObjcUML 安装方法: 1.下载项目 2.执行 3.会又一次开启一个新的xcode 4.选择一个项目,点击 Objc-UML 会 ...

  7. js课程 1-5 js如何测试变量的数据类型

    js课程 1-5 js如何测试变量的数据类型 一.总结 一句话总结:用typeof()方法. 1.js如何判断变量的数据类型? 用typeof()方法. 13 v=10; 14 15 if(typeo ...

  8. VMWare提供三种工作模式桥接(bridge)、NAT(网络地址转换)和host-only(主机模式)

    1.桥接模式 在桥接模式下,VMWare虚拟出来的操作系统就像是局域网中的一台独立的主机(主机和虚拟机处于对等地位),它可以访问网内任何一台机器.在桥接模式下,我们往往需要为虚拟主机配置IP地址.子网 ...

  9. [tmux] Create collections of panes using tmux windows

    In tmux, a window is a collection of panes. Creating multiple windows is a great way to organize you ...

  10. [Angular Directive] Structure directive and <template>

    The structure directive is just a sugar syntax of <template>. Such as: <div *ngIf="nam ...