两个月之前,写过一篇文章【Android界面实现】整合了刷新、加载更多、滑动删除功能的XListview ,介绍了如何整合两个开源项目实现这种功能,但是在实际的使用中,却出现了一些问题,比如说item的点击事件多次调用,item的position不准确,代码量太大太复杂等,一个哥们在自己的工作中使用了这个项目,这些BUG给他造成了困扰。所以,花费了三天时间看懂项目的基础上,我决定重写这个项目,然后就有了今天的这篇文章,给大家介绍一个更加强大的ListView的使用和实现原理。为了纪念XListView和方便区分,我决定给这个控件取名为ZListVIew。
另外,这篇文章涉及到的内容较多,请各位看官自备茶水、瓜子,提前做好准备~
本文将分成两部分进行介绍,前半部分是ZListView的使用介绍,后半部分是实现原理介绍。
ZListView的特点如下:
(1)使用与XListView完全一样
(2)即可以实现刷新、加载功能,也可以关闭,当作普通的ListView使用
(3)支持从四个方向进行侧滑
(4)添加了强大的事件监听,完全掌控各种事件触发过程
(5)修复了之前项目的各种BUG,更加稳定
(6)针对ListView,对滑动Item进行了优化和删减,代码添加注释,更加易懂好用
(7)出现问题随时叫我,强大的后援支持,嘻嘻
一、ZListView使用介绍
在开始介绍使用之前,先给大家看下运行效果,因为不知道什么原因,我上传的gif都变成静态图了,所以请大家点击这个网址看效果:http://ww4.sinaimg.cn/large/005yyi5Jjw1eo1yrys3a6g30ar0h61kx.gif
下面是整个项目的目录结构
BaseSwipeAdapter是一个适配器,如果你的ListView里面需要添加滑动效果的话,就需要继承这个类。
enums包里面是一些枚举值,比如滑动模式,滑动方向,显示模式等。
listener包里面是监听器,OnSwipeLayoutListener是用来监听ZSwipeItem的布局改变事件的,而SwipeListener则是监听ZSwipeItem的open、close、startOpen、startClose、update等事件的,但是我们使用的时候,我们仅需要继承SimpleSwipeListener,然后重写里面我们关心的事件即可。
widget包里面则是核心类,ZListView开头的,是经过修改之后的ListIVew核心类,和XListView的用法完全一样。而ZSwipeItem则是滑动Item的核心类,我们在这里面处理item的各种滑动操作。
介绍完基本的目录结构,下面就要介绍使用了。
MainActivity是我们的一个测试Activity,下面是代码
1. public class MainActivity extends Activity {
2.
3. protected static final String TAG = "MainActivity";
4.
5. private ZListView listView;
6. private Handler handler = new Handler();
7.
8. @Override
9. protected void onCreate(Bundle savedInstanceState) {
10. super.onCreate(savedInstanceState);
11. setContentView(R.layout.activity_main);
12.
13. listView = (ZListView) findViewById(R.id.listview);
14. listView.setXListViewListener(new IXListViewListener() {
15.
16. @Override
17. public void onRefresh() {
18.
19. handler.postDelayed(new Runnable() {
20.
21. @Override
22. public void run() {
23. listView.stopRefresh();
24. }
25. }, 1000);
26. }
27.
28. @Override
29. public void onLoadMore() {
30.
31. handler.postDelayed(new Runnable() {
32.
33. @Override
34. public void run() {
35. listView.stopLoadMore();
36. }
37. }, 1000);
38.
39. }
40. });
41.
42. listView.setPullLoadEnable(true);
43. listView.setOnItemClickListener(new OnItemClickListener() {
44.
45. @Override
46. public void onItemClick(AdapterView<?> parent, View view,
47. int position, long id) {
48.
49. Toast.makeText(MainActivity.this, "onItemClick=" + position,
50. Toast.LENGTH_SHORT).show();
51.
52. }
53.
54. });
55.
56. listView.setOnItemLongClickListener(new OnItemLongClickListener() {
57.
58. @Override
59. public boolean onItemLongClick(AdapterView<?> parent, View view,
60. int position, long id) {
61. Toast.makeText(MainActivity.this,
62. "onItemLongClick=" + position, Toast.LENGTH_SHORT)
63. .show();
64. return true;
65. }
66. });
67.
68. listView.setAdapter(new ListViewAdapter(this));
69.
70. }
71.
72. }
从上面可以看出来,和使用XListView是完全一样的,下面是布局文件的代码,和XListView也是完全一样的,就是替换一下控件名称就可以
1. <?xml version="1.0" encoding="utf-8"?>
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:layout_width="match_parent"
4. android:layout_height="match_parent"
5. android:background="#f0f0f0" >
6.
7. <com.socks.zlistview.widget.ZListView
8. android:id="@+id/listview"
9. android:layout_width="match_parent"
10. android:layout_height="match_parent"
11. android:cacheColorHint="@android:color/transparent" />
12.
13. </LinearLayout>
主要是适配器用起来有一些差别。前面说过,如果我们要实现有侧滑功能的item,就要去继承BaseSwipeAdapter,因此,我们这里就需要这样做。但是在给出适配器的代码之前,我们需要先看一下Item的布局文件的写法,因为这个有强制要求。
1. <?xml version="1.0" encoding="utf-8"?>
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:layout_width="match_parent"
4. android:layout_height="wrap_content" >
5.
6. <com.socks.zlistview.widget.ZSwipeItem
7. android:id="@+id/swipe_item"
8. android:layout_width="match_parent"
9. android:layout_height="wrap_content" >
10.
11. <LinearLayout
12. android:id="@+id/ll"
13. android:layout_width="180dp"
14. android:layout_height="match_parent"
15. android:background="@android:color/holo_red_light"
16. android:gravity="center" >
17.
18. <ImageView
19. android:layout_width="30dp"
20. android:layout_height="wrap_content"
21. android:src="@drawable/trash" />
22. </LinearLayout>
23.
24. <LinearLayout
25. android:layout_width="match_parent"
26. android:layout_height="wrap_content"
27. android:background="@drawable/item_selector"
28. android:orientation="vertical"
29. android:padding="15dp" >
30.
31. <TextView
32. android:id="@+id/tv"
33. android:layout_width="match_parent"
34. android:layout_height="wrap_content"
35. android:textColor="@android:color/black"
36. android:textSize="14sp" />
37. </LinearLayout>
38. </com.socks.zlistview.widget.ZSwipeItem>
39.
40. </LinearLayout>
如果我们需要侧滑功能,那么我们的item里面必须有ZSwipeItem,其实ZSwipeItem是继承的FrameLayout,这点我们在原理讲解阶段会有更加详细的说明。在ZSwipeItem布局的里面,必须有两个ViewGroup布局,第一个ViewGroup里面包裹的就是侧滑之后显示的内容,而第二个ViewGroup则是在前面显示的布局,记住,这两个ViewGroup是必须的!否则会报错哦!
说完item布局文件的写法,下面开始看一下Adapter的代码。
1. public class ListViewAdapter extends BaseSwipeAdapter {
2.
3. protected static final String TAG = "ListViewAdapter";
4.
5. private Activity context;
6.
7. public ListViewAdapter(Activity context) {
8. this.context = context;
9. }
10.
11. @Override
12. public int getCount() {
13. return 20;
14. }
15.
16. @Override
17. public Object getItem(int position) {
18. return position;
19. }
20.
21. @Override
22. public long getItemId(int position) {
23. return position;
24. }
25.
26. @Override
27. public int getSwipeLayoutResourceId(int position) {
28. return R.id.swipe_item;
29. }
30.
31. @Override
32. public View generateView(int position, ViewGroup parent) {
33. return context.getLayoutInflater().inflate(R.layout.item_listview,
34. parent, false);
35. }
36.
37. @Override
38. public void fillValues(final int position, View convertView) {
39. final ZSwipeItem swipeItem = (ZSwipeItem) convertView
40. .findViewById(R.id.swipe_item);
41. LinearLayout ll = (LinearLayout) convertView.findViewById(R.id.ll);
42.
43. TextView tv = (TextView) convertView.findViewById(R.id.tv);
44. tv.setText(position + "." + " 测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本测试文本");
45.
46. if (position % 4 == 1) {
47. swipeItem.setShowMode(ShowMode.PullOut);
48. swipeItem.setDragEdge(DragEdge.Right);
49. } else if (position % 4 == 2) {
50. swipeItem.setShowMode(ShowMode.LayDown);
51. swipeItem.setDragEdge(DragEdge.Right);
52. } else if (position % 4 == 3) {
53. swipeItem.setShowMode(ShowMode.PullOut);
54. swipeItem.setDragEdge(DragEdge.Left);
55. } else if (position % 4 == 0) {
56. swipeItem.setShowMode(ShowMode.LayDown);
57. swipeItem.setDragEdge(DragEdge.Left);
58. }
59.
60. swipeItem.addSwipeListener(new SimpleSwipeListener() {
61. @Override
62. public void onOpen(ZSwipeItem layout) {
63. Log.d(TAG, "打开:" + position);
64. }
65.
66. @Override
67. public void onClose(ZSwipeItem layout) {
68. Log.d(TAG, "关闭:" + position);
69. }
70.
71. @Override
72. public void onStartOpen(ZSwipeItem layout) {
73. Log.d(TAG, "准备打开:" + position);
74. }
75.
76. @Override
77. public void onStartClose(ZSwipeItem layout) {
78. Log.d(TAG, "准备关闭:" + position);
79. }
80.
81. @Override
82. public void onHandRelease(ZSwipeItem layout, float xvel, float yvel) {
83. Log.d(TAG, "手势释放");
84. }
85.
86. @Override
87. public void onUpdate(ZSwipeItem layout, int leftOffset,
88. int topOffset) {
89. Log.d(TAG, "位置更新");
90. }
91. });
92.
93. ll.setOnClickListener(new OnClickListener() {
94.
95. @Override
96. public void onClick(View v) {
97. Toast.makeText(context, "删除:" + position, Toast.LENGTH_SHORT)
98. .show();
99. swipeItem.close();
100. }
101. });
102. }
103. }
首先,我们必须继承BaseSwipeAdapter,不要嫌弃我啰嗦哈,继承之后,有几个特殊的需要我们去实现的方法,下面我们介绍这几个方法的用法:
(1)getSwipeLayoutResourceId(int position)这个方法用来返回position位置的布局文件中ZSwipeItem的id,之所以需要有position,是为了在有多种布局在一起的情况。大多数情况下,我们不需要关心position参数即可。
(2)generateView(int posiion , ViewGroup parent)在这个方法里面,我们需要完成布局文件资源的加载,就像上面这样用就可以了。
(3)fillVlues(int position ,View convertIView)这个方法是用来对我们的布局进行数据填充和监听器绑定的,在这里面,你不需要关心convertView是否为null,已经经过实例化了,尽情的调用就可以了。
(4)swipeItem.setShowMode()是在设置显示模式。显示模式一共有两种,PullOut和LayDown,在ShowMode枚举类型里面定义的。PullOut就是推出模式,侧滑之后,前面的布局会被推出屏外,后面的布局从屏外移动进来,而LayOut则是后面的布局会一点点的显示出来,也就是被遮盖主了。
(5)swipeItem.setDragEdge()是在设置侧滑动作的接受方向,Right代表从右边往左滑动的时候,后面布局会显示出来,这个也是一个枚举类型,有上下左右四个取值,一般推荐左右,上下和ListView的滑动有冲突,效果不好。
(6)通过swipeItem.addSwipeListener()可以给滑动item添加各种事件监听,推荐使用SimpleSwipeListener的匿名类,这样就可以只重写自己关心的事件,onOpen和onClose是打开关闭的时候调用,onStartXX则是在动作一开始就调用,因此,如果需要改变后面布局的状态,请在onStartXX的时候调用,onHandRelease()则是在用户手指离开屏幕的时候调用,参数layout是事件发生的ZSwipeItem对象、xvel和yvel则是手势放开瞬间,x和y方向的加速度。onUpdate()在滑动的时候一直会调用,leftOffset和topOffset是距离左上角坐标的距离。
(7)使用SWipeItem之后,可以和平常一样给后面布局中的控件添加各种事件,比如上面的代码就直接给后面的线性布局添加了点击事件,点击之后就会关闭滑动布局。
使用介绍就先写到这里了,项目大家请到我的github下载,因为实现原理稍微有点复杂,因此,我想分到下一篇中介绍。