android 安卓 listview 支持下拉刷新 上拉加载更多

【1】重写listView

import java.text.SimpleDateFormat;
import java.util.Date;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
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.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

public class RefreshListView extends ListView implements OnScrollListener {  

	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  
	private View headView;  
	private ImageView headArrow;  
	private ProgressBar progressBar;  
	private TextView headTitle;  
	private TextView headLastUpdate;  
	private int headContentWidth;  
	private int headContentHeight;  
	private Animation animation;  
	private Animation reverseAnimation;  
	private OnRefreshListner refreshListner;// 刷新监听器  
	private boolean isRefreshable;  
	private boolean isRecored = false;// 用来记录第一次按下坐标点,在整个滑动的过程中 只记录一次  
	private float startY;  
	private boolean isBack = false;// 是从 松开刷新状态 来到的 下拉刷新状态  

	public RefreshListView(Context context, AttributeSet attrs) {  
		super(context, attrs);  
		init(context);  
	}  

	private void init(Context context) {  
		// listview 设置滑动时缓冲背景色  
		setCacheColorHint(0x00000000);  
		headView = View.inflate(context, R.layout.listview_header, null);  
		headArrow = (ImageView) headView.findViewById(R.id.iv_listview_header_arrow);  
		progressBar = (ProgressBar) headView.findViewById(R.id.pb_listview_header);  
		headTitle = (TextView) headView.findViewById(R.id.tv_listview_header_last_update_time);  
		headLastUpdate = (TextView) headView .findViewById(R.id.tv_listview_header_state);  
		headArrow.setMinimumWidth(50);  
		headArrow.setMinimumHeight(70);  
		MeasureView(headView);  
		headContentWidth = headView.getMeasuredWidth();  
		headContentHeight = headView.getMeasuredHeight();  
		headView.setPadding(0, -1*headContentHeight, 0, 0);  
		// 为listView加入顶部View  
		addHeaderView(headView);  
		setOnScrollListener(this);  
		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 (firstVisibleIndex == 0 && !isRecored) {  
				startY = event.getY();  
				isRecored = true;  
			}  
			break;  
		case MotionEvent.ACTION_MOVE:  
			float tempY = event.getY();  
			if (firstVisibleIndex == 0 && !isRecored) {  
				startY = tempY;  
				isRecored = true;  
			}  
			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 (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 onRefresh() {  
		if (refreshListner != null) {  
			refreshListner.onRefresh();  
		}  
	}  

	/** 
	 * HeadView的状态变化效果 
	 */  
	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;  
		}  
	}  

	private int lastPos;//最后一个可见的item的位置  
	private int count;//item总数,注意不是当前可见的item总数  
	private boolean hasFoot = false;//是否有了Foot  

	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();  
		}  
	}
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		if(isFootLoading) return;  
		if(hasFoot && scrollState==SCROLL_STATE_IDLE){  
			isFootLoading = true;  
			onFootLoading();  
		} 
	}
	/** 
	 * 设置下拉刷新监听 
	 */  
	public void setOnRefreshListner(OnRefreshListner listener) {  
		// 设置下拉刷新可用  
		isRefreshable = true;  
		refreshListner = listener;  
	}  

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

	public void setOnAddFootListener(OnAddFootListener addFootListener){  
		onAddFootListener = addFootListener;  
	}  

	//执行添加foot  
	public void onAddFoot(){  
		if(onAddFootListener!=null && hasFoot)  
			onAddFootListener.addFoot();  
	}  
	//是否添加Foot的监听器,如果写在OnFootLoadingListener中会有延迟,效果不好  
	//应该是先进入添加Foot的状态,再进入FootLoading的状态  
	public OnAddFootListener onAddFootListener;  
	//是否进入从底部加载数据的状态的监听器  
	public OnFootLoadingListener footLoadingListener;  
	//正在加载底部数据  
	private boolean isFootLoading = false;  
	public void setOnFootLoadingListener(OnFootLoadingListener footLoading){  
		footLoadingListener = footLoading;  
	}  

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

	/** 
	 * 上拉刷新监听器 
	 */  
	public interface OnFootLoadingListener{  
		// 这里是执行后台获取数据的过程 
		void onFootLoading();  
	}  

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

	/** 
	 * 底部数据加载完成,用户需要加入一个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);  
	}
}

【2】下拉刷新头部布局文件listview_header.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
	android:layout_height="wrap_content"
	android:orientation="horizontal" >

	<FrameLayout
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_margin="10dip" >

		<ImageView
			android:id="@+id/iv_listview_header_arrow"
			android:layout_width="40dp"
			android:layout_height="40dp"
			android:layout_gravity="center"
			android:minWidth="30dip"
			android:src="@drawable/arrow" />

		<ProgressBar
			android:id="@+id/pb_listview_header"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:layout_gravity="center"
			android:indeterminateDrawable="@drawable/common_progressbar"
			android:visibility="gone" />
	</FrameLayout>

	<LinearLayout
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:layout_gravity="center_vertical"
		android:gravity="center_horizontal"
		android:orientation="vertical" >

		<TextView
			android:id="@+id/tv_listview_header_state"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:text="下拉刷新"
			android:textColor="#FF0000"
			android:textSize="18sp" />

		<TextView
			android:id="@+id/tv_listview_header_last_update_time"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:layout_marginTop="5dip"
			android:text="最后刷新时间: 2014-10-10 12:56:12"
			android:textColor="@android:color/white"
			android:textSize="14sp" />
	</LinearLayout>

</LinearLayout>

【3】上拉加载更多布局文件listview_footer.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
	android:layout_height="wrap_content"
	android:orientation="vertical" >

	<LinearLayout
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_gravity="center_horizontal"
		android:layout_margin="10dip"
		android:gravity="center_vertical"
		android:orientation="horizontal" >

		<ProgressBar
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:layout_gravity="center"
			android:indeterminateDrawable="@drawable/common_progressbar" />

		<TextView
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:layout_marginLeft="10dip"
			android:text="加载更多..."
			android:textColor="#FF0000"
			android:textSize="18sp" />
	</LinearLayout>

</LinearLayout>

【4】progressBar--common_progressbar.xml

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
	android:fromDegrees="0"
	android:pivotX="50%"
	android:pivotY="50%"
	android:toDegrees="360" >

	<shape
		android:innerRadiusRatio="3"
		android:shape="ring"
		android:useLevel="false" >
		<gradient
			android:centerColor="#FF6666"
			android:endColor="#FF0000"
			android:startColor="#FFFFFF"
			android:type="sweep" />
	</shape>

</rotate>

【5】使用方法

	adapter=new myListAdapter(items,xxx.this);
	listView.setAdapter(adapter);
	/**
	* 下拉刷新回调
	*/
	listView.setOnRefreshListner(new OnRefreshListner() {

	    public void onRefresh() {
	        new AsyncTask<Void, Void, ArrayList<String>>(){  
		@Override  
		protected ArrayList<String> doInBackground(Void... params) {  
	            try {  
		    //模拟从服务器获取数据的过程  
			Thread.sleep(1500);  
			} catch (InterruptedException e) {  
			    e.printStackTrace();  
			}  
		            return null;  
			}  
			//更新UI的方法,系统自动实现  
			@Override  
			protected void onPostExecute(ArrayList<String> result) {  
			    data.addAll(0,result);//注意是往前添加数据  
			    adapter.notifyDataSetChanged();  
			    listView.onRefreshComplete();//完成下拉刷新,这个方法要调用
			    super.onPostExecute(result);  
			}  
		}.execute();  
	    }
	});

	//上拉加载更多功能

	final View footer = View.inflate(getActivity(), R.layout.listview_footer, null); 
        listView.setOnAddFootListener(new OnAddFootListener() {
	    public void addFoot() {
		listView.addFooterView(footer);  
	    }
	});
	listView.setOnFootLoadingListener(new OnFootLoadingListener() {  
            public void onFootLoading() {  
		new AsyncTask<Void, Void, ArrayList<String>>(){  
		    @Override  
		    protected ArrayList<String> doInBackground(Void... params) {  
		        try {  
		             //模拟从服务器获取数据的过程  
		             Thread.sleep(2000);  
		             } catch (InterruptedException e) {  
		                 e.printStackTrace();  
		               }  
		             return null;  
		     }  
		 //在doInBackground后面执行  
		    protected void onPostExecute(ArrayList<String> result) {    
		       data.addAll(result);//这个是往后添加数据  
		       adapter.notifyDataSetChanged();  
		       listView.onFootLoadingComplete();//完成上拉刷新,就是底部加载完毕,这个方法要调用  
		       //移除footer,这个动作不能少  
		       listView.removeFooterView(footer);  
		       super.onPostExecute(result);  
		    }  
		}.execute();  
	    }  
	});

【6】注意事项

listview设置项目点击事件

listView.setOnItemClickListener(new OnItemClickListener() {

public void onItemClick(AdapterView<?> parent, View view,int position, long id) {

if(position==0||position==items.length()+1) return;

//获取每项的数据,position需减1,第0项为listview_head

}

时间: 2024-10-12 04:45:56

android 安卓 listview 支持下拉刷新 上拉加载更多的相关文章

十分钟实现ListView下拉刷新上滑加载更多

说到ListView下拉刷新几乎每个APP都会用到,所以ListView下拉刷新是很重要的,就像ListView优化一样是你必会的东西. ListView实现下拉刷新如果我们开发人员自己编写相对来说比较费事的,当我们使用第三方库之后我们再来开发这个功能就会省事很多.相比与自己实现可以少编写不少代码,Android-PullToRefresh库可以轻松实现ListView的下拉刷新功能. 要使用Android—PullToRefesh库对ListView实现下拉刷新要经过以下几个步骤: 1.下载A

最新Android ListView 下拉刷新 上滑加载

开发项目过程中基本都会用到listView的下拉刷新和上滑加载更多,之前大家最常用的应该是pull to refresh或它的变种版吧,google官方在最新的android.support.v4包中增加了一个新类SwipeRefreshLayout,地址 这个类的作用就是提供官方的下拉刷新,并且效果相当不错,而上拉加载更多则用我们自定义的listview,也是相当简单. 下拉刷新 简单的介绍下: 首先它是一个viewgroup,但是它只允许有一个子控件,子控件能是任何view,使用的时候,所在

Android下拉刷新库,利用viewdraghelper实现,集成了下拉刷新,底部加载更多,数据初始加载显示loading等功能

项目Github地址:https://github.com/sddyljsx/pulltorefresh Android下拉刷新库,利用viewdraghelper实现. 集成了下拉刷新,底部加载更多,以及刚进入加载数据的loadview.包括了listview与gridview的改写. 效果1: 效果2: 效果3: 效果4: 效果5: 使用说明: imageList=(ListView)findViewById(R.id.image_list); imageAdapter=new ImageA

IOS学习之UiTableView下拉刷新与自动加载更多,百年不变的效果

IOS学习之UiTableView下拉刷新与自动加载更多,百年不变的效果(五) 五一劳动节马上来临,小伙伴有妹有很激动哟,首先祝天下所有的程序猿节日快乐!这个五一对于我来说有点不一样,我的人生从这个五一就转弯了,爱情长跑8年的我结婚了,一会支付宝账号我会公布出去,请自觉打款!谢谢合作. 灯光闪起来: 舞蹈跳起来: 歌曲唱起来: -------------------------------------------------------------------------------------

Android ListView 下拉刷新 点击加载更多

最近项目中用到了ListView的下拉刷新的功能,总结了一下前辈们的代码,单独抽取出来写了一个demo作为示例. 效果图 下拉刷新: 加载更多: CustomListView.java [java] view plaincopy package com.example.uitest.view; import java.util.Date; import com.example.uitest.R; import android.content.Context; import android.uti

支持下拉刷新和上划加载更多的自定义RecyclerView(仿XListView效果)

首先看效果 下拉刷新:        上划加载        在项目更新的过程中,遇到了一个将XListView换成recyclerView的需求,而且更换完之后大体效果不能变,但是对于下拉刷新这样的效果,谷歌给出的解决方案是把RecyclerView放在一个SwipeRefreshLayout中,但是这样其实是拉下一个小圆形控件实现的,和XListView的header效果不同.在网上找了很多的别人代码,都没有实现我想要的效果,于是自己动手写了一个. 具体实现的效果有以下几条 下拉刷新功能:

Android 下拉刷新上啦加载SmartRefreshLayout + RecyclerView

在弄android刷新的时候,可算是耗费了一番功夫,最后发觉有现成的控件,并且非常好用,这里记录一下. 原文是 https://blog.csdn.net/huangxin112/article/details/78781682 ,这里是看了之后,结合自己实际遇到的问题写的. 首先引入包. //下拉框 implementation 'com.android.support:recyclerview-v7:28.0.0-beta01' implementation 'com.scwang.smar

Android如何定制一个下拉刷新,上滑加载更多的容器

前言 下拉刷新和上滑加载更多,是一种比较常用的列表数据交互方式. android提供了原生的下拉刷新容器 SwipeRefreshLayout,可惜样式不能定制. 于是打算自己实现一个专用的.但是下拉刷新和上滑,非常考验对android布局与父子触摸机制的功底,因此参考gitHub上的一个热门的下拉刷新项目 之所以选择他是因为它一个类就完成了所有View的适配,非常的精简强力. 需求 咱对下拉刷新.上滑加载更多的控件,需求如下: 1:下拉刷新,拖动到一定距离,提示文字变成 放手刷新 2:刷新完成

Android 自定义 ListView 上下拉动&ldquo;刷新最新&rdquo;和&ldquo;加载更多&rdquo;歌曲列表

本文内容 环境 测试数据 项目结构 演示 参考资料 本文演示,上拉刷新最新的歌曲列表,和下拉加载更多的歌曲列表.所谓"刷新最新"和"加载更多"是指日期.演示代码太多,点击此处下载,自己调试一下. 下载 Demo 环境 Windows 2008 R2 64 位 Eclipse ADT V22.6.2,Android 4.4.3 SAMSUNG GT-I9008L,Android OS 2.2.2 测试数据 本演示的歌曲信息,共有 20 条,包括歌手名.歌曲名.时长.缩