ListView实现下拉刷新功能

  好久没有写博客了,感受都懒惰了,今天说一下一个自定义的空间,就是ListView下拉列表能够刷新的功能,相信不少同窗都看到过这种功能,最典型的就是新浪微博的下拉刷新列表了。java

  废话很少说,首先呢,下拉刷新的那个带有progressBar的是ListView的headView,因此首先咱们须要自定义一个HeadView,以下所示:android

 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里面。app

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

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

  以下是代码:ui

  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 }

 

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

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

  

 

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

 

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

  若是手机继续在屏幕上滑动,会走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 + "\n");
 50 //        mTextView.append(Build.MANUFACTURER + "\n");
 51 //        mTextView.append(Build.DISPLAY + "\n");
 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 }

就这么多吧。

相关文章
相关标签/搜索