首页 > 代码库 > Android UI之下拉刷新上拉刷新实现

Android UI之下拉刷新上拉刷新实现

在实际开发中我们经常要用到上拉刷新和下拉刷新,因此今天我写了一个上拉和下拉刷新的demo,有一个自定义的下拉刷新控件

只需要在布局文件中直接引用就可以使用,非常方便,非常使用,以下是源代码;

 

自定义的ListView RTPullListView

  1 package com.ryantang.pulllistview;  2   3 import java.util.Date;  4   5 import android.content.Context;  6 import android.util.AttributeSet;  7 import android.util.Log;  8 import android.view.LayoutInflater;  9 import android.view.MotionEvent; 10 import android.view.View; 11 import android.view.ViewGroup; 12 import android.view.animation.LinearInterpolator; 13 import android.view.animation.RotateAnimation; 14 import android.widget.AbsListView; 15 import android.widget.AbsListView.OnScrollListener; 16 import android.widget.BaseAdapter; 17 import android.widget.ImageView; 18 import android.widget.LinearLayout; 19 import android.widget.ListView; 20 import android.widget.ProgressBar; 21 import android.widget.TextView; 22  23 public class RTPullListView extends ListView implements OnScrollListener {   24     private static final String TAG = "RTPullListView"; 25  26     private final static int RELEASE_To_REFRESH = 0; 27     private final static int PULL_To_REFRESH = 1; 28     private final static int REFRESHING = 2; 29     private final static int DONE = 3; 30     private final static int LOADING = 4; 31  32     // 实际的padding的距离与界面上偏移距离的比例 33     private final static int RATIO = 3; 34     private LayoutInflater inflater; 35     private LinearLayout headView; 36     private TextView tipsTextview; 37     private TextView lastUpdatedTextView; 38     private ImageView arrowImageView; 39     private ProgressBar progressBar; 40  41     private RotateAnimation animation; 42     private RotateAnimation reverseAnimation; 43  44     // 用于保证startY的值在一个完整的touch事件中只被记录一次 45     private boolean isRecored; 46  47 //    private int headContentWidth; 48     private int headContentHeight; 49  50     private int startY; 51     private int firstItemIndex; 52     private int state; 53     private boolean isBack; 54     private OnRefreshListener refreshListener; 55  56     private boolean isRefreshable; 57     private boolean isPush; 58  59     private int visibleLastIndex; 60     private int visibleItemCount; 61  62     public RTPullListView(Context context) { 63         super(context); 64         init(context); 65     } 66  67     public RTPullListView(Context context, AttributeSet attrs) { 68         super(context, attrs); 69         init(context); 70     } 71      72     private void init(Context context) { 73         inflater = LayoutInflater.from(context); 74         headView = (LinearLayout) inflater.inflate(R.layout.pulllist_head, null); 75         arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView); 76 //        arrowImageView.setMinimumWidth(70); 77 //        arrowImageView.setMinimumHeight(50); 78         progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar); 79         tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView); 80         lastUpdatedTextView = (TextView) headView.findViewById(R.id.head_lastUpdatedTextView); 81  82         measureView(headView); 83         headContentHeight = headView.getMeasuredHeight(); 84 //        headContentWidth = headView.getMeasuredWidth(); 85  86         headView.setPadding(0, -1 * headContentHeight, 0, 0); 87         headView.invalidate(); 88  89         addHeaderView(headView, null, false); 90         setOnScrollListener(this); 91  92         animation = new RotateAnimation(0, -180, 93                 RotateAnimation.RELATIVE_TO_SELF, 0.5f, 94                 RotateAnimation.RELATIVE_TO_SELF, 0.5f); 95         animation.setInterpolator(new LinearInterpolator()); 96         animation.setDuration(250); 97         animation.setFillAfter(true); 98  99         reverseAnimation = new RotateAnimation(-180, 0,100                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,101                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);102         reverseAnimation.setInterpolator(new LinearInterpolator());103         reverseAnimation.setDuration(200);104         reverseAnimation.setFillAfter(true);105 106         state = DONE;107         isRefreshable = false;108         isPush = true;109     }110     111     public void onScroll(AbsListView arg0, int firstVisiableItem, int arg2,112             int arg3) {113         firstItemIndex = firstVisiableItem;114         visibleLastIndex = firstVisiableItem + arg2 - 1; 115         visibleItemCount = arg2;116         if(firstItemIndex == 1 && !isPush){117             setSelection(0);118         }119     }120     121     public void setSelectionfoot(){122         this.setSelection(visibleLastIndex - visibleItemCount + 1);123     }124 125     public void onScrollStateChanged(AbsListView arg0, int arg1) {126         127     }128     @Override129     public boolean onTouchEvent(MotionEvent event) {130 131         if (isRefreshable) {132             switch (event.getAction()) {133             case MotionEvent.ACTION_DOWN:134                 if (firstItemIndex == 0 && !isRecored) {135                     isRecored = true;136                     isPush = true;137                     startY = (int) event.getY();138                     Log.v(TAG, "在down时候记录当前位置‘");139                 }140                 break;141             case MotionEvent.ACTION_UP:142                 if (state != REFRESHING && state != LOADING) {143                     if (state == DONE) {144                         // 什么都不做145                     }146                     if (state == PULL_To_REFRESH) {147                         state = DONE;148                         changeHeaderViewByState();149 150                         Log.v(TAG, "由下拉刷新状态,到done状态");151                     }152                     if (state == RELEASE_To_REFRESH) {153                         state = REFRESHING;154                         changeHeaderViewByState();155                         onRefresh();156 157                         Log.v(TAG, "由松开刷新状态,到done状态");158                     }159                 }160 161                 isRecored = false;162                 isBack = false;163 164                 break;165 166             case MotionEvent.ACTION_MOVE:167                 int tempY = (int) event.getY();168 169                 if (!isRecored && firstItemIndex == 0) {170                     Log.v(TAG, "在move时候记录下位置");171                     isRecored = true;172                     startY = tempY;173                 }174 175                 if (state != REFRESHING && isRecored && state != LOADING) {176 177                     // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动178 179                     // 可以松手去刷新了180                     if (state == RELEASE_To_REFRESH) {181 182                         setSelection(0);183 184                         // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步185                         if (((tempY - startY) / RATIO < headContentHeight)186                                 && (tempY - startY) > 0) {187                             state = PULL_To_REFRESH;188                             changeHeaderViewByState();189 190                             Log.v(TAG, "由松开刷新状态转变到下拉刷新状态");191                         }192                         // 一下子推到顶了193                         else if (tempY - startY <= 0) {194                             state = DONE;195                             changeHeaderViewByState();196 197                             Log.v(TAG, "由松开刷新状态转变到done状态");198                         }199                         // 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步200                         else {201                             // 不用进行特别的操作,只用更新paddingTop的值就行了202                         }203                     }204                     // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态205                     if (state == PULL_To_REFRESH) {206 207                         setSelection(0);208 209                         // 下拉到可以进入RELEASE_TO_REFRESH的状态210                         if ((tempY - startY) / RATIO >= headContentHeight) {211                             state = RELEASE_To_REFRESH;212                             isBack = true;213                             changeHeaderViewByState();214                             Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");215                         }216                         // 上推到顶了217                         else if (tempY - startY <= 0) {218                             state = DONE;219                             changeHeaderViewByState();220                             isPush = false;221                             Log.v(TAG, "由DOne或者下拉刷新状态转变到done状态");222                         }223                     }224 225                     // done状态下226                     if (state == DONE) {227                         if (tempY - startY > 0) {228                             state = PULL_To_REFRESH;229                             changeHeaderViewByState();230                         }231                     }232 233                     // 更新headView的size234                     if (state == PULL_To_REFRESH) {235                         headView.setPadding(0, -1 * headContentHeight236                                 + (tempY - startY) / RATIO, 0, 0);237 238                     }239 240                     // 更新headView的paddingTop241                     if (state == RELEASE_To_REFRESH) {242                         headView.setPadding(0, (tempY - startY) / RATIO243                                 - headContentHeight, 0, 0);244                     }245 246                 }247 248                 break;249             }250         }251 252         return super.onTouchEvent(event);253     }254 255     // 当状态改变时候,调用该方法,以更新界面256     private void changeHeaderViewByState() {257         switch (state) {258         case RELEASE_To_REFRESH:259             arrowImageView.setVisibility(View.VISIBLE);260             progressBar.setVisibility(View.GONE);261             tipsTextview.setVisibility(View.VISIBLE);262             lastUpdatedTextView.setVisibility(View.VISIBLE);263 264             arrowImageView.clearAnimation();265             arrowImageView.startAnimation(animation);266 267             tipsTextview.setText(getResources().getString(R.string.release_to_refresh));268 269             Log.v(TAG, "当前状态,松开刷新");270             break;271         case PULL_To_REFRESH:272             progressBar.setVisibility(View.GONE);273             tipsTextview.setVisibility(View.VISIBLE);274             lastUpdatedTextView.setVisibility(View.VISIBLE);275             arrowImageView.clearAnimation();276             arrowImageView.setVisibility(View.VISIBLE);277             // 是由RELEASE_To_REFRESH状态转变来的278             if (isBack) {279                 isBack = false;280                 arrowImageView.clearAnimation();281                 arrowImageView.startAnimation(reverseAnimation);282 283                 tipsTextview.setText(getResources().getString(R.string.pull_to_refresh));284             } else {285                 tipsTextview.setText(getResources().getString(R.string.pull_to_refresh));286             }287             Log.v(TAG, "当前状态,下拉刷新");288             break;289 290         case REFRESHING:291 292             headView.setPadding(0, 0, 0, 0);293 294             progressBar.setVisibility(View.VISIBLE);295             arrowImageView.clearAnimation();296             arrowImageView.setVisibility(View.GONE);297             tipsTextview.setText(getResources().getString(R.string.refreshing));298             lastUpdatedTextView.setVisibility(View.VISIBLE);299 300             Log.v(TAG, "当前状态,正在刷新...");301             break;302         case DONE:303             headView.setPadding(0, -1 * headContentHeight, 0, 0);304 305             progressBar.setVisibility(View.GONE);306             arrowImageView.clearAnimation();307             arrowImageView.setImageResource(R.drawable.pulltorefresh);308             tipsTextview.setText(getResources().getString(R.string.pull_to_refresh));309             lastUpdatedTextView.setVisibility(View.VISIBLE);310 311             Log.v(TAG, "当前状态,done");312             break;313         }314     }315 316     public void setonRefreshListener(OnRefreshListener refreshListener) {317         this.refreshListener = refreshListener;318         isRefreshable = true;319     }320 321     public interface OnRefreshListener {322         public void onRefresh();323     }324 325     public void onRefreshComplete() {326         state = DONE;327         lastUpdatedTextView.setText(getResources().getString(R.string.updating) + new Date().toLocaleString());328         changeHeaderViewByState();329         invalidateViews();330         setSelection(0);331     }332 333     private void onRefresh() {334         if (refreshListener != null) {335             refreshListener.onRefresh();336         }337     }338     339     public void clickToRefresh(){340         state = REFRESHING;341         changeHeaderViewByState();342     }343     344     // 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height345     private void measureView(View child) {346         ViewGroup.LayoutParams p = child.getLayoutParams();347         if (p == null) {348             p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,349                     ViewGroup.LayoutParams.WRAP_CONTENT);350         }351         int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);352         int lpHeight = p.height;353         int childHeightSpec;354         if (lpHeight > 0) {355             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,356                     MeasureSpec.EXACTLY);357         } else {358             childHeightSpec = MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);359         }360         child.measure(childWidthSpec, childHeightSpec);361     }362 363     public void setAdapter(BaseAdapter adapter) {364         lastUpdatedTextView.setText(getResources().getString(R.string.updating) + new Date().toLocaleString());365         super.setAdapter(adapter);366     }367 }  


以上就是有下拉和上拉刷新功能的自定义的listview,下拉刷新头和上拉刷新的底部样式都可以自定义

这里给出我的下拉头布局和上拉底部布局,你们可以在此基础上进行修改自定义;

pulllist_head.xml 头布局

 1 <?xml version="1.0" encoding="utf-8"?> 2 <!-- ListView的头部 --> 3 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 4     android:layout_width="fill_parent" 5     android:layout_height="wrap_content" 6     android:paddingTop="5dp" 7     android:paddingBottom="5dp" 8     android:background="#FFFFFFFF" > 9 10     <!-- 内容 -->11 12     <RelativeLayout13         android:id="@+id/head_contentLayout"14         android:layout_width="fill_parent"15         android:layout_height="wrap_content"16         android:layout_marginTop="10dp"17         android:layout_marginBottom="10dp"18         android:paddingLeft="10dp" >19 20         <!-- 箭头图像、进度条 -->21 22         <FrameLayout23             android:layout_width="wrap_content"24             android:layout_height="wrap_content"25             android:layout_alignParentLeft="true"26             android:layout_centerVertical="true" >27 28             <!-- 箭头 -->29 30             <ImageView31                 android:id="@+id/head_arrowImageView"32                 android:layout_width="wrap_content"33                 android:layout_height="wrap_content"34                 android:layout_gravity="center"35                 android:src="http://www.mamicode.com/@drawable/pulltorefresh" />36 37             <!-- 进度条 -->38 39             <ProgressBar40                 android:id="@+id/head_progressBar"41                 style="@android:style/Widget.ProgressBar.Small"42                 android:layout_width="wrap_content"43                 android:layout_height="wrap_content"44                 android:layout_gravity="center"45                 android:visibility="gone" />46         </FrameLayout>47 48         <!-- 提示、最近更新 -->49 50         <LinearLayout51             android:layout_width="wrap_content"52             android:layout_height="wrap_content"53             android:layout_centerHorizontal="true"54             android:gravity="center_horizontal"55             android:orientation="vertical" >56 57             <!-- 提示 -->58 59             <TextView60                 android:id="@+id/head_tipsTextView"61                 android:layout_width="wrap_content"62                 android:layout_height="wrap_content"63                 android:textSize="16sp" />64 65             <!-- 最近更新 -->66 67             <TextView68                 android:id="@+id/head_lastUpdatedTextView"69                 android:layout_width="wrap_content"70                 android:layout_height="wrap_content"71                 android:textSize="13sp" />72         </LinearLayout>73     </RelativeLayout>74 75 </LinearLayout>

底部布局:list_footview.xml

 1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3     android:id="@+id/list_footview" 4     android:layout_width="fill_parent" 5     android:layout_height="fill_parent" 6     android:background="@android:color/white" 7     android:orientation="vertical" > 8  9     <LinearLayout10         android:layout_width="wrap_content"11         android:layout_height="wrap_content"12         android:layout_centerInParent="true"13         android:layout_centerVertical="true"14         android:layout_marginBottom="25dp"15         android:layout_marginTop="25dp"16         android:gravity="center"17         android:orientation="horizontal" >18 19         <TextView20             android:id="@+id/text_view"21             android:layout_width="wrap_content"22             android:layout_height="wrap_content"23             android:gravity="center"24             android:text="获取更多"25             android:textColor="@android:color/black"26             android:textSize="16sp" />27 28         <ProgressBar29             android:id="@+id/footer_progress"30             style="?android:attr/progressBarStyleSmall"31             android:layout_width="wrap_content"32             android:layout_height="wrap_content"33             android:layout_marginLeft="3dp"34             android:visibility="gone" />35     </LinearLayout>36 37 </RelativeLayout>

 

ok这样一个自定义的下拉上拉刷新的listview就定义好了,可以在activity的布局文件中直接引用,并且像操纵listview那样使用

非常方便,这里也给出我使用的源码;

首先是主界面布局:main.xml

 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3     android:layout_width="fill_parent" 4     android:layout_height="fill_parent" 5     android:orientation="vertical" > 6  7     <com.ryantang.pulllistview.RTPullListView 8         android:id="@+id/pullListView" 9         android:layout_width="fill_parent"10         android:layout_height="wrap_content" />11 12 </LinearLayout>

 

主界面activity中使用源码:

  1 package com.ryantang.pulllistview;  2   3 import java.util.ArrayList;  4 import java.util.List;  5   6 import android.app.Activity;  7 import android.os.Bundle;  8 import android.os.Handler;  9 import android.os.Message; 10 import android.view.LayoutInflater; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.widget.ArrayAdapter; 14 import android.widget.ProgressBar; 15 import android.widget.RelativeLayout; 16  17 import com.ryantang.pulllistview.RTPullListView.OnRefreshListener; 18  19 /** 20  * PullListView 21  * @author Ryan 22  * 23  */ 24 public class RTPullListViewActivity extends Activity { 25     private static final int INTERNET_FAILURE = -1; 26     private static final int LOAD_SUCCESS = 1; 27     private static final int LOAD_MORE_SUCCESS = 3; 28     private static final int NO_MORE_INFO = 4; 29     private static final int LOAD_NEW_INFO = 5; 30     private RTPullListView pullListView; 31     private ProgressBar moreProgressBar; 32     private List<String> dataList; 33     private ArrayAdapter<String> adapter; 34      35     @Override 36     public void onCreate(Bundle savedInstanceState) { 37         super.onCreate(savedInstanceState); 38         setContentView(R.layout.main); 39         pullListView = (RTPullListView) this.findViewById(R.id.pullListView); 40          41         dataList = new ArrayList<String>(); 42         for (int i = 0; i < 5; i++) { 43             dataList.add("Item data "+i); 44         } 45         adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, dataList); 46         pullListView.setAdapter(adapter); 47          48         //添加listview底部获取更多按钮(可自定义) 49         LayoutInflater inflater = LayoutInflater.from(this); 50         View view = inflater.inflate(R.layout.list_footview, null); 51         RelativeLayout footerView =(RelativeLayout) view.findViewById(R.id.list_footview); 52         moreProgressBar = (ProgressBar) view.findViewById(R.id.footer_progress); 53         pullListView.addFooterView(footerView); 54          55         //下拉刷新监听器 56         pullListView.setonRefreshListener(new OnRefreshListener() { 57              58             @Override 59             public void onRefresh() { 60                 new Thread(new Runnable() { 61                      62                     @Override 63                     public void run() { 64                         try { 65                             Thread.sleep(2000); 66                             dataList.clear(); 67                             for (int i = 0; i < 5; i++) { 68                                 dataList.add("Item data "+i); 69                             } 70                             myHandler.sendEmptyMessage(LOAD_NEW_INFO); 71                         } catch (InterruptedException e) { 72                             e.printStackTrace(); 73                         } 74                     } 75                 }).start(); 76             } 77         }); 78          79         //获取跟多监听器 80         footerView.setOnClickListener(new OnClickListener() {// 81              82             @Override 83             public void onClick(View v) { 84  85                 moreProgressBar.setVisibility(View.VISIBLE); 86                  87                 new Thread(new Runnable() { 88                      89                     @Override 90                     public void run() { 91                         try { 92                             Thread.sleep(2000); 93                             for (int i = 0; i < 5; i++) { 94                                 dataList.add("New item data "+i); 95                             } 96                             myHandler.sendEmptyMessage(LOAD_MORE_SUCCESS); 97                         } catch (InterruptedException e) { 98                             e.printStackTrace(); 99                         }100                     }101                 }).start();102             }103         });104     }105     106     //结果处理107     private Handler myHandler = new Handler(){108 109         @Override110         public void handleMessage(Message msg) {111             super.handleMessage(msg);112             switch (msg.what) {113             case LOAD_MORE_SUCCESS:114                 moreProgressBar.setVisibility(View.GONE);115                 adapter.notifyDataSetChanged();116                 pullListView.setSelectionfoot();117                 break;118 119             case LOAD_NEW_INFO:120                 adapter.notifyDataSetChanged();121                 pullListView.onRefreshComplete();122                 break;123             default:124                 break;125             }126         }127         128     };129 }

 

Android UI之下拉刷新上拉刷新实现