不废话,代码里面注释很详细,直接上代码:

自定义的RefreshableListView代码:

  1. public class RefreshableListView extends ListView implements OnScrollListener {
  2. private View header; // ListView顶部布局
  3. private LayoutInflater inflater;
  4. private int headerHeight; // 顶部布局Header的高度
  5. private int firstVisisblePosition; // 当前第一个可见的Item的位置
  6. private int scrollState; // ListView当前的滚动状态
  7.  
  8. private boolean remarkTop; // 标记,当前是在ListView的最顶端按下的
  9. private int startY; // 手指按下时的Y值
  10.  
  11. private int state; // 指示当前的状态
  12. private final int STATE_NORMAL = 0; // 正常状态
  13. private final int STATE_PULL = 1; // 提示“下拉可以刷新”的状态
  14. private final int STATE_TOREFRESH = 2; // 提示“松开手指刷新”的状态
  15. private final int STATE_REFRESHING = 3; // 正在刷新的状态
  16.  
  17. // Header布局中的四个控件
  18. private TextView refreshTip; // 显示“下拉可以刷新”/“松开手指刷新”的TextView
  19. private TextView timeTip; // 显示上次刷新的时间的TextView
  20. private ImageView arrowImg; // 向上/向下的箭头的ImageView
  21. private ProgressBar progressBar; // 刷新数据时用到的ProgressBar
  22.  
  23. private ListViewRefreshListener listener; // 刷新数据的接口
  24.  
  25. // 自定义控件都必须实现以下三个构造方法(一个参数、两个参数、三个参数的构造方法)
  26. // 我们在一个参数的构造方法中调用两个参数的构造方法,在两个参数的构造方法中调用三个参数的构造方法,这样不管我们用哪个构造方法,最终的调用代码是一样的
  27. // 一个参数的构造方法:这个方法是在Activity中根据上下文环境直接生成控件时调用的
  28. public RefreshableListView(Context context) {
  29. this(context, null);
  30. }
  31.  
  32. // 两个参数的构造方法:这个方法是在使用了系统属性,没有使用自定义属性时调用的
  33. public RefreshableListView(Context context, AttributeSet attrs) {
  34. this(context, attrs, 0);
  35. }
  36.  
  37. // 三个参数的构造方法:这个方法是在使用了自定义属性时调用的
  38. public RefreshableListView(Context context, AttributeSet attrs, int defStyleAttr) {
  39. super(context, attrs, defStyleAttr);
  40. initView(context);
  41. // 找到Header中的控件
  42. refreshTip = (TextView) header.findViewById(R.id.control_header_refreshtip);
  43. timeTip = (TextView) header.findViewById(R.id.control_header_timetip);
  44. arrowImg = (ImageView) header.findViewById(R.id.control_header_refresharrow);
  45. progressBar = (ProgressBar) header.findViewById(R.id.control_header_progressbar);
  46. }
  47.  
  48. // 初始化界面,添加顶部布局文件到ListView中
  49. private void initView(Context context) {
  50. inflater = LayoutInflater.from(context);
  51. header = inflater.inflate(R.layout.sideworks_layout_header, null);
  52. // 测量顶部布局header的高度
  53. measureView(context);
  54. headerHeight = header.getMeasuredHeight();
  55. setViewTopPadding(-headerHeight); // 设置ListView的上缩进:是负值,表示将header布局缩到屏幕外面去
  56. // 把顶部布局添加到ListView的最上面
  57. this.addHeaderView(header);
  58. // 设置向下滑动时逐渐显示顶部布局(接口回掉方法)
  59. this.setOnScrollListener(this);
  60. }
  61.  
  62. // 测量控件的宽高(通知父佈局:我佔用的寬和高)
  63. private void measureView(Context context) {
  64. ViewGroup.LayoutParams lp = header.getLayoutParams(); // 获取header布局的宽高属性
  65. if (lp == null) {
  66. lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
  67. }
  68. int width = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
  69. int height; // 不能用getChildMeasureSpec方法获取高度的原因是ListView的高度不确定,而宽度是确定的
  70. int tempHeight = lp.height;
  71. if (tempHeight > 0) { // 大于0说明定义了ListView的高度,所以我们用精确布局模式EXACTLY
  72. height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);
  73. } else { // 如果不大于0,则表示没有定义ListView的高度,即UNSPECIFIED
  74. height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
  75. }
  76. header.measure(width, height); // 这行代码很容易报错:NullPointerException,所以SDK17以前的版本必须将布局的最外层设置为LinearLayout
  77. }
  78.  
  79. // 设置ListView的TopPadding属性
  80. private void setViewTopPadding(int topPadding) {
  81. this.setPadding(this.getPaddingLeft(), topPadding, this.getPaddingRight(), this.getPaddingBottom());
  82. this.invalidate(); // invalidate()方法的作用是请求对该控件进行重绘
  83. }
  84.  
  85. @Override
  86. public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
  87. this.firstVisisblePosition = firstVisibleItem;
  88. }
  89.  
  90. @Override
  91. public void onScrollStateChanged(AbsListView view, int scrollState) {
  92. this.scrollState = scrollState;
  93. }
  94.  
  95. @Override
  96. public boolean onTouchEvent(MotionEvent ev) {
  97. switch (ev.getAction()) {
  98. case MotionEvent.ACTION_DOWN:
  99. if (firstVisisblePosition == 0) {
  100. remarkTop = true;
  101. startY = (int) ev.getY(); // 如果按下时是处在ListView最上面的Item,则记录当前的Y坐标值
  102. }
  103. break;
  104. case MotionEvent.ACTION_MOVE:
  105. onMove(ev);
  106. break;
  107. case MotionEvent.ACTION_UP:
  108. if (state == STATE_TOREFRESH) { // 滑动到了“松开手指刷新数据”的高度
  109. state = STATE_REFRESHING;
  110. refreshViewByState();
  111. listener.refreshListView(); // 调用接口,刷新数据
  112. } else if (state == STATE_PULL) { // 还是处在“下拉刷新数据”的高度
  113. state = STATE_NORMAL;
  114. remarkTop = false;
  115. refreshViewByState();
  116. }
  117. break;
  118. }
  119. return super.onTouchEvent(ev);
  120. }
  121.  
  122. // 判断移动过程中的操作
  123. private void onMove(MotionEvent ev) {
  124. if (!remarkTop) { // 如果按下地点不是ListView的第一个Item,则不做处理,正常滑动
  125. return;
  126. }
  127. int tempY = (int) ev.getY(); // 当前移动到了什么位置(Y坐标值)
  128. int space = tempY - startY; // 判断当前移动了多大距离(即header布局被拉下来多少),向下拉时是正值
  129. int topPadding = space - headerHeight; // 当前还在屏幕外面的header布局的高度
  130. switch (state) {
  131. case STATE_NORMAL:
  132. if (space > 0) {
  133. state = STATE_PULL;
  134. refreshViewByState();
  135. }
  136. break;
  137. case STATE_PULL:
  138. setViewTopPadding(topPadding);
  139. if (space > headerHeight && scrollState == SCROLL_STATE_TOUCH_SCROLL) { // 滑动过header高度的一半并且仍然在滑动
  140. state = STATE_TOREFRESH;
  141. refreshViewByState();
  142. }
  143. break;
  144. case STATE_TOREFRESH:
  145. setViewTopPadding(topPadding);
  146. if (space < headerHeight) {
  147. state = STATE_PULL;
  148. refreshViewByState();
  149. } else if (space <= 0) {
  150. state = STATE_NORMAL;
  151. remarkTop = false;
  152. refreshViewByState();
  153. }
  154. break;
  155. }
  156. }
  157.  
  158. // 根据当前状态,改变界面显示
  159. private void refreshViewByState() {
  160. // 箭头反转的两个动画
  161. RotateAnimation anim1 = new RotateAnimation(0, 180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
  162. anim1.setDuration(500);
  163. anim1.setFillAfter(true);
  164. RotateAnimation anim2 = new RotateAnimation(180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
  165. anim2.setDuration(500);
  166. anim2.setFillAfter(true);
  167.  
  168. switch (state) {
  169. case STATE_NORMAL:
  170. setViewTopPadding(-headerHeight);
  171. arrowImg.clearAnimation();
  172. break;
  173. case STATE_PULL:
  174. arrowImg.setVisibility(View.VISIBLE);
  175. progressBar.setVisibility(View.GONE);
  176. refreshTip.setText("下拉可以刷新!");
  177. arrowImg.clearAnimation();
  178. arrowImg.setAnimation(anim2);
  179. break;
  180. case STATE_TOREFRESH:
  181. arrowImg.setVisibility(View.VISIBLE);
  182. progressBar.setVisibility(View.GONE);
  183. refreshTip.setText("松开立即刷新!");
  184. arrowImg.clearAnimation();
  185. arrowImg.setAnimation(anim1);
  186. break;
  187. case STATE_REFRESHING:
  188. setViewTopPadding(0);
  189. arrowImg.setVisibility(View.GONE);
  190. progressBar.setVisibility(View.VISIBLE);
  191. refreshTip.setText("正在刷新......");
  192. arrowImg.clearAnimation();
  193. break;
  194. }
  195. }
  196.  
  197. public void onRefreshComplete() {
  198. state = STATE_NORMAL;
  199. remarkTop = false;
  200. refreshViewByState();
  201. String time = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
  202. timeTip.setText(time);
  203. }
  204.  
  205. // 刷新数据的接口,要通过接口回掉的方式更新数据
  206. public interface ListViewRefreshListener {
  207. public void refreshListView();
  208. }
  209.  
  210. public void setListViewRefreshListener(ListViewRefreshListener listener) {
  211. this.listener = listener;
  212. }
  213. }

header布局界面sideworks_layout_header.xml代码:

  1. <LinearLayout 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="50.0dip"
  5. android:background="@color/cl_header_bg"
  6. android:gravity="center"
  7. android:padding="10.0dip" >
  8.  
  9. <RelativeLayout
  10. android:layout_width="wrap_content"
  11. android:layout_height="wrap_content"
  12. android:background="@color/cl_transparent" >
  13.  
  14. <ImageView
  15. android:id="@+id/control_header_refresharrow"
  16. android:layout_width="wrap_content"
  17. android:layout_height="35.0dip"
  18. android:layout_centerVertical="true"
  19.  
  20. android:layout_marginRight="15.0dip"
  21. android:contentDescription="@string/app_name"
  22. android:src="@drawable/refresh_arrow" />
  23.  
  24. <ProgressBar
  25. android:id="@+id/control_header_progressbar"
  26. style="?android:attr/progressBarStyleSmall"
  27. android:layout_width="wrap_content"
  28. android:layout_height="wrap_content"
  29. android:layout_centerVertical="true"
  30. android:layout_marginRight="15.0dip"
  31. android:visibility="gone" />
  32.  
  33. <LinearLayout
  34. android:id="@+id/position_header_tips"
  35. android:layout_width="wrap_content"
  36. android:layout_height="wrap_content"
  37. android:layout_centerVertical="true"
  38. android:orientation="vertical"
  39. android:paddingLeft="30.0dip" >
  40.  
  41. <TextView
  42. android:id="@+id/control_header_refreshtip"
  43. android:layout_width="wrap_content"
  44. android:layout_height="wrap_content"
  45. android:text="@string/str_header_refreshtip"
  46. android:textColor="@color/cl_black"
  47. android:textSize="12.0sp" />
  48.  
  49. <TextView
  50. android:id="@+id/control_header_timetip"
  51. android:layout_width="wrap_content"
  52. android:layout_height="wrap_content"
  53. android:layout_marginTop="-7.0dip"
  54. android:textColor="@color/cl_black"
  55. android:textSize="12.0sp" />
  56. </LinearLayout>
  57. </RelativeLayout>
  58.  
  59. </LinearLayout>

主界面布局activity_main.xml代码:

  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.view.RefreshableListView
  7. android:id="@+id/control_main_listview"
  8. android:layout_width="match_parent"
  9. android:layout_height="match_parent"
  10. android:cacheColorHint="@color/cl_transparent" />
  11.  
  12. </RelativeLayout>

主界面MainActivity.java代码:

  1. public class MainActivity extends Activity implements ListViewRefreshListener {
  2. private RefreshableListView testList;
  3. public static List<String> dataList;
  4. public static ArrayAdapter<String> listAdapter;
  5.  
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_main);
  10. initView();
  11. }
  12.  
  13. private void initView() {
  14. testList = (RefreshableListView) findViewById(R.id.control_main_listview);
  15. testList.setListViewRefreshListener(this);
  16. dataList = getData();
  17. listAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1, dataList);
  18. testList.setAdapter(listAdapter);
  19. }
  20.  
  21. private List<String> getData() {
  22. dataList = new ArrayList<String>();
  23. for (int i = 0; i < 10; i++) {
  24. dataList.add("This is a test data.");
  25. }
  26. return dataList;
  27. }
  28.  
  29. @Override
  30. public void refreshListView() {
  31. // 延时两秒后显示两条新数据:This is a new data.
  32. new Handler().postDelayed(new Runnable() {
  33. public void run() {
  34. for (int i = 0; i < 2; i++) {
  35. dataList.add(0, "This is a new data.");
  36. }
  37. listAdapter.notifyDataSetChanged();
  38. testList.onRefreshComplete();
  39. }
  40. }, 2000);
  41. }
  42. }

Android之下拉刷新的ListView的更多相关文章

  1. android 之下拉刷新

    一.概述 Android 下拉刷新几乎是每个应用都必带的功能, 并且现在下拉刷新第三方库也越来越多了,很方便就能实现该功能, 下面我介绍一下 自己常用的几个方法. 二.例子 第一种方式:就是集成Lis ...

  2. Android之下拉刷新,上啦加载的实现(一)

    转载地址http://blog.csdn.net/leehong2005/article/details/12567757#t5 前段时间项目中用到了下拉刷新功能,之前在网上也找到过类似的demo,但 ...

  3. Xamarin.Android之下拉刷新

    一.前言 当今任何一个App中只要存在列表,基本上都会使用下拉刷新,而身为Xamarin一族的我们自然也不会落后,下面笔者将带领大家在Xamarin下实现Android中的下拉刷新的效果. 二.准备工 ...

  4. android之下拉刷新(reflush)

    package com.example.reflush; import android.app.ListActivity; import android.os.Bundle; import andro ...

  5. Android UI之下拉刷新上拉刷新实现

    在实际开发中我们经常要用到上拉刷新和下拉刷新,因此今天我写了一个上拉和下拉刷新的demo,有一个自定义的下拉刷新控件 只需要在布局文件中直接引用就可以使用,非常方便,非常使用,以下是源代码: 自定义的 ...

  6. 打造通用的Android下拉刷新组件(适用于ListView、GridView等各类View)

    前言 近期在做项目时,使用了一个开源的下拉刷新ListView组件.极其的不稳定,bug还多.稳定的组件又写得太复杂了,jar包较大.在我的一篇博客中也讲述过下拉刷新的实现,即Android打造(Li ...

  7. android 自定义控件之下拉刷新源码详解

    下拉刷新 是请求网络数据中经常会用的一种功能. 实现步骤如下: 1.新建项目   PullToRefreshDemo,定义下拉显示的头部布局pull_to_refresh_refresh.xml &l ...

  8. 【FastDev4Android框架开发】RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout(三十一)

    转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/49992269 本文出自:[江清清的博客] (一).前言: [好消息] ...

  9. [转]Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能

    版权声明:本文出自郭霖的博客,转载必须注明出处. 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9255575 最近项目中需要用到L ...

随机推荐

  1. webdriver学习笔记

    该篇文章记录本人在学习及使用webdriver做自动化测试时遇到的各种问题及解决方式,问题比较杂乱.问题的解决方式来源五花八门,如有疑问请随时指正一遍改正. 1.WebDriver入门 //webdr ...

  2. 《明解c语言》已看完,练习代码此奉上

    2016年9月20日至2016年11月12日,从学校图书馆借来的<明解c语言>看完了. 大三第一个学期,前8周,有c语言程序设计的课.课本是学校里的老师编写出版的,为了压缩空间,减少页面, ...

  3. 【51Nod 1244】莫比乌斯函数之和

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1244 模板题... 杜教筛和基于质因子分解的筛法都写了一下模板. 杜教筛 ...

  4. 【转】40条常见的移动端Web页面问题解决方案

    1.安卓浏览器看背景图片,有些设备会模糊                  2.图片加载                            3.假如手机网站不用兼容IE浏览器,一般我们会使用zep ...

  5. redis 学习笔记(6)-cluster集群搭建

    上次写redis的学习笔记还是2014年,一转眼已经快2年过去了,在段时间里,redis最大的变化之一就是cluster功能的正式发布,以前要搞redis集群,得借助一致性hash来自己搞shardi ...

  6. [LeetCode] Transpose File 转置文件

    Given a text file file.txt, transpose its content. You may assume that each row has the same number ...

  7. [LeetCode] Permutations II 全排列之二

    Given a collection of numbers that might contain duplicates, return all possible unique permutations ...

  8. C# 7.0 新功能代码范例

    随着新版本的IDE Visual Studio 15 紧锣密鼓的开发中,2016年8月24日,微软发布了与之配套的C# 7.0 preview 的新特性. 其主要特性有: 内联声明out变量 (Out ...

  9. telent对端口检测状态分析

    telnet基于TCP/IP协议通信的,把远程的shell反弹回本地! yum install -y telnet apt-get  install telnet ###端口被封,有墙堵住 [root ...

  10. spring 拦截器

    1.mvc.xml <!-- 自定义拦截链配置 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping p ...