在实际开发中我们经常要用到上拉刷新和下拉刷新,因此今天我写了一个上拉和下拉刷新的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 @Override 129 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的size 234 if (state == PULL_To_REFRESH) { 235 headView.setPadding(0, -1 * headContentHeight 236 + (tempY - startY) / RATIO, 0, 0); 237 238 } 239 240 // 更新headView的paddingTop 241 if (state == RELEASE_To_REFRESH) { 242 headView.setPadding(0, (tempY - startY) / RATIO 243 - 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以及height 345 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 <RelativeLayout 13 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 <FrameLayout 23 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 <ImageView 31 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="@drawable/pulltorefresh" /> 36 37 <!-- 进度条 --> 38 39 <ProgressBar 40 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 <LinearLayout 51 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 <TextView 60 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 <TextView 68 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 <LinearLayout 10 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 <TextView 20 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 <ProgressBar 29 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 @Override 110 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 }
效果图如下: