`
geningaixin
  • 浏览: 18722 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

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事件实现上区分的还不是很清晰明确,还有待优化。



分享到:
评论

相关推荐

    android listView下拉刷新 上拉加载 分开

    标题提到的"android listView下拉刷新 上拉加载 分开",意味着它提供了三个独立的示例,分别针对下拉刷新和上拉加载功能进行实现。下面我们将详细探讨这些知识点。 1. **下拉刷新(Pull-to-Refresh)** 下拉刷新功能...

    Android Listview下拉刷新上拉加载源码

    "Android Listview下拉刷新上拉加载源码"这个资源可能包含了实现这些功能的具体代码示例。 下拉刷新功能允许用户通过手势向下拉动ListView的顶部来更新列表内容,通常用于获取最新数据。这种功能的实现通常依赖于第...

    android listview下拉刷新上拉加载

    首先,我们看到标题和描述提到了"android listview下拉刷新上拉加载",这通常是指在ListView顶部添加一个可下拉的刷新视图,当用户下拉时显示刷新动画并触发数据更新。同样,在ListView底部添加一个可上拉的加载视图...

    android listview下拉刷新上拉加载更多改良版

    这就是“android listview下拉刷新上拉加载更多改良版”所解决的问题。 下拉刷新(Pull-to-Refresh)功能让用户可以通过手势向下拉动列表来更新数据,而上拉加载更多(Load More)则允许用户在滚动到底部时加载更多...

    android listview 下拉刷新 上拉加载整合

    总结来说,"android listview 下拉刷新 上拉加载整合"是Android开发中提升用户体验的重要技术点,通过使用开源库或自定义实现,我们可以为ListView添加这两种功能,提高应用的交互性和功能性。对于初学者,理解并...

    android下拉刷新+上拉加载+滑动删除的ListView

    为了提升用户体验,开发者经常需要在ListView中实现下拉刷新、上拉加载和滑动删除等功能。本篇将详细讲解如何在Android中实现这些特性。 下拉刷新(Pull-to-Refresh)功能允许用户通过向下拉动列表来获取最新数据。...

    android listview下拉刷新 上拉加载更多

    android listview下拉刷新 上拉加载更多 http://blog.csdn.net/wenwei19861106/article/details/78016472 http://blog.csdn.net/wenwei19861106/article/details/78016472

    完美的ListView下拉刷新上拉加载实例Demo

    "完美的ListView下拉刷新上拉加载实例Demo"就是一个这样的示例项目,它旨在帮助开发者理解和实现这种交互模式。 下拉刷新(Pull-to-Refresh)功能允许用户通过在ListView顶部向下拉动来更新列表内容,通常用于获取...

    自定义listview下拉刷新上拉加载更多以及与google官方的下拉刷新结合使用

    总的来说,自定义ListView下拉刷新和上拉加载更多虽然涉及到一些复杂的交互逻辑,但通过合理的设计和第三方库,可以大大简化开发过程。在实际项目中,可以根据需求选择自定义实现或者利用已有的解决方案,以提供更好...

    android listview 下拉刷新 动态加载数据 图文混排

    以上就是关于“Android ListView下拉刷新、动态加载数据及图文混排”的实现步骤。通过这个功能,用户可以在滚动到列表底部时加载更多内容,而下拉刷新则允许用户获取最新的数据,提高了用户体验。

    android Listview下拉刷新 上拉(滑动分页)加载更多

    总之,下拉刷新和上拉加载更多是提升Android应用用户体验的关键特性,通过合理使用开源库和自定义事件监听,可以轻松地在ListView中实现这些功能。开发者可以根据项目需求选择合适的库,或者自定义实现,以满足各种...

    自定义listView下拉刷新上拉加载更多

    总结起来,自定义ListView下拉刷新和上拉加载更多是提升Android应用性能和用户体验的重要手段。通过自定义组件,开发者可以灵活地设计交互和视觉效果,更好地匹配应用的整体设计。理解并掌握这种自定义实现方法对于...

    android listview 下拉刷新 上拉翻页 仿新浪微博客户端

    总结,实现"android listview 下拉刷新 上拉翻页 仿新浪微博客户端"的功能,关键在于选用合适的第三方库(如XListView)并正确设置和监听其刷新和加载事件。同时,合理的数据加载策略、动画效果以及状态处理都是提升...

    android自定义下拉刷新上拉加载

    在Android应用开发中,"下拉刷新"和"上拉加载"是常见的功能,用于提供流畅的用户体验,尤其是在处理大量数据列表时。本教程将详细讲解如何在Android中实现自定义的下拉刷新和上拉加载功能。 首先,我们要了解这两个...

    android 下拉刷新 上拉加载 listview 实现demo

    在Android开发中,下拉刷新和上拉加载是常见的用户交互功能,特别是在列表视图(ListView)中。这种设计能够提升用户体验,使用户无需离开当前界面就能获取更多数据。本教程将详细介绍如何在Android中实现这样的功能。...

    Android ListView下拉刷新 Demo.rar

    本Demo "Android ListView下拉刷新 Demo.rar" 主要是为了展示如何在ListView中实现下拉刷新功能,帮助开发者更好地理解和实践这一功能。 首先,我们要理解下拉刷新的基本概念。下拉刷新,顾名思义,是指用户在...

    android stdio listview 下拉刷新 上拉加载

    综上所述,实现Android Studio环境下的ListView下拉刷新和上拉加载,需要结合SwipeRefreshLayout和自定义的OnScrollListener。这两个功能极大地提升了用户在浏览列表数据时的交互体验,使得应用更具吸引力。在实际...

    Android应用源码 ListView下拉刷新 Demo

    在Android开发中,ListView是一种常用的组件,用于展示...通过分析和理解这个"Android应用源码 ListView下拉刷新 Demo",开发者能够更好地掌握在实际项目中实现ListView下拉刷新的方法,提高应用的交互性和用户体验。

    自定义ListView实现下拉刷新+加载更多功能Demo

    在Android开发中,自定义ListView实现下拉刷新和加载更多的功能是常见的需求,尤其是在构建具有数据流滚动和实时更新的应用程序时。这个"自定义ListView实现下拉刷新+加载更多功能Demo"旨在帮助开发者理解如何集成...

Global site tag (gtag.js) - Google Analytics