首页 > 代码库 > 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之下拉刷新上拉刷新实现
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。