Android中ListView下拉刷新的实现

ListView中的下拉刷新是非常常见的,也是经常使用的,看到有很多同学想要,那我就整理一下,供大家参考。那我就不解释,直接上代码了。

这里需要自己重写一下ListView,重写代码如下:

  1 package net.loonggg.listview;
  2 
  3 import java.util.Date;
  4 
  5 import android.content.Context;
  6 import android.util.AttributeSet;
  7 import android.view.LayoutInflater;
  8 import android.view.MotionEvent;
  9 import android.view.View;
 10 import android.view.ViewGroup;
 11 import android.view.animation.LinearInterpolator;
 12 import android.view.animation.RotateAnimation;
 13 import android.widget.AbsListView;
 14 import android.widget.ImageView;
 15 import android.widget.LinearLayout;
 16 import android.widget.ListView;
 17 import android.widget.ProgressBar;
 18 import android.widget.TextView;
 19 import android.widget.AbsListView.OnScrollListener;
 20 
 21 public class MyListView extends ListView implements OnScrollListener {
 22 
 23     private final static int RELEASE_To_REFRESH = 0;// 下拉过程的状态值
 24     private final static int PULL_To_REFRESH = 1; // 从下拉返回到不刷新的状态值
 25     private final static int REFRESHING = 2;// 正在刷新的状态值
 26     private final static int DONE = 3;
 27     private final static int LOADING = 4;
 28 
 29     // 实际的padding的距离与界面上偏移距离的比例
 30     private final static int RATIO = 3;
 31     private LayoutInflater inflater;
 32 
 33     // ListView头部下拉刷新的布局
 34     private LinearLayout headerView;
 35     private TextView lvHeaderTipsTv;
 36     private TextView lvHeaderLastUpdatedTv;
 37     private ImageView lvHeaderArrowIv;
 38     private ProgressBar lvHeaderProgressBar;
 39 
 40     // 定义头部下拉刷新的布局的高度
 41     private int headerContentHeight;
 42 
 43     private RotateAnimation animation;
 44     private RotateAnimation reverseAnimation;
 45 
 46     private int startY;
 47     private int state;
 48     private boolean isBack;
 49 
 50     // 用于保证startY的值在一个完整的touch事件中只被记录一次
 51     private boolean isRecored;
 52 
 53     private OnRefreshListener refreshListener;
 54 
 55     private boolean isRefreshable;
 56 
 57     public MyListView(Context context) {
 58         super(context);
 59         init(context);
 60     }
 61 
 62     public MyListView(Context context, AttributeSet attrs) {
 63         super(context, attrs);
 64         init(context);
 65     }
 66 
 67     private void init(Context context) {
 68         setCacheColorHint(context.getResources().getColor(R.color.transparent));
 69         inflater = LayoutInflater.from(context);
 70         headerView = (LinearLayout) inflater.inflate(R.layout.lv_header, null);
 71         lvHeaderTipsTv = (TextView) headerView
 72                 .findViewById(R.id.lvHeaderTipsTv);
 73         lvHeaderLastUpdatedTv = (TextView) headerView
 74                 .findViewById(R.id.lvHeaderLastUpdatedTv);
 75 
 76         lvHeaderArrowIv = (ImageView) headerView
 77                 .findViewById(R.id.lvHeaderArrowIv);
 78         // 设置下拉刷新图标的最小高度和宽度
 79         lvHeaderArrowIv.setMinimumWidth(70);
 80         lvHeaderArrowIv.setMinimumHeight(50);
 81 
 82         lvHeaderProgressBar = (ProgressBar) headerView
 83                 .findViewById(R.id.lvHeaderProgressBar);
 84         measureView(headerView);
 85         headerContentHeight = headerView.getMeasuredHeight();
 86         // 设置内边距,正好距离顶部为一个负的整个布局的高度,正好把头部隐藏
 87         headerView.setPadding(0, -1 * headerContentHeight, 0, 0);
 88         // 重绘一下
 89         headerView.invalidate();
 90         // 将下拉刷新的布局加入ListView的顶部
 91         addHeaderView(headerView, null, false);
 92         setOnScrollListener(this);
 93         // 设置旋转动画事件
 94         animation = new RotateAnimation(0, -180,
 95                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,
 96                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);
 97         animation.setInterpolator(new LinearInterpolator());
 98         animation.setDuration(250);
 99         animation.setFillAfter(true);
100 
101         reverseAnimation = new RotateAnimation(-180, 0,
102                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,
103                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);
104         reverseAnimation.setInterpolator(new LinearInterpolator());
105         reverseAnimation.setDuration(200);
106         reverseAnimation.setFillAfter(true);
107 
108         // 一开始的状态就是下拉刷新完的状态,所以为DONE
109         state = DONE;
110         // 是否正在刷新
111         isRefreshable = false;
112     }
113 
114     @Override
115     public boolean onTouchEvent(MotionEvent ev) {
116         if (isRefreshable) {
117             switch (ev.getAction()) {
118             case MotionEvent.ACTION_DOWN:
119                 if (!isRecored) {
120                     isRecored = true;
121                     startY = (int) ev.getY();// 手指按下时记录当前位置
122                 }
123                 break;
124             case MotionEvent.ACTION_UP:
125                 if (state != REFRESHING && state != LOADING) {
126                     if (state == PULL_To_REFRESH) {
127                         state = DONE;
128                         changeHeaderViewByState();
129                     }
130                     if (state == RELEASE_To_REFRESH) {
131                         state = REFRESHING;
132                         changeHeaderViewByState();
133                         onLvRefresh();
134                     }
135                 }
136                 isRecored = false;
137                 isBack = false;
138 
139                 break;
140 
141             case MotionEvent.ACTION_MOVE:
142                 int tempY = (int) ev.getY();
143                 if (!isRecored) {
144                     isRecored = true;
145                     startY = tempY;
146                 }
147                 if (state != REFRESHING && isRecored && state != LOADING) {
148                     // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
149                     // 可以松手去刷新了
150                     if (state == RELEASE_To_REFRESH) {
151                         setSelection(0);
152                         // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步
153                         if (((tempY - startY) / RATIO < headerContentHeight)// 由松开刷新状态转变到下拉刷新状态
154                                 && (tempY - startY) > 0) {
155                             state = PULL_To_REFRESH;
156                             changeHeaderViewByState();
157                         }
158                         // 一下子推到顶了
159                         else if (tempY - startY <= 0) {// 由松开刷新状态转变到done状态
160                             state = DONE;
161                             changeHeaderViewByState();
162                         }
163                     }
164                     // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
165                     if (state == PULL_To_REFRESH) {
166                         setSelection(0);
167                         // 下拉到可以进入RELEASE_TO_REFRESH的状态
168                         if ((tempY - startY) / RATIO >= headerContentHeight) {// 由done或者下拉刷新状态转变到松开刷新
169                             state = RELEASE_To_REFRESH;
170                             isBack = true;
171                             changeHeaderViewByState();
172                         }
173                         // 上推到顶了
174                         else if (tempY - startY <= 0) {// 由DOne或者下拉刷新状态转变到done状态
175                             state = DONE;
176                             changeHeaderViewByState();
177                         }
178                     }
179                     // done状态下
180                     if (state == DONE) {
181                         if (tempY - startY > 0) {
182                             state = PULL_To_REFRESH;
183                             changeHeaderViewByState();
184                         }
185                     }
186                     // 更新headView的size
187                     if (state == PULL_To_REFRESH) {
188                         headerView.setPadding(0, -1 * headerContentHeight
189                                 + (tempY - startY) / RATIO, 0, 0);
190 
191                     }
192                     // 更新headView的paddingTop
193                     if (state == RELEASE_To_REFRESH) {
194                         headerView.setPadding(0, (tempY - startY) / RATIO
195                                 - headerContentHeight, 0, 0);
196                     }
197 
198                 }
199                 break;
200 
201             default:
202                 break;
203             }
204         }
205         return super.onTouchEvent(ev);
206     }
207 
208     // 当状态改变时候,调用该方法,以更新界面
209     private void changeHeaderViewByState() {
210         switch (state) {
211         case RELEASE_To_REFRESH:
212             lvHeaderArrowIv.setVisibility(View.VISIBLE);
213             lvHeaderProgressBar.setVisibility(View.GONE);
214             lvHeaderTipsTv.setVisibility(View.VISIBLE);
215             lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);
216 
217             lvHeaderArrowIv.clearAnimation();// 清除动画
218             lvHeaderArrowIv.startAnimation(animation);// 开始动画效果
219 
220             lvHeaderTipsTv.setText("松开刷新");
221             break;
222         case PULL_To_REFRESH:
223             lvHeaderProgressBar.setVisibility(View.GONE);
224             lvHeaderTipsTv.setVisibility(View.VISIBLE);
225             lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);
226             lvHeaderArrowIv.clearAnimation();
227             lvHeaderArrowIv.setVisibility(View.VISIBLE);
228             // 是由RELEASE_To_REFRESH状态转变来的
229             if (isBack) {
230                 isBack = false;
231                 lvHeaderArrowIv.clearAnimation();
232                 lvHeaderArrowIv.startAnimation(reverseAnimation);
233 
234                 lvHeaderTipsTv.setText("下拉刷新");
235             } else {
236                 lvHeaderTipsTv.setText("下拉刷新");
237             }
238             break;
239 
240         case REFRESHING:
241 
242             headerView.setPadding(0, 0, 0, 0);
243 
244             lvHeaderProgressBar.setVisibility(View.VISIBLE);
245             lvHeaderArrowIv.clearAnimation();
246             lvHeaderArrowIv.setVisibility(View.GONE);
247             lvHeaderTipsTv.setText("正在刷新...");
248             lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);
249             break;
250         case DONE:
251             headerView.setPadding(0, -1 * headerContentHeight, 0, 0);
252 
253             lvHeaderProgressBar.setVisibility(View.GONE);
254             lvHeaderArrowIv.clearAnimation();
255             lvHeaderArrowIv.setImageResource(R.drawable.arrow);
256             lvHeaderTipsTv.setText("下拉刷新");
257             lvHeaderLastUpdatedTv.setVisibility(View.VISIBLE);
258             break;
259         }
260     }
261 
262     // 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height
263     private void measureView(View child) {
264         ViewGroup.LayoutParams params = child.getLayoutParams();
265         if (params == null) {
266             params = new ViewGroup.LayoutParams(
267                     ViewGroup.LayoutParams.FILL_PARENT,
268                     ViewGroup.LayoutParams.WRAP_CONTENT);
269         }
270         int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0,
271                 params.width);
272         int lpHeight = params.height;
273         int childHeightSpec;
274         if (lpHeight > 0) {
275             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
276                     MeasureSpec.EXACTLY);
277         } else {
278             childHeightSpec = MeasureSpec.makeMeasureSpec(0,
279                     MeasureSpec.UNSPECIFIED);
280         }
281         child.measure(childWidthSpec, childHeightSpec);
282     }
283 
284     public void setonRefreshListener(OnRefreshListener refreshListener) {
285         this.refreshListener = refreshListener;
286         isRefreshable = true;
287     }
288 
289     public interface OnRefreshListener {
290         public void onRefresh();
291     }
292 
293     public void onRefreshComplete() {
294         state = DONE;
295         lvHeaderLastUpdatedTv.setText("最近更新:" + new Date().toLocaleString());
296         changeHeaderViewByState();
297     }
298 
299     private void onLvRefresh() {
300         if (refreshListener != null) {
301             refreshListener.onRefresh();
302         }
303     }
304 
305     public void setAdapter(LvAdapter adapter) {
306         lvHeaderLastUpdatedTv.setText("最近更新:" + new Date().toLocaleString());
307         super.setAdapter(adapter);
308     }
309 
310     @Override
311     public void onScrollStateChanged(AbsListView view, int scrollState) {
312 
313     }
314 
315     @Override
316     public void onScroll(AbsListView view, int firstVisibleItem,
317             int visibleItemCount, int totalItemCount) {
318         if (firstVisibleItem == 0) {
319             isRefreshable = true;
320         } else {
321             isRefreshable = false;
322         }
323 
324     }
325 
326 }
View Code

重写完ListView之后,在布局文件中是这么使用的,头部下拉刷新的布局文件lv_header.xml的代码如下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!-- ListView的头部 -->
 3 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 4     android:layout_width="fill_parent"
 5     android:layout_height="wrap_content"
 6     android:background="#000000" >
 7 
 8     <!-- 内容 -->
 9 
10     <RelativeLayout
11         android:id="@+id/head_contentLayout"
12         android:layout_width="fill_parent"
13         android:layout_height="wrap_content"
14         android:paddingLeft="30dp" >
15 
16         <!-- 箭头图像、进度条 -->
17 
18         <FrameLayout
19             android:layout_width="wrap_content"
20             android:layout_height="wrap_content"
21             android:layout_alignParentLeft="true"
22             android:layout_centerVertical="true" >
23 
24             <!-- 箭头 -->
25 
26             <ImageView
27                 android:id="@+id/lvHeaderArrowIv"
28                 android:layout_width="wrap_content"
29                 android:layout_height="wrap_content"
30                 android:layout_gravity="center"
31                 android:src="@drawable/arrow" />
32 
33             <!-- 进度条 -->
34 
35             <ProgressBar
36                 android:id="@+id/lvHeaderProgressBar"
37                 style="?android:attr/progressBarStyleSmall"
38                 android:layout_width="wrap_content"
39                 android:layout_height="wrap_content"
40                 android:layout_gravity="center"
41                 android:visibility="gone" />
42         </FrameLayout>
43 
44         <!-- 提示、最近更新 -->
45 
46         <LinearLayout
47             android:layout_width="wrap_content"
48             android:layout_height="wrap_content"
49             android:layout_centerHorizontal="true"
50             android:gravity="center_horizontal"
51             android:orientation="vertical" >
52 
53             <!-- 提示 -->
54 
55             <TextView
56                 android:id="@+id/lvHeaderTipsTv"
57                 android:layout_width="wrap_content"
58                 android:layout_height="wrap_content"
59                 android:text="下拉刷新"
60                 android:textColor="@color/white"
61                 android:textSize="20sp" />
62 
63             <!-- 最近更新 -->
64 
65             <TextView
66                 android:id="@+id/lvHeaderLastUpdatedTv"
67                 android:layout_width="wrap_content"
68                 android:layout_height="wrap_content"
69                 android:text="上次更新"
70                 android:textColor="@color/gold"
71                 android:textSize="10sp" />
72         </LinearLayout>
73     </RelativeLayout>
74 
75 </LinearLayout>
View Code

在Main.xml中进行设置,代码如下:

 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:background="#000000"
 6     android:orientation="vertical" >
 7 
 8     <net.loonggg.listview.MyListView
 9         android:id="@+id/lv"
10         android:layout_width="fill_parent"
11         android:layout_height="fill_parent" />
12 
13 </LinearLayout>
View Code

然后就是在MainActivity中实现,代码如下:

 1 package net.loonggg.listview;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 import net.loonggg.listview.MyListView.OnRefreshListener;
 7 import android.app.Activity;
 8 import android.os.AsyncTask;
 9 import android.os.Bundle;
10 import android.view.View;
11 
12 public class MainActivity extends Activity {
13     private List<String> list;
14     private MyListView lv;
15     private LvAdapter adapter;
16 
17     @Override
18     protected void onCreate(Bundle savedInstanceState) {
19         super.onCreate(savedInstanceState);
20         setContentView(R.layout.activity_main);
21         lv = (MyListView) findViewById(R.id.lv);
22         list = new ArrayList<String>();
23         list.add("loonggg");
24         list.add("我们都是开发者");
25         list.add("我们都是开发者");
26         list.add("我们都是开发者");
27         list.add("我们都是开发者");
28         list.add("我们都是开发者");
29         list.add("我们都是开发者");
30         list.add("我们都是开发者");
31         list.add("我们都是开发者");
32         list.add("我们都是开发者");
33         list.add("我们都是开发者");
34         list.add("我们都是开发者");
35         list.add("我们都是开发者");
36         list.add("我们都是开发者");
37         list.add("我们都是开发者");
38         list.add("我们都是开发者");
39         list.add("我们都是开发者");
40         adapter = new LvAdapter(list, this);
41         lv.setAdapter(adapter);
42 
43         lv.setonRefreshListener(new OnRefreshListener() {
44 
45             @Override
46             public void onRefresh() {
47                 new AsyncTask<Void, Void, Void>() {
48                     protected Void doInBackground(Void... params) {
49                         try {
50                             Thread.sleep(1000);
51                         } catch (Exception e) {
52                             e.printStackTrace();
53                         }
54                         list.add("刷新后添加的内容");
55                         return null;
56                     }
57 
58                     @Override
59                     protected void onPostExecute(Void result) {
60                         adapter.notifyDataSetChanged();
61                         lv.onRefreshComplete();
62                     }
63                 }.execute(null, null, null);
64             }
65         });
66     }
67 }
View Code

这里还需要为ListView设置一下Adapter,自定义的Adapter如下:

 1 package net.loonggg.listview;
 2 
 3 import java.util.List;
 4 
 5 import android.content.Context;
 6 import android.view.View;
 7 import android.view.ViewGroup;
 8 import android.widget.BaseAdapter;
 9 import android.widget.TextView;
10 
11 public class LvAdapter extends BaseAdapter {
12     private List<String> list;
13     private Context context;
14 
15     public LvAdapter(List<String> list, Context context) {
16         this.list = list;
17         this.context = context;
18     }
19 
20     @Override
21     public int getCount() {
22         return list.size();
23     }
24 
25     @Override
26     public Object getItem(int position) {
27         return list.get(position);
28     }
29 
30     @Override
31     public long getItemId(int position) {
32         return position;
33     }
34 
35     @Override
36     public View getView(int position, View convertView, ViewGroup parent) {
37         TextView tv = new TextView(context.getApplicationContext());
38         tv.setText(list.get(position));
39         return tv;
40     }
41 
42 }
View Code

到这里就完了,代码中的解释非常详细,具体的我就不多说了,也不解释了,自己看看并研究吧!

非著名程序员可能是东半球最好的技术分享公众号。每天,每周定时推送一些有关移动开发的原创文章和教程,微信号:smart_android。
原文地址:https://www.cnblogs.com/loonggg/p/3201505.html