Android ListView 下拉刷新、上拉加载

之前在用ListView做下拉刷新和上拉加载的时候一直都是在网上找的实例,
但效果都不是太好,然后自己根据实例的思路,自己梳理了一下,自己也写了一把,感觉也不是太好,在此做下记录吧,希望对初入ANDROID开发的新手们有所帮助,直接上代码吧


package cn.zan.control.view;

import java.util.Date;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import cn.zan.zan_society.R;

/**
 * Gening 2013-10-31
 **/
public class CustomListView extends ListView {
	
	private static final int DONE = 0;
	private static final int PULL_TO_REFRESH = 1;
	private static final int RELEASE_TO_REFRESH = 2;
	private static final int REFRESHING = 3;
	private static final float RATIO = 3;
	
	private int state;                         // 当前下拉刷新的状态
	
	private int firstVisibleIndex;             // ListView 中第一个可以看见的Item下标

	/***************************** ListView 头部 View 控件 ***************************/
	private View headView;                      // ListView 头部 View
	private ImageView headArrow;                // ListView 头部 View的箭头
	private ProgressBar progressBar;            // ListView 头部 View的读取转圈
	private TextView headTitle;                 // ListView 头部 View里的文字
	private TextView headLastUpdate;            // ListView 头部 View里的文字,最后更新时间
	private int headContentHeight;              // ListView 头部 View的高度
	private Animation animation;
	private Animation reverseAnimation;

	private boolean isRefreshable;
	private boolean isRecored = false;          // 用来记录第一次按下坐标点,在整个滑动的过程中 只记录一次
	
	private float startY;
	private boolean isBack = false;

	private boolean isFootLoading = false;      // 正在加载底部数据标识
	private boolean hasFoot = false;            // 是否有了底部 View(FootView)
	private int lastPos;                        // 最后一个可见的item的位置
	private int count;                          // ListView Item总数,注意不是当前可见的item总数

	public CustomListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		if (isInEditMode()) { return; }
		init(context);
	}

	private void init(Context context) {
		// listview 设置滑动时缓冲背景色
		setCacheColorHint(0x00000000);

		headView = View.inflate(context, R.layout.head, null);
		headArrow = (ImageView) headView.findViewById(R.id.head_arrow);
		progressBar = (ProgressBar) headView.findViewById(R.id.progressbar);
		headTitle = (TextView) headView.findViewById(R.id.head_title);
		headLastUpdate = (TextView) headView.findViewById(R.id.head_last_update);

		headArrow.setMinimumWidth(50);
		headArrow.setMinimumHeight(70);

		MeasureView(headView);

		headContentHeight = headView.getMeasuredHeight();

		headView.setPadding(0, -1 * headContentHeight, 0, 0);
		addHeaderView(headView); // 为 ListView加入顶部 View
		this.setOnScrollListener(custom_listview_onscroll_lis);

		animation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
		animation.setDuration(250);
		animation.setFillAfter(true); // 设定动画结束时,停留在动画结束位置 (保留动画效果)
		animation.setInterpolator(new LinearInterpolator()); // 匀速变化

		reverseAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
		reverseAnimation.setDuration(200);
		reverseAnimation.setFillAfter(true);// 设定动画结束时,停留在动画结束位置 (保留动画效果)
		reverseAnimation.setInterpolator(new LinearInterpolator());// 匀速变化

		// 设置当前headView的状态
		state = DONE;

		// 设置当前下拉刷新是否可用
		isRefreshable = false;
	}

	/** 测量headView的 宽高 **/
	private void MeasureView(View child) {
		ViewGroup.LayoutParams lp = child.getLayoutParams();

		if (null == lp) {
			lp = new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
		}

		int measureChildWidth = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
		int measureChildHeight;

		if (lp.height > 0) {
			measureChildHeight = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
		} else {
			measureChildHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
		}
		child.measure(measureChildWidth, measureChildHeight);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if (isRefreshable) {
				if (firstVisibleIndex == 0 && !isRecored) {
					startY = event.getY();
					isRecored = true;
				}
			}
			break;
		case MotionEvent.ACTION_MOVE:
			if (isRefreshable) {
				float tempY = event.getY();
				if (firstVisibleIndex == 0 && !isRecored) {
					startY = tempY;
					isRecored = true;
				}
				if (isRecored) {
					if (tempY - startY < 0) {
						break;
					}
					if (state != REFRESHING) {
						if (state == PULL_TO_REFRESH) {
							if ((tempY - startY) / RATIO >= headContentHeight && (tempY - startY) > 0) {
								// 向下拉了 从下拉刷新的状态 来到 松开刷新的状态
								state = RELEASE_TO_REFRESH;
								changeHeadViewOfState();
							} else if ((tempY - startY) <= 0) {
								// 向上推了 从下拉刷新的状态 来到 刷新完成的状态
								state = DONE;
								changeHeadViewOfState();
							}
						} else if (state == RELEASE_TO_REFRESH) {
							// 向上推了 还没有完全将HEADVIEW 隐藏掉(可以看到一部分)
							if ((tempY - startY) / RATIO < headContentHeight && (tempY - startY) > 0) {
								// 从松开刷新的状态 来到 下拉刷新的状态
								state = PULL_TO_REFRESH;
								changeHeadViewOfState();
								isBack = true;
							} else if ((tempY - startY) <= 0) {
								// 向上推了 一下子推到了最上面 从松开刷新的状态 来到 刷新完成的状态 (数据不刷新的)
								state = DONE;
								changeHeadViewOfState();
							}
						} else if (state == DONE) {
							// 刷新完成的状态 来到 下拉刷新的状态
							if ((tempY - startY) > 0) {
								state = PULL_TO_REFRESH;
								changeHeadViewOfState();
							}
						}
						if (state == PULL_TO_REFRESH)
							headView.setPadding(0, (int) ((tempY - startY) / RATIO - headContentHeight), 0, 0);
						if (state == RELEASE_TO_REFRESH)
							headView.setPadding(0, (int) ((tempY - startY) / RATIO - headContentHeight), 0, 0);
					}
				}
			}
			break;
		case MotionEvent.ACTION_UP:
			if (isRefreshable) {
				if (state != REFRESHING) {
					if (state == PULL_TO_REFRESH) {
						// 松手
						state = DONE;
						changeHeadViewOfState();
					} else if (state == RELEASE_TO_REFRESH) {
						// 松手
						state = REFRESHING;
						changeHeadViewOfState();
						// 执行数据刷新方法
						onRefresh();
					}
				}
				isRecored = false;
				isBack = false;
			}
			break;
		}
		return super.onTouchEvent(event);
	}

	/** 改变下拉刷新时,头部控件显示样式 **/
	private void changeHeadViewOfState() {
		switch (state) {
		case PULL_TO_REFRESH:
			headArrow.setVisibility(View.VISIBLE);
			progressBar.setVisibility(View.GONE);
			headTitle.setVisibility(View.VISIBLE);
			headLastUpdate.setVisibility(View.VISIBLE);
			headArrow.clearAnimation();
			headTitle.setText("下拉可以刷新");
			// 由 松开刷新 到 下拉刷新
			if (isBack) {
				headArrow.startAnimation(animation);
				isBack = false;
			}
			break;
		case RELEASE_TO_REFRESH:
			headArrow.setVisibility(View.VISIBLE);
			progressBar.setVisibility(View.GONE);
			headTitle.setVisibility(View.VISIBLE);
			headLastUpdate.setVisibility(View.VISIBLE);
			headArrow.clearAnimation();
			headArrow.startAnimation(reverseAnimation);
			headTitle.setText("松开可以刷新");
			break;
		case REFRESHING:
			headArrow.setVisibility(View.GONE);
			progressBar.setVisibility(View.VISIBLE);
			headTitle.setVisibility(View.VISIBLE);
			headLastUpdate.setVisibility(View.VISIBLE);
			headArrow.clearAnimation();
			headTitle.setText("正在刷新...");
			headView.setPadding(0, 0, 0, 0);
			break;
		case DONE:
			headArrow.setVisibility(View.VISIBLE);
			progressBar.setVisibility(View.GONE);
			headTitle.setVisibility(View.VISIBLE);
			headLastUpdate.setVisibility(View.VISIBLE);
			headArrow.clearAnimation();
			headTitle.setText("下拉可以刷新");
			headView.setPadding(0, -1 * headContentHeight, 0, 0);
			break;
		}
	}

	/** ListView 监听事件 **/
	private OnScrollListener custom_listview_onscroll_lis = new OnScrollListener() {
		@Override
		public void onScrollStateChanged(AbsListView view, int scrollState) {
			if (!isFootLoading) {
				// 没有滚动
				if (hasFoot && scrollState == SCROLL_STATE_IDLE) {
					isFootLoading = true;
					onFootLoading();
				}
			}
		}

		@Override
		public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
			firstVisibleIndex = firstVisibleItem;
			lastPos = getLastVisiblePosition();
			count = totalItemCount;

			// 因为刚进入的时候,lastPos=-1,count=0,这个时候不能让它执行onAddFoot方法
			if (lastPos == count - 1 && !hasFoot && lastPos != -1) {
				hasFoot = true;
				onAddFoot();
			}
		}
	};

	/** 下拉刷新监听 **/
	private OnRefreshListner refreshListner; // 刷新监听器

	/** 设置下拉刷新监听器 **/
	public void setOnRefreshListner(OnRefreshListner listener) {
		isRefreshable = true;
		refreshListner = listener;
	} 

	/** 执行下拉刷新操作 **/
	private void onRefresh() {
		if (refreshListner != null) {
			refreshListner.onRefresh();
		}
	} 

	/** 添加底部View(FootView)监听器 **/
	public OnAddFootListener onAddFootListener;

	/** 设置添加Foot监听器 **/
	public void setOnAddFootListener(OnAddFootListener addFootListener) {
		onAddFootListener = addFootListener;
	} 

	/** 执行添加Foot **/
	public void onAddFoot() {
		if (onAddFootListener != null && hasFoot) {
			onAddFootListener.addFoot();
		}
	} 

	/** 上拉加载,监听器 **/
	public OnFootLoadingListener footLoadingListener;

	public void setOnFootLoadingListener(OnFootLoadingListener footLoading) {
		footLoadingListener = footLoading;
	}

	/** 执行底部加载 **/
	public void onFootLoading() {
		if (footLoadingListener != null && isFootLoading) {
			footLoadingListener.onFootLoading();
		}
	} 

	/********************************************************************** 监听器 **************************************************************************/
	/** 下拉刷新监听器 **/
	public interface OnRefreshListner {
		// 下拉刷新的时候,在这里执行获取数据的过程
		void onRefresh();
	}

	/** 添加Foot的监听器 **/
	public interface OnAddFootListener {
		// 这里是用户addFootView的操作
		void addFoot();
	}

	/** 上拉加载监听器 **/
	public interface OnFootLoadingListener {
		// 上拉加载的时候,在这里行后台获取数据的过程
		void onFootLoading();
	}

	/******************************************************************** 对外调用方法 *****************************************************************/
	/** 底部数据加载完成,用户需要加入一个removeFootView的操作 **/
	public void onFootLoadingComplete() {
		hasFoot = false;
		isFootLoading = false;
	}

	/** 上拉刷新完成时 所执行的操作,更改状态,隐藏head **/
	public void onRefreshComplete() {
		state = DONE;
		changeHeadViewOfState();
		headLastUpdate.setText("最后刷新时间: " + new Date().toLocaleString());
	}
	
	@Override
	public void setAdapter(ListAdapter adapter) {
		headLastUpdate.setText("最后刷新时间: " + new Date().toLocaleString());
		super.setAdapter(adapter);
	}
}


在外部使用的时候,
需使用下拉刷新,直接实现CustomListView的OnRefreshListenner接口
需使用上拉加载,先实现Custom的setOnAddFootListener接口为ListView赋上想要显示的布局,然后再实现OnFootLoadingListener接口执行加载时需要做的请求、数据解析及ListView数据源刷新。最后需调用CustomListView.OnFootLoadingComplete()回复初始状态 和 CustomListView.removeFooterView()移除FootViewW布局。

大概实现就是这样,在此因为时间的原因没有写上实现的思路,有时间补上。但是此实例有明显的不足之处。
1. 下拉刷新与上拉加载的效果太突兀,还有待优化。
2. 下拉刷新与上拉加载的touch事件实现上区分的还不是很清晰明确,还有待优化。



猜你喜欢

转载自geningaixin.iteye.com/blog/2034379