Android之下拉刷新的ListView

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

自定义的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
42             <TextView
43                 android:id="@+id/control_header_refreshtip"
44                 android:layout_width="wrap_content"
45                 android:layout_height="wrap_content"
46                 android:text="@string/str_header_refreshtip"
47                 android:textColor="@color/cl_black"
48                 android:textSize="12.0sp" />
49
50             <TextView
51                 android:id="@+id/control_header_timetip"
52                 android:layout_width="wrap_content"
53                 android:layout_height="wrap_content"
54                 android:layout_marginTop="-7.0dip"
55                 android:textColor="@color/cl_black"
56                 android:textSize="12.0sp" />
57         </LinearLayout>
58     </RelativeLayout>
59
60 </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 }
时间: 2024-08-04 01:47:15

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

android 之下拉刷新

一.概述 Android 下拉刷新几乎是每个应用都必带的功能, 并且现在下拉刷新第三方库也越来越多了,很方便就能实现该功能, 下面我介绍一下 自己常用的几个方法. 二.例子 第一种方式:就是集成ListView实现自定义控件完成上下拉刷新 public class PullToRefreshListView extends ListView implements OnScrollListener, OnClickListener { /** * 下拉状态 */ private static fi

Xamarin.Android之下拉刷新

随笔- 9  文章- 0  评论- 144 再探.NET的PE文件结构(安全篇) 一.开篇 首先写在前面,这篇文章源于个人的研究和探索,由于.NET有自己的反射机制.可以清楚的将源代码反射出来.这样你的软件就非常easy被破解.当然这篇文章不会说怎么样保护你的软件不被破解.相反是借用一个软件来讲述是怎么被攻破的.也会有人说这是一篇破文,我事实上这篇文章已经写了非常长时间了.不知道以什么形式发出来,由于毕竟是有些破解类的东西.可是我认为从这篇文章相反的是可以带来一些启示.大家应该都知道Reflec

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

下拉刷新 是请求网络数据中经常会用的一种功能. 实现步骤如下: 1.新建项目   PullToRefreshDemo,定义下拉显示的头部布局pull_to_refresh_refresh.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmln

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

前言 最近在做项目时,使用了一个开源的下拉刷新ListView组件,极其的不稳定,bug还多.稳定的组件又写得太复杂了,jar包较大.在我的一篇博客中也讲述过下拉刷新的实现,即Android打造(ListView.GridView等)通用的下拉刷新.上拉自动加载的组件.但是这种通过修改Margin的形式感觉不是特别的流畅,因此在这漫长的国庆长假又花了点时间用另外的原理实现了一遍,特此分享出来. 基本原理 原理就是自定义一个ViewGroup,将Header View, Content View,

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

在实际开发中我们经常要用到上拉刷新和下拉刷新,因此今天我写了一个上拉和下拉刷新的demo,有一个自定义的下拉刷新控件 只需要在布局文件中直接引用就可以使用,非常方便,非常使用,以下是源代码: 自定义的ListView RTPullListView 1 package com.ryantang.pulllistview; 2 3 import java.util.Date; 4 5 import android.content.Context; 6 import android.util.Attr

Android之下拉与上拉刷新

转载请注明出处:http://blog.csdn.net/loveyaozu/article/details/51240525 Android日常开发中,对于下拉与上拉刷新控件的使用非常之频繁.一般都会采用第三方库,但是下拉刷新做到简单优雅并不是太多,甚至有的兼容性都存在问题.这个是不能接受的.最近在github上面看到一位哥们提供的下拉刷新的库非常不错,非常简洁.所以今天把它推荐给大家,供大家日常开发使用.文章结尾会给大家提供Demo供各位下载参考使用. Demo中一共提供四种下拉/上拉刷新.

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

转载请标明出处: http://blog.csdn.net/developer_jiangqq/article/details/49992269 本文出自:[江清清的博客] (一).前言: [好消息]个人网站已经上线运行,后面博客以及技术干货等精彩文章会同步更新,请大家关注收藏:http://www.lcode.org 话说RecyclerView已经面市很久,也在很多应用中得到广泛的使用,在整个开发者圈子里面也拥有很不错的口碑,那说明RecyclerView拥有比ListView,GridVi

App列表之下拉刷新

Android的ListView是应用最广的一个组件,功能强大,扩展性灵活(不局限于ListView本身一个类),前面的文章有介绍分组,拖拽,3D立体,游标,圆角,而今天我们要介绍的是另外一个扩展ListView:下拉刷新的ListView.    下拉刷新界面最初流行于iphone应用界面,如图: 然后在Android中也逐渐被应用,比如微博,资讯类.    所以,今天要实现的结果应该也是类似的,先贴出最终完成效果,如下图,接下来我们一步一步实现. 1. 流程分析    下拉刷新最主要的流程是

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

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9255575 最近项目中需要用到ListView下拉刷新的功能,一开始想图省事,在网上直接找一个现成的,可是尝试了网上多个版本的下拉刷新之后发现效果都不怎么理想.有些是因为功能不完整或有Bug,有些是因为使用起来太复杂,十全十美的还真没找到.因此我也是放弃了在网上找现成代码的想法,自己花功夫编写了一种非常简单的下拉刷新实现方案,现在拿出来和大家分享一下.相信在阅读完本篇文章之后,大