ListView實現下拉刷新功能


  很久沒有寫博客了,感覺都懶惰了,今天說一下一個自定義的空間,就是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 }

 

  代碼有點多,需要耐心的看,這里我主要說一下大體的思路,細節就不在說了

  還是先把截圖看一下吧,對照着比較好說:

  

 

  代碼里面寫了一個接口,用作刷新用的。

 

  手指按下屏幕的時候會走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 + "\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 }

就這么多吧。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM