首页 > 代码库 > Android PullToRefresh 下拉刷新,上拉很多其它,支持ScrollView,ListView,可方便拓展GridView,WebView等

Android PullToRefresh 下拉刷新,上拉很多其它,支持ScrollView,ListView,可方便拓展GridView,WebView等

在写着东西之前。从网上找到非常多这方面的源代码,可是基本没有找到惬意的。包含在GitHub上的比較有名的Android-PullToRefresh-master。思来想去还是自己写吧。当然当中借鉴了一些别的开源代码!

废话不多说,直接上代码。凝视非常全乎,应该不难理解,Demo下载地址在最后:

package com.zs.pulltorefreshtest;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.Scroller;

/**
 * 下拉刷新控件,主要測试了ScrollView,代码中已实现ListView下拉和上拉刷新,只是没有怎么測
 * 至于GridView、WebView等,代码中没有实现。只是非常好拓展,在isReadyForPullUp() 和 
 * isReadyForPullDown()这两个方法中增加对应的View的上下边界推断就OK了
 * @author zhangshuo 
 * @version 1.0
 */
public class PullToRefreshView extends RelativeLayout {

	/**手指滑动距离与控件移动距离的比例为2:1*/
	static final float FRICTION = 2.0f;

	/**显示“下拉刷新”的状态*/
	static final int PULL_TO_REFRESH = 0x0;
	/**显示“释放刷新”的状态*/
	static final int RELEASE_TO_REFRESH = 0x1;
	/**用户通过下拉进入的刷新状态*/
	static final int REFRESHING = 0x2;
	/**用户通过代码强制进入的刷新状态*/
	static final int MANUAL_REFRESHING = 0x3;

	/**私有模式,不提供对外调用。
	 * 仅用来标示“用户下拉刷新成功后,headerView显示在头部,当用户手指向上滑动时。将headerView尾随用户滑动向上滑动”
	 * 及“用户上拉很多其它成功后,footerView显示在底部,当用户手指向下滑动时。将footerView尾随用户滑动向下滑动”这两个过程的模式*/
	private static final int MODE_PULL_TO_SCROLL_HEADER_OR_FOOTER = 0x0;
	/**标示当前支持下拉刷新模式*/
	public static final int MODE_PULL_DOWN_TO_REFRESH = 0x1;
	/**标示当前支持上拉很多其它模式*/
	public static final int MODE_PULL_UP_TO_REFRESH = 0x2;
	/**标示当前支持下拉刷新和上拉很多其它两种模式*/
	public static final int MODE_BOTH = 0x3;


	private Context context;
	/**滚动对象*/
	private Scroller scroller;
	/**推断用户手指的移动距离是否足以响应为move*/
	private int touchSlop;

	private float initialMotionY;
	private float lastMotionX;
	private float lastMotionY;
	private boolean isBeingDragged = false;

	/**记录headerView当前的状态*/
	private int headerState = PULL_TO_REFRESH;
	/**记录footerView当前的状态*/
	private int footerState = PULL_TO_REFRESH;
	/**当前所支持的模式*/
	private int mode = MODE_PULL_DOWN_TO_REFRESH;
	/**当前处于的模式*/
	private int currentMode;
	
	/**依据不同的mode,contentView所在父View的位置不同,下拉刷新时为1,上拉很多其它时为1,上拉下拉都支持时为2*/
	private int index = 1;

	/**标示当处于刷新状态时,是否须要禁用滑动*/
	private boolean disableScrollingWhileRefreshing = false;

	/**标示是否同意滑动刷新*/
	private boolean isPullToRefreshEnabled = true;

	private LoadingLayout headerLayout;
	private LoadingLayout footerLayout;
	private int headerHeight;
	
	/**记录当处于刷新状态时。用户继续下拉的次数*/
	private int pullWithRefreshingCount = 0;
	/**记录当处于载入很多其它状态时,用户继续上拉的次数*/
	private int pullWithLoadingMoreCount = 0;

	/**刷新回调接口*/
	private OnRefreshListener onRefreshListener;
	
	/**载入很多其它回调接口*/
	private onl oadMoreListener onl oadMoreListener;

	
	public PullToRefreshView(Context context) {
		super(context);
		init(context, null);
	}

	public PullToRefreshView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context, attrs);
	}
	
	/**
	 * @方法描写叙述:	初始化方法
	 * @作者:zhangshuo
	 * @param context
	 * @param attrs
	 */
	private void init(Context context, AttributeSet attrs) {

		scroller = new Scroller(context);

		touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

		this.context = context;

		this.addLoadingView();
	}

	/**
	 * @方法描写叙述: 依据当前模式设置,载入头部和底部布局	
	 * @作者:zhangshuo
	 */
	public void addLoadingView() {

		String pullDownLabel = context
				.getString(R.string.pull_to_refresh_pull_down_label);
		String refreshingDownLabel = context
				.getString(R.string.pull_to_refresh_refreshing_down_label);
		String releaseDownLabel = context
				.getString(R.string.pull_to_refresh_release_down_label);
		String pullUpLabel = context
				.getString(R.string.pull_to_refresh_pull_up_label);
		String refreshingUpLabel = context
				.getString(R.string.pull_to_refresh_refreshing_up_label);
		String releaseUpLabel = context
				.getString(R.string.pull_to_refresh_release_up_label);
		
		/*载入头部和底部View*/
		if (mode == MODE_PULL_DOWN_TO_REFRESH || mode == MODE_BOTH) {
			headerLayout = new LoadingLayout(context,
					MODE_PULL_DOWN_TO_REFRESH, releaseDownLabel, pullDownLabel,
					refreshingDownLabel);
			addView(headerLayout, 0, new LinearLayout.LayoutParams(
					ViewGroup.LayoutParams.MATCH_PARENT,
					ViewGroup.LayoutParams.WRAP_CONTENT));
			measureView(headerLayout);
			headerHeight = headerLayout.getMeasuredHeight();
		}
		if (mode == MODE_PULL_UP_TO_REFRESH || mode == MODE_BOTH) {
			footerLayout = new LoadingLayout(context, MODE_PULL_UP_TO_REFRESH,
					releaseUpLabel, pullUpLabel, refreshingUpLabel);
			RelativeLayout.LayoutParams lp2 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);  
		    lp2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);   
		    addView(footerLayout, lp2);  
			measureView(footerLayout);
			headerHeight = footerLayout.getMeasuredHeight();
		}

		/*隐藏头部和底部View*/
		switch (mode) {
		case MODE_BOTH:
			index = 2;
			setPadding(0, -headerHeight, 0, -headerHeight);
			break;
		case MODE_PULL_UP_TO_REFRESH:
			index = 1;
			setPadding(0, 0, 0, -headerHeight);
			break;
		case MODE_PULL_DOWN_TO_REFRESH:
		default:
			index = 1;
			setPadding(0, -headerHeight, 0, 0);
			break;
		}

	}

	/**
	 * 在头部和底部View增加完毕后。又一次布局。以避免在隐藏headerView和footerView时会把一部分内容(contentView)隐藏
	 */
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		// TODO Auto-generated method stub
		super.onLayout(changed, l, t, r, b);
		View contentView = null;
		RelativeLayout.LayoutParams lp1 = null; 
		switch (mode) {
		case MODE_BOTH:
			contentView = this.getChildAt(index);
			lp1 = (LayoutParams) contentView.getLayoutParams();
			lp1.setMargins(0, headerHeight, 0, headerHeight);
			break;
		case MODE_PULL_UP_TO_REFRESH:
			contentView = this.getChildAt(index);
			lp1 = (LayoutParams) contentView.getLayoutParams();
			lp1.setMargins(0, 0, 0, headerHeight);
			break;
		case MODE_PULL_DOWN_TO_REFRESH:
		default:
			contentView = this.getChildAt(index);
			lp1 = (LayoutParams) contentView.getLayoutParams();
			lp1.setMargins(0, headerHeight, 0, 0);
			break;
		}
	}
	
	private void measureView(View child) {
		ViewGroup.LayoutParams p = child.getLayoutParams();
		if (p == null) {
			p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
					ViewGroup.LayoutParams.WRAP_CONTENT);
		}

		int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
		int lpHeight = p.height;
		int childHeightSpec;
		if (lpHeight > 0) {
			childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
					MeasureSpec.EXACTLY);
		} else {
			childHeightSpec = MeasureSpec.makeMeasureSpec(0,
					MeasureSpec.UNSPECIFIED);
		}
		child.measure(childWidthSpec, childHeightSpec);
	}

	@Override
	public final boolean onInterceptTouchEvent(MotionEvent event) {

		Log.e("Intercept", "start");

		if (!isPullToRefreshEnabled) {
			return false;
		}

		if ((isLoadingMore() || isRefreshing()) && disableScrollingWhileRefreshing) {
			return true;
		}
		
		final int action = event.getAction();

		if (action == MotionEvent.ACTION_CANCEL
				|| action == MotionEvent.ACTION_UP) {
			isBeingDragged = false;
			return false;
		}

		if (action != MotionEvent.ACTION_DOWN && isBeingDragged) {
			return true;
		}

		switch (action) {
		case MotionEvent.ACTION_DOWN: {
			Log.e("Intercept", "down");
			if (isReadyForPull()) {
				lastMotionY = initialMotionY = event.getY();
				lastMotionX = event.getX();
				isBeingDragged = false;
			}
			break;
		}
		case MotionEvent.ACTION_MOVE: {
			Log.e("Intercept", "move");
			if (isReadyForPull()) {
				final float y = event.getY();
				final float dy = y - lastMotionY;
				final float yDiff = Math.abs(dy);
				final float xDiff = Math.abs(event.getX() - lastMotionX);

				if (yDiff > touchSlop && yDiff > xDiff) {
					if ((mode == MODE_PULL_DOWN_TO_REFRESH || mode == MODE_BOTH)
							&& dy >= 0.0001f && isReadyForPullDown()) {
						/*能够下拉刷新*/
						lastMotionY = y;
						isBeingDragged = true;
						currentMode = MODE_PULL_DOWN_TO_REFRESH;
					} else if ((mode == MODE_PULL_UP_TO_REFRESH || mode == MODE_BOTH)
							&& dy <= 0.0001f && isReadyForPullUp()) {
						/*能够上拉很多其它*/
						lastMotionY = y;
						isBeingDragged = true;
						currentMode = MODE_PULL_UP_TO_REFRESH;
					}else if((isRefreshing() && getScrollY() < 0)|| (isLoadingMore() && getScrollY() > 0)){
						/*当前headerView或footerView处于显示状态,开启尾随手指滑动模式*/
						lastMotionY = y;
						isBeingDragged = true;
						currentMode = MODE_PULL_TO_SCROLL_HEADER_OR_FOOTER;
					}
				}
			}
			break;
		}
		}
		return isBeingDragged;
	}
	
	@Override
	public final boolean onTouchEvent(MotionEvent event) {
		Log.e("Touch", "start");
		if (!isPullToRefreshEnabled) {
			return false;
		}

		if (isRefreshing() && disableScrollingWhileRefreshing) {
			return true;
		}

		if (event.getAction() == MotionEvent.ACTION_DOWN
				&& event.getEdgeFlags() != 0) {
			return false;
		}

		switch (event.getAction()) {
		
		case MotionEvent.ACTION_DOWN: {
			Log.e("Touch", "down");
			if (isReadyForPull()) {
				lastMotionY = initialMotionY = event.getY();
				return true;
			}
			break;
		}
		case MotionEvent.ACTION_MOVE: {
			Log.e("Touch", "move");
			if (isBeingDragged) {
				lastMotionY = event.getY();
				this.pullEvent();
				return true;
			}
			break;
		}
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP: {
			Log.e("Touch", "up");
			if (isBeingDragged) {
				isBeingDragged = false;

				if(isRefreshing() && pullWithRefreshingCount == 0){
					pullWithRefreshingCount = 1;
				}
				if(isLoadingMore() && pullWithLoadingMoreCount == 0){
					pullWithLoadingMoreCount = 1;
				}
				
				switch (currentMode) {
				case MODE_PULL_TO_SCROLL_HEADER_OR_FOOTER:
					/*将headerView和footerView隐藏*/
					smoothScrollTo(0);
					break;
				case MODE_PULL_UP_TO_REFRESH:
					/*推断是否激活载入很多其它*/
					if (footerState == RELEASE_TO_REFRESH && null != onl oadMoreListener) {
						setLoadingMoreInternal(true);
						onLoadMoreListener.onLoadMore();
					} else {
						smoothScrollTo(0);
					}
					break;
				case MODE_PULL_DOWN_TO_REFRESH:
					/*推断是否激活刷新*/
					if (headerState == RELEASE_TO_REFRESH && null != onRefreshListener) {
						setRefreshingInternal(true);
						onRefreshListener.onRefresh();
					} else {
						smoothScrollTo(0);
					}
					break;
				}
				
				return true;
			}
			break;
		}
		}

		return false;
	}
	
	/**
	 * @方法描写叙述:	处理用户滑动的方法
	 * @作者:zhangshuo
	 *
	 * @return
	 */
	private boolean pullEvent() {

		final int newHeight;
		final int oldHeight = this.getScrollY();

		switch (currentMode) {
		case MODE_PULL_TO_SCROLL_HEADER_OR_FOOTER:
			newHeight = Math.round((initialMotionY - lastMotionY));
			break;
		case MODE_PULL_UP_TO_REFRESH:
			newHeight = Math.round(Math.max(initialMotionY - lastMotionY, 0)
					/ FRICTION);
			break;
		case MODE_PULL_DOWN_TO_REFRESH:
		default:
			newHeight = Math.round(Math.min(initialMotionY - lastMotionY, 0)
					/ FRICTION);
			break;
		}
		
		if(isRefreshing() && pullWithRefreshingCount == 0){
			/*处于刷新状态下,第一次继续下拉。此时headerView已经显示在头部*/
			if((-headerHeight + newHeight) < 0){
				scrollTo(-headerHeight + newHeight);
			}else{
				scrollTo(0);
				if(((ScrollView)getChildAt(index)).getChildAt(0).getHeight() > getChildAt(index).getHeight()){
					getChildAt(index).scrollTo(0, newHeight - headerHeight);
				}
			}
		}else if(isLoadingMore() && pullWithLoadingMoreCount == 0){
			/*处于刷新状态下。第一次继续下拉。此时headerView已经显示在头部*/
			if((headerHeight + newHeight) > 0){
				scrollTo(headerHeight + newHeight);
			}else{
				scrollTo(0);
				if(((ScrollView)getChildAt(index)).getChildAt(0).getHeight() > getChildAt(index).getHeight()){
					getChildAt(index).scrollTo(0, newHeight + headerHeight + ((ScrollView)getChildAt(index)).getChildAt(0).getHeight() - getChildAt(index).getHeight());
				}
			}
		}else{
			scrollTo(newHeight);
		}
		
		if (newHeight != 0) {
			
			switch (currentMode) {
			case MODE_PULL_UP_TO_REFRESH:
				if (footerState == PULL_TO_REFRESH && headerHeight < Math.abs(newHeight)) {
					footerState = RELEASE_TO_REFRESH;
					footerLayout.releaseToRefresh();
					return true;

				} else if (footerState == RELEASE_TO_REFRESH
						&& headerHeight >= Math.abs(newHeight)) {
					footerState = PULL_TO_REFRESH;
					footerLayout.pullToRefresh();
					return true;
				}
				break;
			case MODE_PULL_DOWN_TO_REFRESH:
				if (headerState == PULL_TO_REFRESH && headerHeight < Math.abs(newHeight)) {
					headerState = RELEASE_TO_REFRESH;
					headerLayout.releaseToRefresh();
					return true;

				} else if (headerState == RELEASE_TO_REFRESH
						&& headerHeight >= Math.abs(newHeight)) {
					headerState = PULL_TO_REFRESH;
					headerLayout.pullToRefresh();
					return true;
				}
				break;
			}
			
		}

		return oldHeight != newHeight;
	}
	
	/**
	 * @方法描写叙述: 推断当前状态能否够进行上拉很多其它或下拉刷新的滑动操作	
	 * @作者:zhangshuo
	 * @return
	 */
	private boolean isReadyForPull() {
		switch (mode) {
		case MODE_PULL_DOWN_TO_REFRESH:
			return isReadyForPullDown();
		case MODE_PULL_UP_TO_REFRESH:
			return isReadyForPullUp();
		case MODE_BOTH:
			return isReadyForPullUp() || isReadyForPullDown();
		}
		return false;
	}

	/**
	 * @方法描写叙述: 推断当前状态能否够进行下拉刷新操作	
	 * @作者:zhangshuo
	 * @return
	 */
	private boolean isReadyForPullDown() {
		// TODO Auto-generated method stub
		if (getChildCount() > 1) {
			Log.e("Ready--down", String.valueOf(getChildCount()));
			View childView = this.getChildAt(index);
			if (childView instanceof ListView) {
				int top = ((ListView) childView).getChildAt(0).getTop();
				int pad = ((ListView) childView).getListPaddingTop();
				if ((Math.abs(top - pad)) < 3
						&& ((ListView) childView).getFirstVisiblePosition() == 0) {
					return true;
				} else {
					return false;
				}
			} else if (childView instanceof ScrollView) {
				Log.e("Ready--down", "scrollView");
				if (((ScrollView) childView).getScrollY() == 0) {
					return true;
				} else {
					return false;
				}
			}

		}
		return false;
	}

	/**
	 * @方法描写叙述:推断当前状态能否够上拉很多其它的滑动操作	
	 * @作者:zhangshuo
	 * @return
	 */
	private boolean isReadyForPullUp() {
		// TODO Auto-generated method stub
		if (getChildCount() > 1) {
			Log.e("Ready--up", String.valueOf(getChildCount()));
			View childView = this.getChildAt(index);
			if (childView instanceof ListView) {
				int top = ((ListView) childView).getChildAt(
						((ListView) childView).getCount()).getBottom();
				int pad = ((ListView) childView).getListPaddingBottom();
				if ((Math.abs(top - pad)) < 3
						&& ((ListView) childView).getFirstVisiblePosition() == ((ListView) childView)
								.getCount()) {
					return true;
				} else {
					return false;
				}
			} else if (childView instanceof ScrollView) {
				Log.e("Ready--up", "scrollView");
				int off = ((ScrollView) childView).getScrollY()
						+ ((ScrollView) childView).getHeight()
						- ((ScrollView) childView).getChildAt(0).getHeight();
				if (off >= 0) {
					return true;
				} else {
					return false;
				}
			}

		}
		return false;
	}

	/**
	 * @方法描写叙述:	是否同意上拉很多其它或下拉刷新的滑动操作
	 * @作者:zhangshuo
	 * @return
	 */
	public final boolean isPullToRefreshEnabled() {
		return isPullToRefreshEnabled;
	}

	/**
	 * @方法描写叙述:	当处于刷新状态时,是否须要禁用滑动
	 * @作者:zhangshuo
	 * @return
	 */
	public final boolean isDisableScrollingWhileRefreshing() {
		return disableScrollingWhileRefreshing;
	}

	/**
	 * @方法描写叙述:	当前正处于刷新中
	 * @作者:zhangshuo
	 * @return
	 */
	public final boolean isRefreshing() {
		return headerState == REFRESHING || headerState == MANUAL_REFRESHING;
	}
	
	/**
	 * @方法描写叙述:	当前正处于载入很多其它中
	 * @作者:zhangshuo
	 * @return
	 */
	public final boolean isLoadingMore() {
		return footerState == REFRESHING || footerState == MANUAL_REFRESHING;
	}

	/**
	 * @方法描写叙述:	 设置当处于刷新状态时。是否须要禁用滑动
	 * @作者:zhangshuo
	 * @param disableScrollingWhileRefreshing
	 */
	public final void setDisableScrollingWhileRefreshing(
			boolean disableScrollingWhileRefreshing) {
		this.disableScrollingWhileRefreshing = disableScrollingWhileRefreshing;
	}

	/**
	 * @方法描写叙述:	结束刷新状态
	 * @作者:zhangshuo
	 *
	 */
	public final void onRefreshComplete() {
		if (headerState != PULL_TO_REFRESH) {
			resetHeader();
		}
		pullWithRefreshingCount = 0;
	}
	
	/**
	 * @方法描写叙述:	结束载入很多其它状态
	 * @作者:zhangshuo
	 *
	 */
	public final void onl oadMoreComplete() {
		if (footerState != PULL_TO_REFRESH) {
			resetFooter();
		}
		pullWithLoadingMoreCount = 0;
	}

	/**
	 * @方法描写叙述: 设置否同意滑动刷新	
	 * @作者:zhangshuo
	 * @param enable
	 */
	public final void setPullToRefreshEnabled(boolean enable) {
		this.isPullToRefreshEnabled = enable;
	}

	/**
	 * @方法描写叙述:	强制设置为刷新状态
	 * @作者:zhangshuo
	 */
	public final void setRefreshing() {
		this.setRefreshing(true);
	}
	
	/**
	 * @方法描写叙述:	强制设置为载入很多其它状态
	 * @作者:zhangshuo
	 */
	public final void setLoadingMore(){
		this.setLoadingMore(true);
	}

	/**
	 * @方法描写叙述:	强制设置为刷新状态
	 * @作者:zhangshuo
	 * @param doScroll
	 */
	public final void setRefreshing(boolean doScroll) {
		if (!isRefreshing()) {
			setRefreshingInternal(doScroll);
			headerState = MANUAL_REFRESHING;
		}
	}
	
	/**
	 * @方法描写叙述:	强制设置为载入很多其它状态
	 * @作者:zhangshuo
	 * @param doScroll
	 */
	public final void setLoadingMore(boolean doScroll) {
		if (!isLoadingMore()) {
			setLoadingMoreInternal(doScroll);
			footerState = MANUAL_REFRESHING;
		}
	}

	protected final int getCurrentMode() {
		return currentMode;
	}

	protected final LoadingLayout getFooterLayout() {
		return footerLayout;
	}

	protected final LoadingLayout getHeaderLayout() {
		return headerLayout;
	}

	protected final int getHeaderHeight() {
		return headerHeight;
	}

	protected final int getMode() {
		return mode;
	}

	/**
	 * @方法描写叙述:	重置headerView
	 * @作者:zhangshuo
	 */
	protected void resetHeader() {
		headerState = PULL_TO_REFRESH;
		isBeingDragged = false;

		if (null != headerLayout) {
			headerLayout.reset();
		}

		smoothScrollTo(0);
	}
	
	/**
	 * @方法描写叙述:	重置footerView
	 * @作者:zhangshuo
	 */
	protected void resetFooter() {
		footerState = PULL_TO_REFRESH;
		isBeingDragged = false;

		if (null != footerLayout) {
			footerLayout.reset();
		}

		smoothScrollTo(0);
	}

	/**
	 * @方法描写叙述:	强制设置为刷新状态,并显示出headerView
	 * @作者:zhangshuo
	 * @param doScroll
	 */
	protected void setRefreshingInternal(boolean doScroll) {
		headerState = REFRESHING;
		pullWithRefreshingCount = 0;
		
		if (null != headerLayout) {
			headerLayout.refreshing();
		}

		if (doScroll) {
			smoothScrollTo(-headerHeight);
		}
	}
	
	/**
	 * @方法描写叙述:	强制设置为载入很多其它状态,并显示出footerView
	 * @作者:zhangshuo
	 * @param doScroll
	 */
	protected void setLoadingMoreInternal(boolean doScroll) {
		footerState = REFRESHING;
		pullWithLoadingMoreCount = 0;
		
		if (null != footerLayout) {
			footerLayout.refreshing();
		}

		if (doScroll) {
			smoothScrollTo(headerHeight);
		}
	}

	protected final void scrollTo(int y) {
		scrollTo(0, y);
	}

	protected final void smoothScrollTo(int y) {

		scroller.startScroll(0, getScrollY(), 0, -(getScrollY() - y), 500);
		invalidate();

	}

	@Override
	public void computeScroll() {
		// TODO Auto-generated method stub
		if (scroller.computeScrollOffset()) {
			scrollTo(0, this.scroller.getCurrY());
			postInvalidate();
		}
	}

	/**
	 * @方法描写叙述: 设置刷新回调接口	
	 * @作者:zhangshuo
	 * @param listener
	 */
	public final void setOnRefreshListener(OnRefreshListener listener) {
		this.onRefreshListener = listener;
	}
	
	/**
	 * @方法描写叙述:	设置载入很多其它回调接口
	 * @作者:zhangshuo
	 * @param listener
	 */
	public final void setOnLoadMoreListener(OnLoadMoreListener listener){
		this.onLoadMoreListener  = listener;
	}
	
	/**
	 * @CLASS:OnRefreshListener
	 * @描写叙述: 刷新回调接口	
	 * @作者:zhangshuo
	 * @版本号:v1.0
	 * @日期:2014年7月15日 上午11:59:50
	 */
	public static interface OnRefreshListener {

		public void onRefresh();

	}
	
	/**
	 * @CLASS:OnLoadMoreListener
	 * @描写叙述: 载入很多其它回调接口	
	 * @作者:zhangshuo
	 * @版本号:v1.0
	 * @日期:2014年7月15日 下午12:00:06
	 */
	public static interface onl oadMoreListener {

		public void onl oadMore();

	}

}
<span style="font-size:24px;">因为时间关系,我主要測试了ScrollView,代码中已实现ListView下拉和上拉刷新,只是没有怎么測。</span>
<span style="font-size:24px;">至于GridView、WebView等,代码中没有实现。只是非常好拓展,在isReadyForPullUp() 和 
isReadyForPullDown()这两个方法中增加对应的View的上下边界推断就OK了!</span>
技术分享技术分享



源代码下载地址:http://download.csdn.net/detail/super_spy/7642641

Android PullToRefresh 下拉刷新,上拉很多其它,支持ScrollView,ListView,可方便拓展GridView,WebView等