listview下拉刷新和上拉加载更多的多种实现方案
listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明。
方案一:添加头布局和脚布局
android系统为listview提供了addfootview和addheadview两个API。这样可以直接自定义一个View,以添加视图的形式实现下来刷新和上拉加载。
实现步骤
1、创建一个类继承ListView:class PullToRefreshListView extends ListView;
2、在构造方法中添加HeadView:addHeaderView(headView);
3、获取HeadView的高。测量控件的高可以有两方法getMeasuredHeight和getHeight,getMeasuredHeight()在onMeasure方法执行之后才能获取到;getHeight() 在onLayout方法执行之后才能获取到值;
4、显示和隐藏headView,通过setpadding实现,当向下滑,且第一条可见item是第0条的时候才需要设置HeadView的paddingTop来显示HeadView。
显示:headView.setPadding(0,0,0,0);
隐藏:headView.setPadding(0,-headViewHeight,0,0);
5、下拉刷新三种状态的判断,移动的时候,当paddingTop < 0 的时候,说明HeadView没有完全显示出来,进入下拉刷新状态;移动的时候,当paddingTop >= 0 的时候, 说明HeadView已经完全显示出来了,进入松开以新状态;手指抬起的时候,且当前状态是松开刷新状态的时候,进入正在刷新状态; 当已经是“正在刷新”状态时, 则不允许再做”下拉刷新”和”松开刷新”的操作了,在Move事件中加入判断,如果已经是正在刷新状态了,则不处理下拉的操作了。
6、下拉箭头的转动。下拉刷新是向下,松开刷新时向上。旋转动画通过属性动画实现。隐藏箭头的时候要清除动画:iv_arrow.clearAnimation(); 如果不隐藏动画效果,设置View.GONE之后还是看得见的。
7、HeadView显示时,当手指松开时的处理,松开时如果是“正在刷新”状态,则把headVie完全显示;松开时如果是“下拉刷新”状态,则把HeadView完全隐藏。
8、增加FooterView:addFooterView(footerView)。当ListView处于空闲状态,并且最后一条可见item是ListView中的最后一条数据时显示footview, footerView显示出来后,ListView不会自动上滑把FooterView显示出来的,所以需要手动设置:setSelection(getCount() - 1);即选中最后一条。
9、增加回调监听器。当ListView处于刷新状态的时候会调用onRefreshing()方法;当ListView处于加载更多的时候会调用onLoadMore()。加载完成后通知控件加载完成。
具体实现:
import com.itheima.pulltorefreshlistview.R; import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView; public class PullToRefreshListView extends ListView { private View headerView;
private float downY;
private int headerViewHeight;
/** 状态:下拉刷新 */
private static final int STATE_PULL_TO_REFRESH = 0;
/** 状态:松开刷新 */
private static final int STATE_RELEASE_REFRESH = 1;
/** 状态:正在刷新 */
private static final int STATE_REFRESHING = 2;
/** 当前状态 */
private int currentState = STATE_PULL_TO_REFRESH; // 默认是下拉刷新状态
private ImageView iv_arrow;
private ProgressBar progress_bar;
private TextView tv_state;
private RotateAnimation upAnim;
private RotateAnimation downAnim;
private OnRefreshingListener mOnRefreshingListener;
private View footerView;
private int footerViewHeight;
/** 正在加载更多 */
private boolean loadingMore; public PullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeaderView();
initFooterView();
} private void initHeaderView() {
headerView = View.inflate(getContext(), R.layout.header_view, null);
iv_arrow = (ImageView) headerView.findViewById(R.id.iv_arrow);
progress_bar = (ProgressBar) headerView.findViewById(R.id.progress_bar);
showRefreshingProgressBar(false);
tv_state = (TextView) headerView.findViewById(R.id.tv_state);
headerView.measure(0, 0); // 主动触发测量,mesure内部会调用onMeasure
headerViewHeight = headerView.getMeasuredHeight();
hideHeaderView();
super.addHeaderView(headerView);
upAnim = createRotateAnim(0f, -180f);
downAnim = createRotateAnim(-180f, -360f);
} private void initFooterView() {
footerView = View.inflate(getContext(), R.layout.footer_view, null);
footerView.measure(0, 0);// 主动触发测量,mesure内部会调用onMeasure
footerViewHeight = footerView.getMeasuredHeight();
hideFooterView();
super.addFooterView(footerView); super.setOnScrollListener(new OnScrollListener() { // 当ListView滚动的状态发生改变的时候会调用这个方法
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE // ListView处于空闲状态
&& getLastVisiblePosition() == getCount() - 1 // 界面上可见的最后一条item是ListView中最后的一条item
&& loadingMore == false // 如果当前没有去做正在加载更多的事情
) {
loadingMore = true;
showFooterView();
setSelection(getCount() - 1); if (mOnRefreshingListener != null) {
mOnRefreshingListener.onLoadMore();
}
}
} // 当ListView滚动的时候会调用这个方法
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { }
});
} private void hideFooterView() {
int paddingTop = -footerViewHeight;
setFooterViewPaddingTop(paddingTop);
} private void showFooterView() {
int paddingTop = 0;
setFooterViewPaddingTop(paddingTop);
} private void setFooterViewPaddingTop(int paddingTop) {
footerView.setPadding(0, paddingTop, 0, 0);
} /**
* 设置显示进度的圈圈
* @param showProgressBar 如果是true,则显示ProgressBar,否则的话显示箭头
*/
private void showRefreshingProgressBar(boolean showProgressBar) {
progress_bar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
iv_arrow.setVisibility(!showProgressBar ? View.VISIBLE : View.GONE); if (showProgressBar) {
iv_arrow.clearAnimation(); // 有动画的View要清除动画才能真正的隐藏
}
} /**
* 创建旋转动画
* @param fromDegrees 从哪个角度开始转
* @param toDegrees 转到哪个角度
* @return
*/
private RotateAnimation createRotateAnim(float fromDegrees, float toDegrees) {
int pivotXType = RotateAnimation.RELATIVE_TO_SELF; // 旋转点的参照物
int pivotYType = RotateAnimation.RELATIVE_TO_SELF; // 旋转点的参照物
float pivotXValue = 0.5f; // 旋转点x方向的位置
float pivotYValue = 0.5f; // 旋转点y方向的位置
RotateAnimation ra = new RotateAnimation(fromDegrees, toDegrees, pivotXType, pivotXValue, pivotYType, pivotYValue);
ra.setDuration(300);
ra.setFillAfter(true); // 让动画停留在结束位置
return ra;
} /** 隐藏HeaderView */
private void hideHeaderView() {
int paddingTop = -headerViewHeight;
setHeaderViewPaddingTop(paddingTop);
} /** 显示HeaderView */
private void showHeaderView() {
int paddingTop = 0;
setHeaderViewPaddingTop(paddingTop);
} /**
* 设置HeaderView的paddingTop
* @param paddingTop
*/
private void setHeaderViewPaddingTop(int paddingTop) {
headerView.setPadding(0, paddingTop, 0, 0);
} @Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (currentState == STATE_REFRESHING) {
// 如果当前已经是“正在刷新“的状态了,则不用去处理下拉刷新了
return super.onTouchEvent(ev);
} int fingerMoveDistanceY = (int) (ev.getY() - downY); // 手指移动的距离
// 如果是向下滑动,并且界面上可见的第一条item是ListView的索引为0的item时我们才处理下拉刷新的操作
if (fingerMoveDistanceY > 0 && getFirstVisiblePosition() == 0) {
int paddingTop = -headerViewHeight + fingerMoveDistanceY;
setHeaderViewPaddingTop(paddingTop); if (paddingTop < 0 && currentState != STATE_PULL_TO_REFRESH) {
// 如果paddingTop小于0,说明HeaderView没有完全显示出来,则进入下拉刷新的状态
currentState = STATE_PULL_TO_REFRESH;
tv_state.setText("下拉刷新");
iv_arrow.startAnimation(downAnim);
showRefreshingProgressBar(false);
// 让箭头转一下
} else if (paddingTop >= 0 && currentState != STATE_RELEASE_REFRESH) {
// 如果paddingTop>=0,说明HeaderView已经完全显示出来,则进入松开刷新的状态
currentState = STATE_RELEASE_REFRESH;
tv_state.setText("松开刷新");
iv_arrow.startAnimation(upAnim);
showRefreshingProgressBar(false); }
return true;
}
break;
case MotionEvent.ACTION_UP:
if (currentState == STATE_RELEASE_REFRESH) {
// 如果当前状态是松开刷新,并且抬起了手,则进入正在刷新状态
currentState = STATE_REFRESHING;
tv_state.setText("正在刷新");
showRefreshingProgressBar(true);
showHeaderView(); if (mOnRefreshingListener != null) {
mOnRefreshingListener.onRefreshing();
}
} else if (currentState == STATE_PULL_TO_REFRESH) {
// 如果抬起手时是下拉刷新状态,则把HeaderView完成隐藏
hideHeaderView();
}
break;
}
return super.onTouchEvent(ev);
} public void setOnRefreshingListener(OnRefreshingListener mOnRefreshingListener) {
this.mOnRefreshingListener = mOnRefreshingListener;
} /** ListView刷新的监听器 */
public interface OnRefreshingListener {
/** 当ListView可以刷新数据的时候会调用这个方法 */
void onRefreshing();
/** 当ListView可以加载更多 的时候会调用这个方法 */
void onLoadMore();
} /** 联网刷新数据的操作已经完成了 */
public void onRefreshComplete() {
hideHeaderView();
currentState = STATE_PULL_TO_REFRESH;
showRefreshingProgressBar(false);
} /** 加载更多新数据的操作已经完成了 */
public void onLoadmoreComplete() {
hideFooterView();
loadingMore = false;
} }
调用listview:
listView = (PullToRefreshListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
listView.setOnRefreshingListener(new OnRefreshingListener() {
@Override
public void onRefreshing() {
reloadData();
}
@Override
public void onLoadMore() {
oadMore();
}
});
方案二: listview的多种样式显示
设置listview的适配器的时候可以实现两个方法: getViewTypeCount()和getItemViewType(),前者指定条目的种类,后者返回具体的类型,这样可以根据不同的类型设计相关的样式,包括上拉加载更多,和下拉刷新,两者类似,因此这里仅仅给出加载更多的写法。具体实现如下:
1、重写getViewTypeCount()和getItemViewType(),这里包括普通的item条目和加载更多的条目,所以getViewTypeCount()返回值为2;
@Override
public int getViewTypeCount() {
return super.getViewTypeCount() + 1;
} @Override
public int getItemViewType(int position) {
if (position == getCount() - 1) {
return 0;
} else {
return addViewType(position); //构造一个方法出来,方便子类修改,添加更多的样式
}
} public int addViewType(int position) {
return 1;
}
2、在getview()中针对不同的类型添加布局:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
BaseHoldle holdle;
if (convertView == null) {
if (getItemViewType(position) == 0) { //type为0 表示应该加载加载更多的视图
holdle = getLoadmoreHoldle();
} else { //否则为普通视图
holdle = getSpecialBaseHoldle(position);
}
} else {
holdle = (BaseHoldle) convertView.getTag();
} if (getItemViewType(position) == 0) { //加载更多视图,请求网络获取数据
if (havemore()) {
holdle.setDataAndRefreshHoldleView(LoadmoreHoldle.LOADMORE_LODING);
triggleLoadMoreData();
} else {
holdle.setDataAndRefreshHoldleView(LoadmoreHoldle.LOADMORE_NONE);
}
} else { //普通视图视图,请求网络获取数据 T data = (T) mdata.get(position);
holdle.setDataAndRefreshHoldleView(data);
} mHoldleView = holdle.mHoldleView;
mHoldleView.setScaleX(0.6f);
mHoldleView.setScaleY(0.6f);
ViewCompat.animate(mHoldleView).scaleX(1).scaleY(1).setDuration(400).setInterpolator(new OvershootInterpolator(4)).start();
return mHoldleView;
}
3、具体的加载更多视图的实现
private BaseHoldle getLoadmoreHoldle() {
if (mLoadmoreHoldle == null) {
mLoadmoreHoldle = new LoadmoreHoldle();
}
return mLoadmoreHoldle;
} public class LoadmoreHoldle extends BaseHoldle {
@Bind(R.id.item_loadmore_container_loading)
LinearLayout itemloadmorecontainerloading;
@Bind(R.id.item_loadmore_container_retry)
LinearLayout itemloadmorecontainerretry;
@Bind(R.id.item_loadmore_tv_retry)
TextView item_loadmore_tv_retry; public static final int LOADMORE_LODING = 0;
public static final int LOADMORE_ERROR = 1;
public static final int LOADMORE_NONE = 2;
private int mCurretState; @Override
public void refreshHoldleView(Object data) {
itemloadmorecontainerloading.setVisibility(View.GONE);
itemloadmorecontainerretry.setVisibility(View.GONE);
mCurretState = (int) data;
switch (mCurretState) {
case LOADMORE_LODING:
itemloadmorecontainerloading.setVisibility(View.VISIBLE);
break;
case LOADMORE_ERROR:
itemloadmorecontainerretry.setVisibility(View.VISIBLE);
break;
case LOADMORE_NONE:
break;
}
} @Override
public View ininViewHoldle() {
View view = View.inflate(UiUtils.getContext(), R.layout.itemloadmore, null);
ButterKnife.bind(this, view);
return view;
}
} //holder基类,提取公共的方法
public abstract class BaseHoldle<T> {
public View mHoldleView; public T mdata; public BaseHoldle() {
mHoldleView = ininViewHoldle();
mHoldleView.setTag(this);
} public void setDataAndRefreshHoldleView(T mdata) {
this.mdata = mdata;
refreshHoldleView(mdata);
} public abstract void refreshHoldleView(T data); public abstract View ininViewHoldle(); }
方案三: SwipeRefreshLayout实现下来刷新
SwipeRefreshLayout对下不兼容,且只有下拉刷新功能没有上拉加载更多的功能。当时作为Andriod5.0之后的新特性,使用起来方便,可以直接调用系统的API。使用方法也较为简单。具体实现如下:
首先声明控件,设置颜色:
refreshLayout = (SwipeRefreshLayout) findViewById(R.id.refresh);
refreshLayout.setOnRefreshListener(this);
refreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,
android.R.color.holo_green_light,android.R.color.holo_orange_light,
android.R.color.holo_red_light);
refreshLayout.setProgressBackgroundColor(R.color.refresh_bg);
refreshLayout.setProgressBackgroundColor(R.color.refresh_bg);
写一个类实现SwipeRefreshLayout.OnRefreshListener,重写onRefresh()方法:
@Override
public void onRefresh() {
refreshLayout.postDelayed(new Runnable() {
@Override
public void run() {
//请求网络,获取数据
refreshLayout.setRefreshing(false);
}
},3000);
}
listview下拉刷新和上拉加载更多的多种实现方案的更多相关文章
- Android 自定义 ListView 上下拉动“刷新最新”和“加载更多”歌曲列表
本文内容 环境 测试数据 项目结构 演示 参考资料 本文演示,上拉刷新最新的歌曲列表,和下拉加载更多的歌曲列表.所谓"刷新最新"和"加载更多"是指日期.演示代码 ...
- juery下拉刷新,div加载更多元素并添加点击事件(二)
buffer.append("<div class='col-xs-3 "+companyId+"' style='padding-left: 10px; padd ...
- android--------自定义控件ListView实现下拉刷新和上拉加载
开发项目过程中基本都会用到listView的下拉刷新和上滑加载更多,为了方便重写的ListView来实现下拉刷新,同时添加了上拉自动加载更多的功能. Android下拉刷新可以分为两种情况: 1.获取 ...
- Android 使用PullToRefresh实现下拉刷新和上拉加载(ExpandableListView)
PullToRefresh是一套实现非常好的下拉刷新库,它支持: 1.ListView 2.ExpandableListView 3.GridView 4.WebView 等多种常用的需要刷新的Vie ...
- 使用PullToRefresh实现下拉刷新和上拉加载
使用PullToRefresh实现下拉刷新和上拉加载 分类: Android2013-12-20 15:51 78158人阅读 评论(91) 收藏 举报 Android下拉刷新上拉加载PullToRe ...
- 安卓开发笔记——关于开源组件PullToRefresh实现下拉刷新和上拉加载(一分钟搞定,超级简单)
前言 以前在实现ListView下拉刷新和上拉加载数据的时候都是去继承原生的ListView重写它的一些方法,实现起来非常繁杂,需要我们自己去给ListView定制下拉刷新和上拉加载的布局文件,然后添 ...
- Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment
1.效果预览 1.1.这个首页就是一个Fragment碎片,本文讲述的就是这个碎片的搭建方式. 下拉会有一个旋转的刷新圈,上拉会刷新数据. 1.2.整体结构 首先底层的是BaseFragment 然后 ...
- Android 5.X新特性之为RecyclerView添加下拉刷新和上拉加载及SwipeRefreshLayout实现原理
RecyclerView已经写过两篇文章了,分别是Android 5.X新特性之RecyclerView基本解析及无限复用 和 Android 5.X新特性之为RecyclerView添加Header ...
- iscroll.js 下拉刷新和上拉加载
html代码如下 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...
随机推荐
- .Net中的AOP系列之构建一个汽车租赁应用
返回<.Net中的AOP>系列学习总目录 本篇目录 开始一个新项目 没有AOP的生活 变更的代价 使用AOP重构 本系列的源码本人已托管于Coding上:点击查看. 本系列的实验环境:VS ...
- setTimeout 的黑魔法
setTimeout,前端工程师必定会打交道的一个函数.它看上去非常的简单,朴实.有着一个很不平凡的名字--定时器.让年少的我天真的以为自己可以操纵未来.却不知朴实之中隐含着惊天大密.我还记得我第一次 ...
- WebApi - 路由
这段时间的博客打算和大家一起分享下webapi的使用和心得,主要原因是群里面有朋友说希望能有这方面的文章分享,随便自己也再回顾下:后面将会和大家分不同篇章来分享交流心得,希望各位多多扫码支持和点赞,谢 ...
- 在jekyll模板博客中添加网易云模块
最近使用GitHub Pages + Jekyll 搭建了个人博客,作为一名重度音乐患者,博客里面可以不配图,但是不能不配音乐啊. 遂在博客里面引入了网易云模块,这里要感谢网易云的分享机制,对开发者非 ...
- C++ 拷贝构造函数和赋值运算符
本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义 ...
- JDBC Tutorials: Commit or Rollback transaction in finally block
http://skeletoncoder.blogspot.com/2006/10/jdbc-tutorials-commit-or-rollback.html JDBC Tutorials: Com ...
- 微信小程序开发日记——高仿知乎日报(下)
本人对知乎日报是情有独钟,看我的博客和github就知道了,写了几个不同技术类型的知乎日报APP 要做微信小程序首先要对html,css,js有一定的基础,还有对微信小程序的API也要非常熟悉 我将该 ...
- C#事件-使用事件需要的步骤
事件是C#中另一高级概念,使用方法和委托相关.奥运会参加百米的田径运动员听到枪声,比赛立即进行.其中枪声是事件,而运动员比赛就是这个事件发生后的动作.不参加该项比赛的人对枪声没有反应. 从程序员的角度 ...
- 一切从“简”,解放IT运维人员
运维人的神技 运维既是个技术活儿也是个苦差事,而运维人员被期望有着无限的技能:主机.存储.网络.操作系统样样精通,而且还要会写SQL.shell.开发语言java..net.python等等,对业务更 ...
- echo命令
linux的echo命令, 在shell编程中极为常用, 在终端下打印变量value的时候也是常常用到的, 因此有必要了解下echo的用法echo命令的功能是在显示器上显示一段文字,一般起到一个提示的 ...