转自:,本文出自:【张鸿洋的博客】
群里一哥们今天聊天偶然提到这个git hub上的控件:pull-to-refresh ,有兴趣的看下,例子中的功能极其强大,支持很多控件。本篇博客详细给大家介绍下ListView和GridView利用pull-to-rerfesh 实现下拉刷新和上拉加载更多。
1、ListView下拉刷新快速入门
pull-to-refresh对ListView进行了封装,叫做:PullToRefreshListView,用法和listview没什么区别,下面看demo.
布局文件:
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.
6. <com.handmark.pulltorefresh.library.PullToRefreshListView
7. xmlns:ptr="http://schemas.android.com/apk/res-auto"
8. android:id="@+id/pull_refresh_list"
9. android:layout_width="fill_parent"
10. android:layout_height="fill_parent"
11. android:cacheColorHint="#00000000"
12. android:divider="#19000000"
13. android:dividerHeight="4dp"
14. android:fadingEdge="none"
15. android:fastScrollEnabled="false"
16. android:footerDividersEnabled="false"
17. android:headerDividersEnabled="false"
18. android:smoothScrollbar="true" >
19. </com.handmark.pulltorefresh.library.PullToRefreshListView>
20.
21. </RelativeLayout>
声明了一个PullToRefreshListView,里面所有的属性都是ListView的,没有任何其他属性,当然了PullToRefreshListView也提供了很多配置的属性,后面会详细介绍。
Activity的代码:
1. package com.example.zhy_pulltorefreash_chenyoca;
2.
3. import java.util.LinkedList;
4.
5. import android.app.Activity;
6. import android.os.AsyncTask;
7. import android.os.Bundle;
8. import android.text.format.DateUtils;
9. import android.widget.ArrayAdapter;
10. import android.widget.ListView;
11.
12. import com.handmark.pulltorefresh.library.PullToRefreshBase;
13. import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener;
14. import com.handmark.pulltorefresh.library.PullToRefreshListView;
15.
16. public class PullToRefreshListActivity extends Activity
17. {
18.
19. private LinkedList<String> mListItems;
20. /**
21. * 上拉刷新的控件
22. */
23. private PullToRefreshListView mPullRefreshListView;
24.
25. private ArrayAdapter<String> mAdapter;
26.
27. private int mItemCount = 9;
28.
29. @Override
30. protected void onCreate(Bundle savedInstanceState)
31. {
32. super.onCreate(savedInstanceState);
33. setContentView(R.layout.activity_main);
34. // 得到控件
35. mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);
36. //初始化数据
37. initDatas();
38. //设置适配器
39. new ArrayAdapter<String>(this,
40. android.R.layout.simple_list_item_1, mListItems);
41. mPullRefreshListView.setAdapter(mAdapter);
42. // 设置监听事件
43. mPullRefreshListView
44. new OnRefreshListener<ListView>()
45. {
46. @Override
47. public void onRefresh(
48. PullToRefreshBase<ListView> refreshView)
49. {
50. String label = DateUtils.formatDateTime(
51. getApplicationContext(),
52. System.currentTimeMillis(),
53. DateUtils.FORMAT_SHOW_TIME
54. | DateUtils.FORMAT_SHOW_DATE
55. | DateUtils.FORMAT_ABBREV_ALL);
56. // 显示最后更新的时间
57. refreshView.getLoadingLayoutProxy()
58. .setLastUpdatedLabel(label);
59.
60. // 模拟加载任务
61. new GetDataTask().execute();
62. }
63. });
64.
65. }
66.
67. private void initDatas()
68. {
69. // 初始化数据和数据源
70. new LinkedList<String>();
71.
72. for (int i = 0; i < mItemCount; i++)
73. {
74. "" + i);
75. }
76. }
77.
78. private class GetDataTask extends AsyncTask<Void, Void, String>
79. {
80.
81. @Override
82. protected String doInBackground(Void... params)
83. {
84. try
85. {
86. 2000);
87. catch (InterruptedException e)
88. {
89. }
90. return "" + (mItemCount++);
91. }
92.
93. @Override
94. protected void onPostExecute(String result)
95. {
96. mListItems.add(result);
97. mAdapter.notifyDataSetChanged();
98. // Call onRefreshComplete when the list has been refreshed.
99. mPullRefreshListView.onRefreshComplete();
100. }
101. }
102.
103. }
代码极其简单,得到PullToRefreshListView控件,然后像ListView一样设置数据集。当然了,我们有下拉刷新,所以必须设置下拉刷新的回调:
setOnRefreshListener(new OnRefreshListener<ListView>(){}
我们在回调中模拟了一个异步任务,加载了一个Item。
效果图:
下拉时,执行我们的GetDataTask任务,任务执行完成后在onPostExecute中调用mPullRefreshListView.onRefreshComplete();完成刷新。
是不是分分钟实现下拉刷新。当然了,你可能会有疑问,下拉刷新的指示器上的文字可以自定义吗?那个图片可以换成箭头吗?说好的上拉加载更多呢?后面会一一添加~
2、添加上拉加载更多
如过希望实现上拉加载更多,那么首先需要在布局文件的声明属性中添加一个属性,用于指定目前的下拉模式:
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.
6. <com.handmark.pulltorefresh.library.PullToRefreshListView
7. xmlns:ptr="http://schemas.android.com/apk/res-auto"
8. android:id="@+id/pull_refresh_list"
9. android:layout_width="fill_parent"
10. android:layout_height="fill_parent"
11. android:cacheColorHint="#00000000"
12. android:divider="#19000000"
13. android:dividerHeight="4dp"
14. android:fadingEdge="none"
15. android:fastScrollEnabled="false"
16. android:footerDividersEnabled="false"
17. android:headerDividersEnabled="false"
18. android:smoothScrollbar="true"
19. ptr:ptrMode="both" >
20. </com.handmark.pulltorefresh.library.PullToRefreshListView>
21.
22. </RelativeLayout>
我们添加了一个属性:ptr:ptrMode="both" ,意思:上拉和下拉都支持。
可选值为:disabled(禁用下拉刷新),pullFromStart(仅支持下拉刷新),pullFromEnd(仅支持上拉刷新),both(二者都支持),manualOnly(只允许手动触发)
当然了,如果你不喜欢在布局文件中指定,完全可以使用代码设置,在onCreate里面写:mPullRefreshListView.setMode(Mode.BOTH);//设置你需要的模式
设置了模式为双向都支持,当然必须为上拉和下拉分别设置回调,请看下面的代码:
1. package com.example.zhy_pulltorefreash_chenyoca;
2.
3. import java.util.LinkedList;
4.
5. import android.app.Activity;
6. import android.os.AsyncTask;
7. import android.os.Bundle;
8. import android.text.format.DateUtils;
9. import android.util.Log;
10. import android.widget.ArrayAdapter;
11. import android.widget.ListView;
12.
13. import com.handmark.pulltorefresh.library.PullToRefreshBase;
14. import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode;
15. import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener;
16. import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener2;
17. import com.handmark.pulltorefresh.library.PullToRefreshListView;
18.
19. public class PullToRefreshListActivity extends Activity
20. {
21.
22. private LinkedList<String> mListItems;
23. /**
24. * 上拉刷新的控件
25. */
26. private PullToRefreshListView mPullRefreshListView;
27.
28. private ArrayAdapter<String> mAdapter;
29.
30. private int mItemCount = 9;
31.
32. @Override
33. protected void onCreate(Bundle savedInstanceState)
34. {
35. super.onCreate(savedInstanceState);
36. setContentView(R.layout.activity_main);
37. // 得到控件
38. mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);
39. mPullRefreshListView.setMode(Mode.BOTH);
40. // 初始化数据
41. initDatas();
42. // 设置适配器
43. new ArrayAdapter<String>(this,
44. android.R.layout.simple_list_item_1, mListItems);
45. mPullRefreshListView.setAdapter(mAdapter);
46.
47. mPullRefreshListView
48. new OnRefreshListener2<ListView>()
49. {
50. @Override
51. public void onPullDownToRefresh(
52. PullToRefreshBase<ListView> refreshView)
53. {
54. "TAG", "onPullDownToRefresh");
55. //这里写下拉刷新的任务
56. new GetDataTask().execute();
57. }
58.
59. @Override
60. public void onPullUpToRefresh(
61. PullToRefreshBase<ListView> refreshView)
62. {
63. "TAG", "onPullUpToRefresh");
64. //这里写上拉加载更多的任务
65. new GetDataTask().execute();
66. }
67. });
68.
69. }
70.
71. private void initDatas()
72. {
73. // 初始化数据和数据源
74. new LinkedList<String>();
75.
76. for (int i = 0; i < mItemCount; i++)
77. {
78. "" + i);
79. }
80. }
81.
82. private class GetDataTask extends AsyncTask<Void, Void, String>
83. {
84.
85. @Override
86. protected String doInBackground(Void... params)
87. {
88. try
89. {
90. 2000);
91. catch (InterruptedException e)
92. {
93. }
94. return "" + (mItemCount++);
95. }
96.
97. @Override
98. protected void onPostExecute(String result)
99. {
100. mListItems.add(result);
101. mAdapter.notifyDataSetChanged();
102. // Call onRefreshComplete when the list has been refreshed.
103. mPullRefreshListView.onRefreshComplete();
104. }
105. }
和第一段的代码只有一个地方有区别,可能很难发现:
mPullRefreshListView.setOnRefreshListener(new OnRefreshListener2<ListView>(){});注意这里的接口类型是OnRefreshListener2,多了个2,和上面的不一样,这个接口包含两个方法,一个上拉回调,一个下拉回调。好了,这样我们就成功添加了上拉与下拉,并且分别可以控制其回调代码。
效果图:
咋样,是不是也很简单~注:如果你的上拉和下拉需求是执行一样的代码,那么你可以继续注册OnRefreshListener接口,上拉和下拉都会执行同一个方法。
接下来介绍如何使用带下拉刷新和加载更多的的GridView和自定义样式~
3、带下拉和上拉的GridView ( PullToRefreshGridView )
同样的pull-to-refresh把GridView封装为:PullToRefreshGridView 。用法和PullToRefreshListView一摸一样~
首先看主布局文件:
1. <?xml version="1.0" encoding="utf-8"?>
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3. android:layout_width="fill_parent"
4. android:layout_height="fill_parent"
5. android:orientation="vertical" >
6.
7. <!-- The PullToRefreshGridView replaces a standard GridView widget. -->
8.
9. <com.handmark.pulltorefresh.library.PullToRefreshGridView
10. xmlns:ptr="http://schemas.android.com/apk/res-auto"
11. android:id="@+id/pull_refresh_grid"
12. android:layout_width="fill_parent"
13. android:layout_height="fill_parent"
14. android:columnWidth="100dp"
15. android:gravity="center_horizontal"
16. android:horizontalSpacing="1dp"
17. android:numColumns="auto_fit"
18. android:stretchMode="columnWidth"
19. android:verticalSpacing="1dp"
20. ptr:ptrDrawable="@drawable/ic_launcher"
21. ptr:ptrMode="both" />
22.
23. </LinearLayout>
PullToRefreshGridView 的item的布局文件:
1. <?xml version="1.0" encoding="utf-8"?>
2. <TextView xmlns:android="http://schemas.android.com/apk/res/android"
3. android:id="@+id/id_grid_item_text"
4. android:layout_width="100dp"
5. android:gravity="center"
6. android:textColor="#ffffff"
7. android:textSize="16sp"
8. android:background="#000000"
9. android:layout_height="100dp" />
接下来就是Activity的代码了:
1. public class PullToRefreshGridActivity extends Activity
2. {
3. private LinkedList<String> mListItems;
4. private PullToRefreshGridView mPullRefreshListView;
5. private ArrayAdapter<String> mAdapter;
6.
7. private int mItemCount = 10;
8.
9. @Override
10. protected void onCreate(Bundle savedInstanceState)
11. {
12. super.onCreate(savedInstanceState);
13. setContentView(R.layout.activity_ptr_grid);
14. // 得到控件
15. mPullRefreshListView = (PullToRefreshGridView) findViewById(R.id.pull_refresh_grid);
16.
17. // 初始化数据和数据源
18. initDatas();
19.
20. new ArrayAdapter<String>(this, R.layout.grid_item,
21. R.id.id_grid_item_text, mListItems);
22. mPullRefreshListView.setAdapter(mAdapter);
23.
24. mPullRefreshListView
25. new OnRefreshListener2<GridView>()
26. {
27.
28. @Override
29. public void onPullDownToRefresh(
30. PullToRefreshBase<GridView> refreshView)
31. {
32. "TAG", "onPullDownToRefresh"); // Do work to
33. String label = DateUtils.formatDateTime(
34. getApplicationContext(),
35. System.currentTimeMillis(),
36. DateUtils.FORMAT_SHOW_TIME
37. | DateUtils.FORMAT_SHOW_DATE
38. | DateUtils.FORMAT_ABBREV_ALL);
39.
40. // Update the LastUpdatedLabel
41. refreshView.getLoadingLayoutProxy()
42. .setLastUpdatedLabel(label);
43.
44. new GetDataTask().execute();
45. }
46.
47. @Override
48. public void onPullUpToRefresh(
49. PullToRefreshBase<GridView> refreshView)
50. {
51. "TAG", "onPullUpToRefresh"); // Do work to refresh
52. // the list here.
53. new GetDataTask().execute();
54. }
55. });
56. }
57.
58. private void initDatas()
59. {
60. new LinkedList<String>();
61.
62. for (int i = 0; i < mItemCount; i++)
63. {
64. "");
65. }
66. }
67.
68. private class GetDataTask extends AsyncTask<Void, Void, Void>
69. {
70.
71. @Override
72. protected Void doInBackground(Void... params)
73. {
74. try
75. {
76. 2000);
77. catch (InterruptedException e)
78. {
79. }
80. return null;
81. }
82.
83. @Override
84. protected void onPostExecute(Void result)
85. {
86. "" + mItemCount++);
87. mAdapter.notifyDataSetChanged();
88. // Call onRefreshComplete when the list has been refreshed.
89. mPullRefreshListView.onRefreshComplete();
90. }
91. }
基本上上例没有任何区别,直接看效果图吧:
效果还是不错的,如果你比较细心会发现,那个下拉刷新的转圈的图片咋变成机器人了,那是因为我在布局文件里面设置了:
1. <com.handmark.pulltorefresh.library.PullToRefreshGridView
2. ptr:ptrDrawable="@drawable/ic_launcher"
3. ...
4. />
当然了这是旋转的效果,一般常用的还有,一个箭头倒置的效果,其实也很简单,一个属性:
ptr:ptrAnimationStyle="flip"
去掉 ptr:ptrDrawable="@drawable/ic_launcher"这个属性,如果你希望用下图默认的箭头,你也可以自定义。
添加后,箭头就是这个样子:
ptr:ptrAnimationStyle的取值:flip(翻转动画), rotate(旋转动画) 。
ptr:ptrDrawable则就是设置图标了。
4、自定义下拉指示器文本内容等效果
可以在初始化完成mPullRefreshListView后,通过mPullRefreshListView.getLoadingLayoutProxy()可以得到一个ILoadingLayout对象,这个对象可以设置各种指示器中的样式、文本等。
1. ILoadingLayout startLabels = mPullRefreshListView
2. .getLoadingLayoutProxy();
3. "你可劲拉,拉...");// 刚下拉时,显示的提示
4. "好嘞,正在刷新...");// 刷新时
5. "你敢放,我就敢刷新...");// 下来达到一定距离时,显示的提示
如果你比较细心,会发现,前面我们设置上次刷新时间已经用到了:
// Update the LastUpdatedLabel
refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label);
现在的效果是:
默认是上拉和下拉的字同时改变的,如果我希望单独改变呢?
1. private void initIndicator()
2. {
3. ILoadingLayout startLabels = mPullRefreshListView
4. true, false);
5. "你可劲拉,拉...");// 刚下拉时,显示的提示
6. "好嘞,正在刷新...");// 刷新时
7. "你敢放,我就敢刷新...");// 下来达到一定距离时,显示的提示
8.
9. ILoadingLayout endLabels = mPullRefreshListView.getLoadingLayoutProxy(
10. false, true);
11. "你可劲拉,拉2...");// 刚下拉时,显示的提示
12. "好嘞,正在刷新2...");// 刷新时
13. "你敢放,我就敢刷新2...");// 下来达到一定距离时,显示的提示
14. }
mPullRefreshListView.getLoadingLayoutProxy(true, false);接收两个参数,为true,false返回设置下拉的ILoadingLayout;为false,true返回设置上拉的。
5、常用的一些属性
当然了,pull-to-refresh在xml中还能定义一些属性:
ptrMode,ptrDrawable,ptrAnimationStyle这三个上面已经介绍过。
ptrRefreshableViewBackground 设置整个mPullRefreshListView的背景色
ptrHeaderBackground 设置下拉Header或者上拉Footer的背景色
ptrHeaderTextColor 用于设置Header与Footer中文本的颜色
ptrHeaderSubTextColor 用于设置Header与Footer中上次刷新时间的颜色
ptrShowIndicator如果为true会在mPullRefreshListView中出现icon,右上角和右下角,挺有意思的。
ptrHeaderTextAppearance , ptrSubHeaderTextAppearance分别设置拉Header或者上拉Footer中字体的类型颜色等等。
ptrRotateDrawableWhilePulling当动画设置为rotate时,下拉是是否旋转。
ptrScrollingWhileRefreshingEnabled刷新的时候,是否允许ListView或GridView滚动。觉得为true比较好。
ptrListViewExtrasEnabled 决定了Header,Footer以何种方式加入mPullRefreshListView,true为headView方式加入,就是滚动时刷新头部会一起滚动。
最后2个其实对于用户体验还是挺重要的,如果设置的时候考虑下~。其他的属性自己选择就好。
注:上述属性很多都可以代码控制,如果有需要可以直接mPullRefreshListView.set属性名 查看
以上为pull-to-refresh所有支持的属性~~
定义了一堆的效果:
右上、右下那个图标就是ptrShowIndicator。好了,大家可以按照自己的需求对着上面的属性解释配置。
在github上下载的例子,是依赖3个项目的,一个基本的library_pullToRefresh,一个PullToRefreshViewPager,一个PullToRefreshListFragment ;
上面介绍的例子只依赖library_pullToRefresh,其他两个依赖不用导入。
源码点击下载