ListView实现下拉刷新功能

摘要:
我很久没有写博客了,我觉得很懒。今天,我将讨论一个自定义空间,它是可以刷新ListView下拉列表的函数。我相信很多学生都见过这个功能,最典型的是新浪微博的下拉刷新列表--最近更新了--˃596667686970FrameLayout包含两个图标,一个是下拉箭头,另一个是刷新的ProgressBar,因为这两个图标不会同时出现,所以我们将他们的房子放在FrameLayout中。HeadView已完成。我们需要继续使用ListView并添加HeadView,并在ListView中侦听onTouchEvent事件中的滑动。

  很久没有写博客了,感觉都懒惰了,今天说一下一个自定义的空间,就是ListView下拉列表可以刷新的功能,相信很多同学都看到过这种功能,最典型的就是新浪微博的下拉刷新列表了。

  废话不多说,首先呢,下拉刷新的那个带有progressBar的是ListView的headView,所以首先我们需要自定义一个HeadView,如下所示:

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

  FrameLayout里面是两个图标,一个是下拉的箭头,另一个是刷新的ProgressBar,因为这两个不会同时出现,所以我们把他们房子一个FrameLayout里面。

  右边是两个TextView,一个显示提示的信息,一个显示上次更新的详情。

  headView完成了,我们要继续ListView,并添加HeadView,在ListView中的onTouchEvent事件里面对滑动做监听之类的。

  如下是代码:

  1 package com.alex.helloworld;
  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.AbsListView.OnScrollListener;
 15 import android.widget.ImageView;
 16 import android.widget.LinearLayout;
 17 import android.widget.ListAdapter;
 18 import android.widget.ListView;
 19 import android.widget.ProgressBar;
 20 import android.widget.TextView;
 21 
 22 public class CustomListView extends ListView implements OnScrollListener {
 23 
 24     private final static int RELEASE_TO_REFRESH = 0;
 25     private final static int PULL_TO_REFRESH = 1;
 26     //正在刷新
 27     private final static int REFRESHING = 2;
 28     //刷新完成
 29     private final static int DONE = 3;
 30     private final static int LOADING = 4;
 31     
 32     private final static int RADIO = 3;
 33     
 34     private LayoutInflater mInflater;
 35     private LinearLayout mHeadView;
 36     private TextView mTipsTextView;
 37     private TextView mLastUpdatedTextView;
 38     private ImageView mArrowImageView;
 39     private ProgressBar mProgressBar;
 40     
 41     private RotateAnimation mAnimation;
 42     private RotateAnimation mReverseAnimation;
 43     private boolean mIsRecored;
 44     private int mHeadContentWidth;
 45     private int mHeadContentHeight;
 46     private int mStartY;
 47     private int mFirstItemIndex;
 48     private int mState;
 49     private boolean mIsBack;
 50     private boolean mISRefreshable;
 51     private OnRefreshListener mRefreshListener;
 52     
 53     public CustomListView(Context context, AttributeSet attrs) {
 54         super(context, attrs);
 55         init(context);
 56     }
 57 
 58     private void init(Context context) {
 59 //        setCacheColorHint(android.R.color.black);
 60         mInflater = LayoutInflater.from(context);
 61         mHeadView = (LinearLayout) mInflater.inflate(R.layout.head, null);
 62         mArrowImageView = (ImageView) mHeadView.findViewById(R.id.head_arrowImageView);
 63 //        mArrowImageView.setMinimumWidth(70);
 64 //        mArrowImageView.setMinimumHeight(50);
 65         mProgressBar = (ProgressBar) mHeadView.findViewById(R.id.head_progressBar);
 66         mTipsTextView = (TextView) mHeadView.findViewById(R.id.head_tipsTextView);
 67         mLastUpdatedTextView = (TextView) mHeadView.findViewById(R.id.head_lastUpdatedTextView);
 68         
 69         measureView(mHeadView);
 70         mHeadContentHeight = mHeadView.getMeasuredHeight();
 71         System.out.println("mHeadContentHeight = " + mHeadContentHeight);
 72         mHeadContentWidth = mHeadView.getMeasuredWidth();
 73         System.out.println("mHeadContentWidth = " + mHeadContentWidth);
 74         mHeadView.setPadding(0, -1 * mHeadContentHeight, 0, 0);
 75         mHeadView.invalidate();
 76         addHeaderView(mHeadView, null, false);
 77         setOnScrollListener(this);
 78         
 79         mAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
 80         mAnimation.setInterpolator(new LinearInterpolator());
 81         mAnimation.setDuration(250);
 82         mAnimation.setFillAfter(true);
 83         
 84         mReverseAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
 85         mReverseAnimation.setInterpolator(new LinearInterpolator());
 86         mReverseAnimation.setDuration(250);
 87         mReverseAnimation.setFillAfter(true);
 88         
 89         mState = DONE;
 90         mISRefreshable = false;
 91     }
 92     
 93     private void measureView(View child) {
 94         android.view.ViewGroup.LayoutParams params = child.getLayoutParams();
 95         System.out.println("params = " + params);
 96         if(params == null) {
 97             params = new LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
 98         }
 99         System.out.println("lpWidth = " + params.width);
100         int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0+0, params.width);
101         System.out.println("childWidthSpec = " + childWidthSpec);
102         int lpHeight = params.height;
103         System.out.println("lpHeight = " + lpHeight);
104         int childHeightSpec;
105         if(lpHeight > 0) {
106             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
107         } else {
108             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.UNSPECIFIED);
109         }
110         System.out.println("childHeightSpec = " + childHeightSpec);
111         child.measure(childWidthSpec, childHeightSpec);
112     }
113 
114     @Override
115     public void onScrollStateChanged(AbsListView view, int scrollState) {
116         
117     }
118 
119     @Override
120     public void onScroll(AbsListView view, int firstVisibleItem,
121             int visibleItemCount, int totalItemCount) {
122         mFirstItemIndex = firstVisibleItem;
123     }
124 
125     public interface OnRefreshListener {
126         public void onRefresh();
127     }
128     
129     private void onRefresh() {
130         if(mRefreshListener != null) {
131             mRefreshListener.onRefresh();
132         }
133     }
134     
135     public void onRefreshComplete() {
136         mState = DONE;
137         mLastUpdatedTextView.setText("已加载完成:" + new Date().toLocaleString());
138         changeHeaderViewByState();
139     }
140     
141     public void setonRefreshListener(OnRefreshListener onRefreshListener) {
142         this.mRefreshListener = onRefreshListener;
143         mISRefreshable = true;
144     }
145     
146     @Override
147     public boolean onTouchEvent(MotionEvent ev) {
148         if(mISRefreshable) {
149             switch (ev.getAction()) {
150             case MotionEvent.ACTION_DOWN:
151                 if(mFirstItemIndex == 0 && !mIsRecored) {
152                     mIsRecored = true;
153                     mStartY = (int) ev.getY();
154                 }
155                 break;
156 
157             case MotionEvent.ACTION_UP:
158                 if(mState != REFRESHING && mState != LOADING) {
159                     if(mState == DONE) {
160                         
161                     }
162                     if(mState == PULL_TO_REFRESH) {
163                         mState = DONE;
164                         changeHeaderViewByState();
165                     }
166                     if(mState == RELEASE_TO_REFRESH) {
167                         mState = REFRESHING;
168                         changeHeaderViewByState();
169                         onRefresh();
170                     }
171                 }
172                 mIsBack = false;
173                 mIsRecored = false;
174                 break;
175                 
176             case MotionEvent.ACTION_MOVE:
177                 int tempY = (int) ev.getY();
178                 if(!mIsRecored && mFirstItemIndex == 0) {
179                     mIsRecored = true;
180                     mStartY = tempY;
181                 }
182                 if(mState != REFRESHING && mIsRecored && mState != LOADING) {
183                     if(mState == RELEASE_TO_REFRESH) {
184                         setSelection(0);
185                         if((tempY - mStartY)/RADIO < mHeadContentHeight && (tempY - mStartY) > 0) {
186                             mState = PULL_TO_REFRESH;
187                             changeHeaderViewByState();
188                         } else if(tempY - mStartY <= 0) {
189                             mState = DONE;
190                             changeHeaderViewByState();
191                         }
192                     }
193                     
194                     if(mState == PULL_TO_REFRESH) {
195                         setSelection(0);
196                         if((tempY - mStartY)/RADIO >= mHeadContentHeight) {
197                             mState = RELEASE_TO_REFRESH;
198                             mIsBack = true;
199                             changeHeaderViewByState();
200                         }
201                     } else if(tempY - mStartY <= 0) {
202                         mState = DONE;
203                         changeHeaderViewByState();
204                     }
205                     
206                     if(mState == DONE) {
207                         if(tempY - mStartY > 0) {
208                             mState = PULL_TO_REFRESH;
209                             changeHeaderViewByState();
210                         }
211                     }
212                     
213                     if(mState == PULL_TO_REFRESH) {
214                         mHeadView.setPadding(0, -1 * mHeadContentHeight + (tempY - mStartY)/RADIO, 0, 0);
215                     }
216                     
217                     if(mState == RELEASE_TO_REFRESH) {
218                         mHeadView.setPadding(0, (tempY - mStartY)/RADIO - mHeadContentHeight, 0, 0);
219                     }
220                 }
221                 break;
222                 
223             default:
224                 break;
225             }
226         }
227         return super.onTouchEvent(ev);
228     }
229 
230     private void changeHeaderViewByState() {
231         switch (mState) {
232         case PULL_TO_REFRESH:
233             mProgressBar.setVisibility(GONE);
234             mTipsTextView.setVisibility(VISIBLE);
235             mLastUpdatedTextView.setVisibility(VISIBLE);
236             mArrowImageView.clearAnimation();
237             mArrowImageView.setVisibility(VISIBLE);
238             if(mIsBack) {
239                 mIsBack = false;
240                 mArrowImageView.clearAnimation();
241                 mArrowImageView.startAnimation(mReverseAnimation);
242                 mTipsTextView.setText("isBack is true!!!");
243             } else {
244                 mTipsTextView.setText("isBack is false!!!");
245             }
246             break;
247 
248         case DONE:
249             mHeadView.setPadding(0, -1 * mHeadContentHeight, 0, 0);
250             mProgressBar.setVisibility(GONE);
251             mArrowImageView.clearAnimation();
252             mArrowImageView.setImageResource(R.drawable.arrow);
253             mTipsTextView.setText("已经加载完毕-DONE");
254             mLastUpdatedTextView.setVisibility(VISIBLE);
255             break;
256             
257         case REFRESHING:
258             mHeadView.setPadding(0, 0, 0, 0);
259             mProgressBar.setVisibility(VISIBLE);
260             mArrowImageView.clearAnimation();
261             mArrowImageView.setVisibility(GONE);
262             mTipsTextView.setText("正在加载中……REFRESHING");
263             break;
264             
265         case RELEASE_TO_REFRESH:
266             mArrowImageView.setVisibility(VISIBLE);
267             mProgressBar.setVisibility(GONE);
268             mTipsTextView.setVisibility(VISIBLE);
269             mLastUpdatedTextView.setVisibility(VISIBLE);
270             mArrowImageView.clearAnimation();
271             mArrowImageView.startAnimation(mAnimation);
272             mTipsTextView.setText("请释放刷新");
273             break;
274         default:
275             break;
276         }
277     }
278     
279     @Override
280     public void setAdapter(ListAdapter adapter) {
281         mLastUpdatedTextView.setText("this is in MyListView:" + new Date().toLocaleString());
282         super.setAdapter(adapter);
283     }
284 }

  代码有点多,需要耐心的看,这里我主要说一下大体的思路,细节就不在说了

  还是先把截图看一下吧,对照着比较好说:

  ListView实现下拉刷新功能第1张

  代码里面写了一个接口,用作刷新用的。

  手指按下屏幕的时候会走Action down,这是有一堆判断条件,如果满足判断条件的话,就记录下Y轴的坐标

  如果手机继续在屏幕上滑动,会走action move,如果手指不松开,一直滑动,会一直走action move,这时也可以拿到Y轴的坐标,判断现在的状态,没有刷新,没有加载,刚开始的默认状态是done的,如果向下滑动,导致坐标差大于0,将状态改为PULL_TO_REFRESH,同时改变控件的一些状态,比如可见性等。

同时要改变headView的padding状态,导致headview渐渐的变为可见:if(mState == PULL_TO_REFRESH) {
      mHeadView.setPadding(0, -1 * mHeadContentHeight + (tempY - mStartY)/RADIO, 0, 0);
     }

如果把padding设置为-1*高度,就会导致headView看不见了。

这时手指继续下滑,如果终点和起点的差值/3 还要大于headView的高度的话,这时改变状态为RELEASE_TO_REFRESH,mState = RELEASE_TO_REFRESH;
       mIsBack = true;这就意味着松手就可以刷新了,当然是要满足终点和起点的差值/3 还要大于headView的高度,如果tempY - mStartY <= 0,说明在向回滑动,状态改为mState = DONE;同时改变控件的状态。

现在只能向回滑动,因为向下滑动不会有任何效果,(tempY - mStartY)/RADIO < mHeadContentHeight && (tempY - mStartY) > 0,继续下滑,改变状态不做操作,同时改变控件状态,向回滑动,改变状态,mState = DONE;同时改变控件状态。

当状态为mState == PULL_TO_REFRESH时,松开手指,就是action up,不做任何改变,当状态为mState == RELEASE_TO_REFRESH时,松手,开始刷新操作。

布局文件:

 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:orientation="vertical" >
 6     
 7     <com.alex.helloworld.CustomListView
 8         android:layout_width="match_parent"
 9         android:layout_height="match_parent"
10         android:id="@+id/listView"/>
11 
12 </LinearLayout>

Activity的文件:

  1 package com.alex.helloworld;
  2 
  3 import java.util.ArrayList;
  4 
  5 import com.alex.helloworld.CustomListView.OnRefreshListener;
  6 
  7 import android.app.Activity;
  8 import android.app.AlertDialog.Builder;
  9 import android.content.DialogInterface;
 10 import android.content.pm.PackageManager.NameNotFoundException;
 11 import android.os.AsyncTask;
 12 import android.os.Bundle;
 13 import android.view.Menu;
 14 import android.widget.ArrayAdapter;
 15 import android.widget.ListView;
 16 
 17 public class HelloWord extends Activity {
 18 
 19 //    private TextView mTextView;
 20     private SlidingLayout slidingLayout;
 21     private ListView contentList;
 22     private ArrayAdapter<String> contentListAdapter;
 23     private String[] contentItems = {
 24             "Content Item 1", "Content Item 2", "Content Item 3",
 25             "Content Item 4", "Content Item 5", "Content Item 6", "Content Item 7",
 26             "Content Item 8", "Content Item 9", "Content Item 10", "Content Item 11",
 27             "Content Item 12", "Content Item 13", "Content Item 14", "Content Item 15",
 28             "Content Item 16" 
 29     };
 30     
 31     private ArrayList<String> mArrays = new ArrayList<String>();
 32     
 33     @Override
 34     protected void onCreate(Bundle savedInstanceState) {
 35         super.onCreate(savedInstanceState);
 36         setContentView(R.layout.test);
 37         for(int i=0; i<contentItems.length; i++) {
 38             mArrays.add(contentItems[i]);
 39         }
 40 //        slidingLayout = (SlidingLayout) findViewById(R.id.sliding_layout);
 41 //        Button showLeftButton = (Button) findViewById(R.id.show_left_menu);
 42 //        Button showRightButton = (Button) findViewById(R.id.show_right_menu);
 43 //        contentList = (ListView) findViewById(R.id.contentList);
 44 //        contentListAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contentItems);
 45 //        contentList.setAdapter(contentListAdapter);
 46 //        mTextView = (TextView) findViewById(R.id.tv);
 47 //        mTextView.setText("");
 48 //        String product = Build.PRODUCT;
 49 //        mTextView.append(product + "
");
 50 //        mTextView.append(Build.MANUFACTURER + "
");
 51 //        mTextView.append(Build.DISPLAY + "
");
 52 //        mTextView.append(Build.VERSION.SDK);
 53 //        checkUpdate();
 54         
 55         final CustomListView listView = (CustomListView) findViewById(R.id.listView);
 56         final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mArrays);
 57         listView.setAdapter(adapter);
 58         listView.setonRefreshListener(new OnRefreshListener() {
 59             
 60             @Override
 61             public void onRefresh() {
 62                 new AsyncTask<Void, Void, Void> () {
 63 
 64                     @Override
 65                     protected Void doInBackground(Void... params) {
 66                         try {
 67                             Thread.sleep(1000);
 68                         } catch (InterruptedException e) {
 69                             e.printStackTrace();
 70                         }
 71                         mArrays.add("add a new one");
 72                         return null;
 73                     }
 74                     
 75                     protected void onPostExecute(Void result) {
 76                         adapter.notifyDataSetChanged();
 77                         listView.onRefreshComplete();
 78                     };
 79                     
 80                 }.execute();
 81             }
 82         });
 83     }
 84 
 85 
 86 //    @Override
 87 //    public boolean onCreateOptionsMenu(Menu menu) {
 88 //        getMenuInflater().inflate(R.menu.hello_word, menu);
 89 //        return true;
 90 //    }
 91     
 92 //    @Override
 93 //    protected void onDestroy() {
 94 //        super.onDestroy();
 95 //    }
 96     
 97 //    private void checkUpdate() {
 98 //        System.out.println("checkUpdate");
 99 //        int localVersion = 0;
100 //        int serverVersion = 0;
101 //        try {
102 //            localVersion = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
103 //            serverVersion = 2;
104 //        } catch (NameNotFoundException e) {
105 //            e.printStackTrace();
106 //        }
107 //        
108 //        if(serverVersion > localVersion) {
109 //            Builder builder = new Builder(this);
110 //            builder.setTitle("更新软件");
111 //            builder.setMessage("更新");
112 //            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
113 //                
114 //                @Override
115 //                public void onClick(DialogInterface dialog, int which) {
116 //                    
117 //                }
118 //            }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
119 //                
120 //                @Override
121 //                public void onClick(DialogInterface dialog, int which) {
122 //                    dialog.dismiss();
123 //                }
124 //            });
125 //            builder.show();
126 //        }
127 //    }
128 }

就这么多吧。

免责声明:文章转载自《ListView实现下拉刷新功能》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇进程间通信 Queue队列使用 生产者消费者模型 线程理论 创建及对象属性方法 线程互斥锁 守护线程python中常用的文件和目录操作(二)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

android悬浮按钮的使用

首先准备一张图片保存在drawable下 在activity_main.xml下 <?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/...

从0开始编译安卓 9.0

手机是Google Pixel XL 编译环境的准备 google 9月更新了地址 https://source.android.com/setup/build/initializinghttps://source.android.com/setup/build/building ubuntu 16.0.4 x64 需要安装的软件包 看上面 的网址 经过测...

Android_动态壁纸介绍

Android的动态壁纸主要用到三个类: android.app.WallpaperManager:这个类主要用于获取系统壁纸的信息。再写动态壁纸的时候用不到。 android.service.wallpaper.WallpaperService:这个类作为服务在运行,同时负责产生一个Engine。 同时,在此类中有一个必须继承的方法为:nCreateE...

Android跨应用启动Service

Android5.0之后规定只能通过显示Intent启动服务,所以掌握以下的启动方式很有必要 步骤一:创建两个安卓项目one,two 步骤二:在项目一中创建一个自定义类继承Service MyIntentService.java package com.contentprovide.liuliu.a2_3; import android.app.Int...

UE4在Android调用Project Tango

  Project Tango应该说是Google一试水AR的设备,其中Project Tango主要二个功能,一个是获取深度信息,如MS的Kinect,有相当多的设备都有这个功能,二是第一人称相对定位,这个就没那么常见了,如果对这个设备有更深的兴趣,可以看知乎上的这二个链接。   Google Project Tango获取深度信息的原理是什么?   P...

Android基于ksoap2调用WCF服务(二):Android篇

上一篇通过一个非常简单的例子,完成了一个WCF的服务。接下来介绍一下Android端。 Android端调用WCF,采用基于ksoap2包来实现。 下载地址(这个需要fan qiang访问,你们懂的):https://code.google.com/p/ksoap2-android/ 我用的3.3.0版本,我传到csdn上,上面地址如果不能访问,可以用这个...