ListView下拉刷新實現


今天遇到一個下拉刷新的需求,但是和以往不同的是,不是頂部刷新,而是先有普通頭部,然后下拉刷新樣式頭部,要求下拉刷新時第一頭部不變,為實現此效果,特總結整理下相關知識點。

1.一個完整的過程:原始-下拉-釋放-刷新-原始

2.移動時:下拉-釋放、下拉-原始;釋放-下拉、釋放-原始(向上推);釋放-刷新(彈起);

3.手勢彈起時:原始、下拉、釋放、刷新(不用考慮)

4.是么時候可以執行下拉刷新?

   通過OnScrollListener監聽ListView滑動到了頂部,即firstVisibleItem=0時;當然onScrollStateChanged可以判斷是否顯示加載更多,這里就不討論了。

1    @Override
2     public void onScrollStateChanged(AbsListView view, int scrollState) {
3 
4     }
5     @Override
6     public void onScroll(AbsListView view, int firstVisibleItem,
7                          int visibleItemCount, int totalItemCount) {
8         mFirstItemIndex = firstVisibleItem;
9     }

5.需要手動計算headView的尺寸,因為寬度是充滿屏幕,可以直接獲取,高度不確定,所以需要手動去計算

 1   private void measureView(View child) {
 2         android.view.ViewGroup.LayoutParams params = child.getLayoutParams();
 3         System.out.println("params = " + params);
 4         if(params == null) {
 5             params = new LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
 6         }
 7         System.out.println("lpWidth = " + params.width);
 8         int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0+0, params.width);
 9         System.out.println("childWidthSpec = " + childWidthSpec);
10         int lpHeight = params.height;
11         System.out.println("lpHeight = " + lpHeight);
12         int childHeightSpec;
13         if(lpHeight > 0) {
14             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
15         } else {
16             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.UNSPECIFIED);
17         }
18         System.out.println("childHeightSpec = " + childHeightSpec);
19         child.measure(childWidthSpec, childHeightSpec);
20     }

6.在onTouchEvent事件里面對滑動做監聽,改變當前狀態

 1 @Override
 2     public boolean onTouchEvent(MotionEvent ev) {
 3         if(mISRefreshable) {
 4             switch (ev.getAction()) {
 5                 case MotionEvent.ACTION_DOWN:
 6                     if(mFirstItemIndex == 0 && !mIsRecored) {
 7                         mIsRecored = true;
 8                         mStartY = (int) ev.getY();
 9                     }
10                     break;
11 
12                 case MotionEvent.ACTION_UP:
13                     if(mState != REFRESHING) {
14                         if(mState == DONE) {
15 
16                         }
17                         if(mState == PULL_TO_REFRESH) {
18                             mState = DONE;
19                             changeHeaderViewByState();
20                         }
21                         if(mState == RELEASE_TO_REFRESH) {
22                             mState = REFRESHING;
23                             changeHeaderViewByState();
24                             onRefresh();
25                         }
26                     }
27                     mIsRecored = false;
28                     break;
29 
30                 case MotionEvent.ACTION_MOVE:
31                     int tempY = (int) ev.getY();
32                     if(!mIsRecored && mFirstItemIndex == 0) {
33                         mIsRecored = true;
34                         mStartY = tempY;
35                     }
36                     if(mState != REFRESHING && mIsRecored) {
37                         if(mState == RELEASE_TO_REFRESH) {//釋放狀態:向上推,
38                             setSelection(0);
39                             if((tempY - mStartY)/RADIO < mHeadContentHeight && (tempY - mStartY) > 0) {
40                                 mState = PULL_TO_REFRESH;
41                                 changeHeaderViewByState();
42                             } else if(tempY - mStartY <= 0) {
43                                 mState = DONE;
44                                 changeHeaderViewByState();
45                             }
46                         }
47 
48                         if(mState == PULL_TO_REFRESH) {
49                             setSelection(0);
50                             if((tempY - mStartY)/RADIO >= mHeadContentHeight) {
51                                 mState = RELEASE_TO_REFRESH;
52                                 changeHeaderViewByState();
53                             }
54                         } else if(tempY - mStartY <= 0) {
55                             mState = DONE;
56                             changeHeaderViewByState();
57                         }
58                         if(mState == DONE) {
59                             if(tempY - mStartY > 0) {
60                                 mState = PULL_TO_REFRESH;
61                                 changeHeaderViewByState();
62                             }
63                         }
64 
65                         if(mState == PULL_TO_REFRESH) {
66                             mHeadView.setPadding(0, -1 * mHeadContentHeight + (tempY - mStartY)/RADIO, 0, 0);
67                         }
68                         if(mState == RELEASE_TO_REFRESH) {
69                             mHeadView.setPadding(0, (tempY - mStartY)/RADIO - mHeadContentHeight, 0, 0);
70                         }
71                     }
72                     break;
73 
74                 default:
75                     break;
76             }
77         }
78         return super.onTouchEvent(ev);
79     }

最后:貼上完整代碼,可以直接拷貝使用哦!       

布局文件:  pull_to_refresh_header.xml  

<?xml version="1.0" encoding="utf-8"?>

<!-- ListView的頭部 -->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <!-- 內容 -->
    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        
        <!-- 箭頭頭像、進度條 -->
        <FrameLayout 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true">
            
            <!-- 箭頭 -->
            <ImageView 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:id="@+id/head_arrowImageView"
                android:src="@drawable/arrow"/>
            
            <!-- 進度條 -->
            <ProgressBar 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style="?android:attr/progressBarStyleSmall"
                android:layout_gravity="center"
                android:visibility="gone"
                android:id="@+id/head_progressBar"/>
        </FrameLayout>
        
        <!-- 提示、最近更新 -->
        <LinearLayout 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:orientation="vertical"
            android:gravity="center_horizontal">
            
            <!-- 提示 -->
            <TextView 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@android:color/white"
                android:id="@+id/head_tipsTextView"
                android:textSize="@dimen/head_tips_size"
                android:text="@string/head_tips_text"/>
            
            <!-- 最近更新 -->
            <TextView 
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/head_lastUpdatedTextView"
                android:textColor="@color/gold"
                android:text="@string/lash_updated_text"
                android:textSize="@dimen/last_updated_size"/>
        </LinearLayout>
        
    </RelativeLayout>

</LinearLayout>

 自定義類:PullToRefreshListView

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

 


免責聲明!

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



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