Android中重写ListView实现下拉刷新

最近项目中刚好有下拉刷新的需求,于是找了一些开源的资料研究了一下,看到网上有很多实现方式,有的继承Linearlayout,有的重写ScrollView,还有的重写Gridview,看到最多的还是重写listview,这里采用哪一种方式的实现没有对错之分,关键要看需求的设计。

这里还是采用大家用的最多的listview实现下拉刷新的操作。

效果如下图:

device-2013-09-20-203440

device-2013-09-20-203530

下面我们来看下代码:

其主要代码是listview实现:

001 public class PullToRefreshListView extends ListView implements OnScrollListener {
002  
003     private final static String TAG = "PullToRefreshListView";
004  
005     // 下拉刷新标志
006     private final static int PULL_To_REFRESH = 0;
007     // 松开刷新标志
008     private final static int RELEASE_To_REFRESH = 1;
009     // 正在刷新标志
010     private final static int REFRESHING = 2;
011     // 刷新完成标志
012     private final static int DONE = 3;
013  
014     private LayoutInflater inflater;
015  
016     private LinearLayout headView;
017     private TextView tipsTextview;
018     private TextView lastUpdatedTextView;
019     private ImageView arrowImageView;
020     private ProgressBar progressBar;
021     // 用来设置箭头图标动画效果
022     private RotateAnimation animation;
023     private RotateAnimation reverseAnimation;
024  
025     // 用于保证startY的值在一个完整的touch事件中只被记录一次
026     private boolean isRecored;
027  
028     private int headContentWidth;
029     private int headContentHeight;
030     private int headContentOriginalTopPadding;
031  
032     private int startY;
033     private int firstItemIndex;
034     private int currentScrollState;
035  
036     private int state;
037  
038     private boolean isBack;
039  
040     public OnRefreshListener refreshListener;
041  
042     public PullToRefreshListView(Context context, AttributeSet attrs) {
043         super(context, attrs);
044         init(context);
045     }
046  
047     public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) {
048         super(context, attrs, defStyle);
049         init(context);
050     }
051  
052     private void init(Context context) {
053         //设置滑动效果
054         animation = new RotateAnimation(0, -180,
055                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,
056                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);
057         animation.setInterpolator(new LinearInterpolator());
058         animation.setDuration(100);
059         animation.setFillAfter(true);
060  
061         reverseAnimation = new RotateAnimation(-180, 0,
062                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,
063                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);
064         reverseAnimation.setInterpolator(new LinearInterpolator());
065         reverseAnimation.setDuration(100);
066         reverseAnimation.setFillAfter(true);
067  
068         inflater = LayoutInflater.from(context);
069         headView = (LinearLayout) inflater.inflate(R.layout.pull_to_refresh_head, null);
070  
071         arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView);
072         arrowImageView.setMinimumWidth(50);
073         arrowImageView.setMinimumHeight(50);
074         progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar);
075         tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);
076         lastUpdatedTextView = (TextView) headView.findViewById(R.id.head_lastUpdatedTextView);
077  
078         headContentOriginalTopPadding = headView.getPaddingTop();
079  
080         measureView(headView);
081         headContentHeight = headView.getMeasuredHeight();
082         headContentWidth = headView.getMeasuredWidth();
083  
084         headView.setPadding(headView.getPaddingLeft(), -1 * headContentHeight, headView.getPaddingRight(), headView.getPaddingBottom());
085         headView.invalidate();
086  
087         //System.out.println("初始高度:"+headContentHeight);
088         //System.out.println("初始TopPad:"+headContentOriginalTopPadding);
089  
090         addHeaderView(headView);
091         setOnScrollListener(this);
092     }
093  
094     public void onScroll(AbsListView view, int firstVisiableItem, int visibleItemCount,  int totalItemCount) {
095         firstItemIndex = firstVisiableItem;
096     }
097  
098     public void onScrollStateChanged(AbsListView view, int scrollState) {
099         currentScrollState = scrollState;
100     }
101  
102     public boolean onTouchEvent(MotionEvent event) {
103         switch (event.getAction()) {
104         case MotionEvent.ACTION_DOWN:
105             if (firstItemIndex == 0 && !isRecored) {
106                 startY = (int) event.getY();
107                 isRecored = true;
108                 //System.out.println("当前-按下高度-ACTION_DOWN-Y:"+startY);
109             }
110             break;
111  
112         case MotionEvent.ACTION_CANCEL://失去焦点&取消动作
113         case MotionEvent.ACTION_UP:
114  
115             if (state != REFRESHING) {
116                 if (state == DONE) {
117                     //System.out.println("当前-抬起-ACTION_UP:DONE什么都不做");
118                 }
119                 else if (state == PULL_To_REFRESH) {
120                     state = DONE;
121                     changeHeaderViewByState();
122                     //System.out.println("当前-抬起-ACTION_UP:PULL_To_REFRESH-->DONE-由下拉刷新状态到刷新完成状态");
123                 }
124                 else if (state == RELEASE_To_REFRESH) {
125                     state = REFRESHING;
126                     changeHeaderViewByState();
127                     onRefresh();
128                     //System.out.println("当前-抬起-ACTION_UP:RELEASE_To_REFRESH-->REFRESHING-由松开刷新状态,到刷新完成状态");
129                 }
130             }
131  
132             isRecored = false;
133             isBack = false;
134  
135             break;
136  
137         case MotionEvent.ACTION_MOVE:
138             int tempY = (int) event.getY();
139             //System.out.println("当前-滑动-ACTION_MOVE Y:"+tempY);
140             if (!isRecored && firstItemIndex == 0) {
141                 //System.out.println("当前-滑动-记录拖拽时的位置 Y:"+tempY);
142                 isRecored = true;
143                 startY = tempY;
144             }
145             if (state != REFRESHING && isRecored) {
146                 // 可以松开刷新了
147                 if (state == RELEASE_To_REFRESH) {
148                     // 往上推,推到屏幕足够掩盖head的程度,但还没有全部掩盖
149                     if ((tempY - startY < headContentHeight+20)
150                             && (tempY - startY) > 0) {
151                         state = PULL_To_REFRESH;
152                         changeHeaderViewByState();
153                         //System.out.println("当前-滑动-ACTION_MOVE:RELEASE_To_REFRESH--》PULL_To_REFRESH-由松开刷新状态转变到下拉刷新状态");
154                     }
155                     // 一下子推到顶
156                     else if (tempY - startY <= 0) {
157                         state = DONE;
158                         changeHeaderViewByState();
159                         //System.out.println("当前-滑动-ACTION_MOVE:RELEASE_To_REFRESH--》DONE-由松开刷新状态转变到done状态");
160                     }
161                     // 往下拉,或者还没有上推到屏幕顶部掩盖head
162                     else {
163                         // 不用进行特别的操作,只用更新paddingTop的值就行了
164                     }
165                 }
166                 // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
167                 else if (state == PULL_To_REFRESH) {
168                     // 下拉到可以进入RELEASE_TO_REFRESH的状态
169                     if (tempY - startY >= headContentHeight+20 && currentScrollState == SCROLL_STATE_TOUCH_SCROLL) {
170                         state = RELEASE_To_REFRESH;
171                         isBack = true;
172                         changeHeaderViewByState();
173                         //System.out.println("当前-滑动-PULL_To_REFRESH--》RELEASE_To_REFRESH-由done或者下拉刷新状态转变到松开刷新");
174                     }
175                     // 上推到顶了
176                     else if (tempY - startY <= 0) {
177                         state = DONE;
178                         changeHeaderViewByState();
179                         //System.out.println("当前-滑动-PULL_To_REFRESH--》DONE-由Done或者下拉刷新状态转变到done状态");
180                     }
181                 }
182                 // done状态下
183                 else if (state == DONE) {
184                     if (tempY - startY > 0) {
185                         state = PULL_To_REFRESH;
186                         changeHeaderViewByState();
187                         //System.out.println("当前-滑动-DONE--》PULL_To_REFRESH-由done状态转变到下拉刷新状态");
188                     }
189                 }
190  
191                 // 更新headView的size
192                 if (state == PULL_To_REFRESH) {
193                     int topPadding = (int)((-1 * headContentHeight + (tempY - startY)));
194                     headView.setPadding(headView.getPaddingLeft(), topPadding, headView.getPaddingRight(), headView.getPaddingBottom());
195                     headView.invalidate();
196                     //System.out.println("当前-下拉刷新PULL_To_REFRESH-TopPad:"+topPadding);
197                 }
198  
199                 // 更新headView的paddingTop
200                 if (state == RELEASE_To_REFRESH) {
201                     int topPadding = (int)((tempY - startY - headContentHeight));
202                     headView.setPadding(headView.getPaddingLeft(), topPadding, headView.getPaddingRight(), headView.getPaddingBottom());
203                     headView.invalidate();
204                     //System.out.println("当前-释放刷新RELEASE_To_REFRESH-TopPad:"+topPadding);
205                 }
206             }
207             break;
208         }
209         return super.onTouchEvent(event);
210     }
211  
212     // 当状态改变时候,调用该方法,以更新界面
213     private void changeHeaderViewByState() {
214         switch (state) {
215         case RELEASE_To_REFRESH:
216  
217             arrowImageView.setVisibility(View.VISIBLE);
218             progressBar.setVisibility(View.GONE);
219             tipsTextview.setVisibility(View.VISIBLE);
220             lastUpdatedTextView.setVisibility(View.VISIBLE);
221  
222             arrowImageView.clearAnimation();
223             arrowImageView.startAnimation(animation);
224  
225             tipsTextview.setText(R.string.pull_to_refresh_release_label);
226  
227             //Log.v(TAG, "当前状态,松开刷新");
228             break;
229         case PULL_To_REFRESH:
230  
231             progressBar.setVisibility(View.GONE);
232             tipsTextview.setVisibility(View.VISIBLE);
233             lastUpdatedTextView.setVisibility(View.VISIBLE);
234             arrowImageView.clearAnimation();
235             arrowImageView.setVisibility(View.VISIBLE);
236             if (isBack) {
237                 isBack = false;
238                 arrowImageView.clearAnimation();
239                 arrowImageView.startAnimation(reverseAnimation);
240             }
241             tipsTextview.setText(R.string.pull_to_refresh_pull_label);
242  
243             //Log.v(TAG, "当前状态,下拉刷新");
244             break;
245  
246         case REFRESHING:
247             //System.out.println("刷新REFRESHING-TopPad:"+headContentOriginalTopPadding);
248             headView.setPadding(headView.getPaddingLeft(), headContentOriginalTopPadding, headView.getPaddingRight(), headView.getPaddingBottom());
249             headView.invalidate();
250  
251             progressBar.setVisibility(View.VISIBLE);
252             arrowImageView.clearAnimation();
253             arrowImageView.setVisibility(View.GONE);
254             tipsTextview.setText(R.string.pull_to_refresh_refreshing_label);
255             lastUpdatedTextView.setVisibility(View.GONE);
256  
257             //Log.v(TAG, "当前状态,正在刷新...");
258             break;
259         case DONE:
260             //System.out.println("完成DONE-TopPad:"+(-1 * headContentHeight));
261             headView.setPadding(headView.getPaddingLeft(), -1 * headContentHeight, headView.getPaddingRight(), headView.getPaddingBottom());
262             headView.invalidate();
263  
264             progressBar.setVisibility(View.GONE);
265             arrowImageView.clearAnimation();
266             // 此处更换图标
267             arrowImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow);
268  
269             tipsTextview.setText(R.string.pull_to_refresh_pull_label);
270             lastUpdatedTextView.setVisibility(View.VISIBLE);
271  
272             //Log.v(TAG, "当前状态,done");
273             break;
274         }
275     }
276  
277     //点击刷新
278     public void clickRefresh() {
279         setSelection(0);
280         state = REFRESHING;
281         changeHeaderViewByState();
282         onRefresh();
283     }
284  
285     public void setOnRefreshListener(OnRefreshListener refreshListener) {
286         this.refreshListener = refreshListener;
287     }
288  
289     public interface OnRefreshListener {
290         public void onRefresh();
291     }
292  
293     public void onRefreshComplete(String update) {
294         lastUpdatedTextView.setText(update);
295         onRefreshComplete();
296     }
297  
298     public void onRefreshComplete() {
299         state = DONE;
300         changeHeaderViewByState();
301     }
302  
303     private void onRefresh() {
304         if (refreshListener != null) {
305             refreshListener.onRefresh();
306         }
307     }
308  
309     // 计算headView的width及height值
310     private void measureView(View child) {
311         ViewGroup.LayoutParams p = child.getLayoutParams();
312         if (p == null) {
313             p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
314                     ViewGroup.LayoutParams.WRAP_CONTENT);
315         }
316         int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
317         int lpHeight = p.height;
318         int childHeightSpec;
319         if (lpHeight > 0) {
320             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
321                     MeasureSpec.EXACTLY);
322         } else {
323             childHeightSpec = MeasureSpec.makeMeasureSpec(0,
324                     MeasureSpec.UNSPECIFIED);
325         }
326         child.measure(childWidthSpec, childHeightSpec);
327     }
328  
329 }

里面的注释可以说写的相当的详细,我这里就不详细解释了,大家可以直接复制到项目中去看,下面我们来看看,如何使用它:

01 pullrefresh_lv = (PullToRefreshListView) findViewById(R.id.pullrefresh_lv);
02         testArr = new LinkedList<String>();
03         testArr.add("↓↓请下拉我看效果↓↓");
04         testAdapter = new TestAdapter(testArr, this);
05         pullrefresh_lv.setAdapter(testAdapter);
06  
07         pullrefresh_lv
08                 .setOnRefreshListener(new PullToRefreshListView.OnRefreshListener() {
09                     public void onRefresh() {
10                         // 这里进行网络操作/数据库操作/文件操作
11                         handler.sendEmptyMessageDelayed(0, 2000);
12                     }
13                 });

使用起来也非常方便,和原生态listview无太多差别,只是多了一个 setOnRefreshListener方法,对的,这里就是处理刷新动作的地方,我这里没有进行网络操作,只是做了一个假数据演示(延迟刷新),下面来看看Handler里面做的处理:

01 Handler handler = new Handler() {
02         public void handleMessage(android.os.Message msg) {
03             switch (msg.what) {
04             case 0:
05                 i++;
06                 testArr.addFirst("刷新第" + i + "条");
07                 pullrefresh_lv
08                         .onRefreshComplete(getString(R.string.pull_to_refresh_update)
09                                 + new Date().toLocaleString());
10                 pullrefresh_lv.onRefreshComplete();
11                 testAdapter.notifyDataSetChanged();
12                 break;
13  
14             default:
15                 break;
16             }
17         };
18     };

这里当我们刷新动作完成后,调用onRefreshComplete()结束该动作,到这里,整个下来刷新的操作流程就讲完了。

因本站存储空间有限,稍后将源码上传其它社区,有问题请留言,感谢大家。
代码已上传,附上链接:http://www.eoeandroid.com/forum.php?mod=viewthread&tid=304996&page=1&extra=#pid3147702

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值