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 }

就這麼多吧。

相關文章
相關標籤/搜索