手把手教你轻松实现listview上拉加载
上篇讲了如何简单快速的的实现listview下拉刷新,那么本篇将讲解如何简单快速的实现上拉加载更多。其实,如果你已经理解了下拉刷新的实现过程,那么实现上拉加载更多将变得轻松起来,原理完全一致,甚至实现代码也几乎相同:
首先,声明上拉状态的一些变量:
private static final int LOAD_DONE = 4;//上拉加载完成
private static final int PULL_TO_LOAD = 5;//上拉中(上拉高度未超出footerview高度)
private static final int RELEASE_TO_LOAD = 6;//上拉中(上拉高度超出footerview高度)
private static final int LOADING = 7;//加载中
private static final float LOAD_RATIO = 3;//上拉系数
private LinearLayout footerView;//footerView布局
private int footerViewHeight;//footerView高度
private int loadstate;//上拉加载状态
private boolean isScrollLast;//是否滑动到底部
private int totalcount;//item总数量
private TextView tv_load;//footview布局中显示的文字
在listview构造方法中初始化(部分代码):
footerView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.listview_foot_view, null, false);
tv_load = (TextView) footerView.findViewById(R.id.tv_load);
addFooterView(footerView);
footerViewHeight = footerView.getMeasuredHeight();
footerView.setPadding(0, 0, 0, -footerViewHeight);
//初始化加载状态
loadstate = LOAD_DONE;
监听OnScroll:
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
totalcount = totalItemCount;
if (firstVisibleItem + visibleItemCount == totalItemCount) {
isScrollLast = true;//滑动到底部
} else {
isScrollLast = false;
}
}
重写onTouch:
case MotionEvent.ACTION_MOVE:
/**
* 上拉加载更多
*/
if (offsetY < 0 && refreshstate == REFRESH_DONE && isScrollLast && loadstate != LOADING) {
float footerViewShowHeight = -offsetY / LOAD_RATIO;
switch (loadstate) {
case LOAD_DONE:
loadstate = PULL_TO_LOAD;
break;
case PULL_TO_LOAD:
setSelection(totalcount);
if (footerViewShowHeight - footerViewHeight >= 0) {
loadstate = RELEASE_TO_LOAD;
changeFooterByState(loadstate);
}
break;
case RELEASE_TO_LOAD:
setSelection(totalcount);
if (footerViewShowHeight - footerViewHeight < 0) {
loadstate = PULL_TO_LOAD;
changeFooterByState(loadstate);
}
break;
}
if (loadstate == PULL_TO_LOAD || loadstate == RELEASE_TO_LOAD) {
footerView.setPadding(0, 0, 0, (int) (footerViewShowHeight - footerViewHeight));
}
}
break;
此处的条件中多加了一个refreshstate == REFRESH_DONE,大家想一下这个条件起到什么作用?意思就是只要下拉状态是 REFRESH_DONE时,才能操作上拉加载,也就是说如果此时正在下拉刷新中,是不能够再上拉加载的,下拉和上拉不会同时执行;
case MotionEvent.ACTION_UP:
/**
* 上拉加载
*/
if (loadstate == PULL_TO_LOAD) {
loadstate = LOAD_DONE;
changeFooterByState(loadstate);
}
if (loadstate == RELEASE_TO_LOAD) {
loadstate = LOADING;
changeFooterByState(loadstate);
mOnLoadMoreListener.onLoadMore();
}
break;
changeFooterByState方法(和changeHeaderByState一样):
/**
* 改变footerview状态
*
* @param loadstate
*/
private void changeFooterByState(int loadstate) {
switch (loadstate) {
case LOAD_DONE:
footerView.setPadding(0, 0, 0, -footerViewHeight);
tv_load.setText("上拉加载更多");
break;
case RELEASE_TO_LOAD:
tv_load.setText("松开加载更多");
break;
case PULL_TO_LOAD:
tv_load.setText("上拉加载更多");
break;
case LOADING:
tv_load.setText("正在加载...");
footerView.setPadding(0, 0, 0, 0);
break;
default:
break;
}
}
看到这里,是不是觉得这代码跟上篇文章中的下拉刷新的实现逻辑步骤完全一样,哈哈,就是这么简单,之前看过不少别人写的listview里面,把上拉下拉混在一起判断,可读性很差,扩展性也不好,别人理解起来也有难度,所以我把两种状态分开来写,简介易懂,互不影响;
好了,回调接口写完就完事啦:
/**
* 加载更多监听
*/
public interface OnLoadMoreListener {
void onLoadMore();
}
/**
* 设置加载更多监听
*
* @param onLoadMoreListener
*/
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
mOnLoadMoreListener = onLoadMoreListener;
}
/**
* 加载更多完成
*/
public void setOnLoadMoreComplete() {
loadstate = LOAD_DONE;
changeFooterByState(loadstate);
}
再来一波,结束战斗(listview全部代码包括下拉和上拉):
package com.baiyuliang.listviewrefreshdemo;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
/**
* Created by baiyuliang on 15/12/2.
*/
public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
private static final int REFRESH_DONE = 0;//下拉刷新完成
private static final int PULL_TO_REFRESH = 1;//下拉中(下拉高度未超出headview高度)
private static final int RELEASE_TO_REFRESH = 2;//准备刷新(下拉高度超出headview高度)
private static final int REFRESHING = 3;//刷新中
private static final float REFRESH_RATIO = 3.0f;//下拉系数,越大下拉灵敏度越低
private LinearLayout headerView;//headerView布局
private int headerViewHeight;//headerView高度
private int refreshstate;//下拉刷新状态
private boolean isScrollFirst;//是否滑动到顶部
private boolean isRefreshable;//是否启用下拉刷新
private TextView tv_refresh;
private static final int LOAD_DONE = 4;//上拉加载完成
private static final int PULL_TO_LOAD = 5;//上拉中(上拉高度未超出footerview高度)
private static final int RELEASE_TO_LOAD = 6;//上拉中(上拉高度超出footerview高度)
private static final int LOADING = 7;//加载中
private static final float LOAD_RATIO = 3;//上拉系数
private LinearLayout footerView;//footerView布局
private int footerViewHeight;//footerView高度
private int loadstate;//上拉加载状态
private boolean isScrollLast;//是否滑动到底部
private int totalcount;//item总数量
private boolean isLoadable;//是否启用上拉加载
private TextView tv_load;//footview布局中显示的文字
private float startY,//手指落点
offsetY;//手指滑动的距离
//监听接口
private OnRefreshListener mOnRefreshListener;
private OnLoadMoreListener mOnLoadMoreListener;
public RefreshListView(Context context) {
super(context);
init(context);
}
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* 初始化view
*
* @param context
*/
private void init(Context context) {
setOverScrollMode(View.OVER_SCROLL_NEVER);
setOnScrollListener(this);
headerView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.listview_head_view, null, false);
tv_refresh = (TextView) headerView.findViewById(R.id.tv_refresh);
footerView = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.listview_foot_view, null, false);
tv_load = (TextView) footerView.findViewById(R.id.tv_load);
measureView(headerView);
measureView(footerView);
addHeaderView(headerView);
addFooterView(footerView);
headerViewHeight = headerView.getMeasuredHeight();
headerView.setPadding(0, -headerViewHeight, 0, 0);
footerViewHeight = footerView.getMeasuredHeight();
footerView.setPadding(0, 0, 0, -footerViewHeight);
//初始化刷新状态
refreshstate = REFRESH_DONE;
//初始化加载状态
loadstate = LOAD_DONE;
//默认启用
isRefreshable = true;
isLoadable = true;
}
@Override
public void onScrollStateChanged(AbsListView absListView, int i) {
}
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
totalcount = totalItemCount;
if (firstVisibleItem == 0) {
isScrollFirst = true;//滑动到顶部
} else {
isScrollFirst = false;
}
if (firstVisibleItem + visibleItemCount == totalItemCount) {
isScrollLast = true;//滑动到底部
} else {
isScrollLast = false;
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
offsetY = ev.getY() - startY;
/**
* 下拉刷新
*/
if (isRefreshable &&offsetY > 0 && loadstate == LOAD_DONE && isScrollFirst && refreshstate != REFRESHING) {
float headerViewShowHeight = offsetY / REFRESH_RATIO;
switch (refreshstate) {
case REFRESH_DONE:
refreshstate = PULL_TO_REFRESH;
break;
case PULL_TO_REFRESH:
setSelection(0);
if (headerViewShowHeight - headerViewHeight >= 0) {
refreshstate = RELEASE_TO_REFRESH;
changeHeaderByState(refreshstate);
}
break;
case RELEASE_TO_REFRESH:
setSelection(0);
if (headerViewShowHeight - headerViewHeight < 0) {
refreshstate = PULL_TO_REFRESH;
changeHeaderByState(refreshstate);
}
break;
}
if (refreshstate == PULL_TO_REFRESH || refreshstate == RELEASE_TO_REFRESH) {
headerView.setPadding(0, (int) (headerViewShowHeight - headerViewHeight), 0, 0);
}
}
/**
* 上拉加载更多
*/
if (isLoadable&&offsetY < 0 && refreshstate == REFRESH_DONE && isScrollLast && loadstate != LOADING) {
float footerViewShowHeight = -offsetY / LOAD_RATIO;
switch (loadstate) {
case LOAD_DONE:
loadstate = PULL_TO_LOAD;
break;
case PULL_TO_LOAD:
setSelection(totalcount);
if (footerViewShowHeight - footerViewHeight >= 0) {
loadstate = RELEASE_TO_LOAD;
changeFooterByState(loadstate);
}
break;
case RELEASE_TO_LOAD:
setSelection(totalcount);
if (footerViewShowHeight - footerViewHeight < 0) {
loadstate = PULL_TO_LOAD;
changeFooterByState(loadstate);
}
break;
}
if (loadstate == PULL_TO_LOAD || loadstate == RELEASE_TO_LOAD) {
footerView.setPadding(0, 0, 0, (int) (footerViewShowHeight - footerViewHeight));
}
}
break;
case MotionEvent.ACTION_UP:
/**
* 下拉刷新
*/
if (isRefreshable){
if (refreshstate == PULL_TO_REFRESH) {
refreshstate = REFRESH_DONE;
changeHeaderByState(refreshstate);
}
if (refreshstate == RELEASE_TO_REFRESH) {
refreshstate = REFRESHING;
changeHeaderByState(refreshstate);
mOnRefreshListener.onRefresh();
}
}
/**
* 上拉加载
*/
if (isLoadable){
if (loadstate == PULL_TO_LOAD) {
loadstate = LOAD_DONE;
changeFooterByState(loadstate);
}
if (loadstate == RELEASE_TO_LOAD) {
loadstate = LOADING;
changeFooterByState(loadstate);
mOnLoadMoreListener.onLoadMore();
}
}
break;
}
return super.onTouchEvent(ev);
}
/**
* 改变headview状态
*
* @param state
*/
private void changeHeaderByState(int state) {
switch (state) {
case REFRESH_DONE:
headerView.setPadding(0, -headerViewHeight, 0, 0);
tv_refresh.setText("下拉刷新");
break;
case RELEASE_TO_REFRESH:
tv_refresh.setText("松开刷新");
break;
case PULL_TO_REFRESH:
tv_refresh.setText("下拉刷新");
break;
case REFRESHING:
headerView.setPadding(0, 0, 0, 0);
tv_refresh.setText("正在刷新");
break;
default:
break;
}
}
/**
* 改变footerview状态
*
* @param loadstate
*/
private void changeFooterByState(int loadstate) {
switch (loadstate) {
case LOAD_DONE:
footerView.setPadding(0, 0, 0, -footerViewHeight);
tv_load.setText("上拉加载更多");
break;
case RELEASE_TO_LOAD:
tv_load.setText("松开加载更多");
break;
case PULL_TO_LOAD:
tv_load.setText("上拉加载更多");
break;
case LOADING:
tv_load.setText("正在加载...");
footerView.setPadding(0, 0, 0, 0);
break;
default:
break;
}
}
/**
* 下拉刷新监听
*/
public interface OnRefreshListener {
void onRefresh();
}
/**
* 设置下拉刷新
*
* @param onRefreshListener
*/
public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
mOnRefreshListener = onRefreshListener;
}
/**
* 下拉刷新完成
*/
public void setOnRefreshComplete() {
refreshstate = REFRESH_DONE;
changeHeaderByState(refreshstate);
}
/**
* 加载更多监听
*/
public interface OnLoadMoreListener {
void onLoadMore();
}
/**
* 设置加载更多监听
*
* @param onLoadMoreListener
*/
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
mOnLoadMoreListener = onLoadMoreListener;
}
/**
* 加载更多完成
*/
public void setOnLoadMoreComplete() {
loadstate = LOAD_DONE;
changeFooterByState(loadstate);
}
/**
* 设置是否启用下拉刷新
*
* @param isRefreshable
*/
public void setIsRefreshable(boolean isRefreshable) {
this.isRefreshable = isRefreshable;
}
/**
* 设置是否启用加载更多
*
* @param isLoadable
*/
public void setIsLoadable(boolean isLoadable) {
this.isLoadable = isLoadable;
}
/**
* 计算控件宽高
*
* @param child
*/
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);
}
}
在上述代码中,我们可以看到多了两个变量:
private boolean isRefreshable;//是否启用下拉刷新
private boolean isLoadable;//是否启用上拉加载
注释已经写的很清楚了,如果我们在某种情况下,不需要下拉或上拉,将这两个值设置为false即可,默认为true,即启用下拉和上拉!
demo下载地址:http://download.csdn.net/detail/baiyuliang2013/9344459
手把手教你轻松实现listview上拉加载的更多相关文章
- 手把手教你实现Android RecyclerView上拉加载功能
摘要 一直在用到RecyclerView时都会微微一颤,因为一直都没去了解怎么实现上拉加载,受够了每次去Github找开源引入,因为感觉就为了一个上拉加载功能而去引入一大堆你不知道有多少BUG的代码, ...
- ListView 上拉加载更多
ListView 上拉加载更多 首先来个效果图 界面布局 <?xml version="1.0" encoding="utf-8"?> <Re ...
- 手把手教你轻松实现listview下拉刷新
很多人觉得自定义一个listview下拉刷新上拉加载更多是一件很牛x的事情,不是大神写不出来,我想大多数童鞋都是做项目用到时就百度,什么pulltorefresh,xlistview...也不看原理, ...
- android ListView上拉加载更多 下拉刷新功能实现(采用pull-to-refresh)
Android实现上拉加载更多功能以及下拉刷新功能, 采用了目前比较火的PullToRefresh,他是目前实现比较好的下拉刷新的类库. 目前他支持的控件有:ListView, ExpandableL ...
- ListView上拉加载,下拉刷新 PullToRefresh的使用
PullToRefresh是一套实现非常好的下拉刷新库,它支持:ListViewExpandableListViewGridViewWebViewScrollViewHorizontalScrollV ...
- google官方的下拉刷新+自定义上拉加载更多
转载请标注转载:http://blog.csdn.net/oqihaogongyuan/article/details/50949118 google官方的下拉刷新+自定义上拉加载更多 现在很多app ...
- ListView实现Item上下拖动交换位置 并且实现下拉刷新 上拉加载更多
ListView实现Item上下拖动交换位置 并且实现下拉刷新 上拉加载更多 package com.example.ListViewDragItem; import android.app.Ac ...
- android ListView下拉刷新 上拉加载更多
背景 最近在公司的项目中要使用到ListView的下拉刷新和上拉加载更多(貌似现在是个项目就有这个功能!哈哈),其实这个东西GitHub上很多,但是我感觉那些框架太大,而且我这个项目只用到了ListV ...
- ListView下拉刷新上拉加载更多实现
这篇文章将带大家了解listview下拉刷新和上拉加载更多的实现过程,先看效果(注:图片中listview中的阴影可以加上属性android:fadingEdge="none"去掉 ...
随机推荐
- Python【第二课】 字符串,列表,字典,集合,文件操作
本篇内容 字符串操作 列表,元组操作 字典操作 集合操作 文件操作 其他 1.字符串操作 1.1 字符串定义 特性:不可修改 字符串是 Python 中最常用的数据类型.我们可以使用引号('或&quo ...
- 修改表单元素中placeholder属性样式、清除IE浏览器中input元素的清除图标和眼睛图标
一.修改input元素placeholder属性样式 在做项目的时候,一般表单元素的placeholder属性样式都是使用浏览器默认的,但有时候为了追求设计上的美感需要修表单元素的placeholde ...
- C语言作业程序设计第一次作业
1.求圆面积和面积 (1)题目: 输入圆的半径,计算圆的周长和面积 (2)流程图: (3)测试数据及运行结果 测试数据:r=4 运行结果: (4)实验分析 没有遇到问题 2.判断闰年问题 (1)题目: ...
- [坑]Spring利用注解@Value获取properties属性为null
今天在项目中想使用@Value来获取Springboot中properties中属性值. 场景:定义了一个工具类,想要获取一些配置参数,使用了@value来获取,但是死活也获取不到. 如何解决:在使用 ...
- div,margin,padding
<!-- 类比礼品盒里装方块月饼.月饼的食用部分(我们把它称之为月饼肉身)要装在小包装盒里,月饼肉身即为content.月饼肉身与直接包裹它的小包装盒(我们把它叫做月饼的衣服)之间的距离叫pad ...
- MySQL使用判断
1.case语法 在第一个方案的返回结果中, value=compare-value.而第二个方案的返回结果是第一种情况的真实结果.如果没有匹配的结果值,则返回结果为ELSE后的结果,如果没有ELSE ...
- MySQL NULL 值处理
MySQL NULL 值处理 我们已经知道MySQL使用 SQL SELECT 命令及 WHERE 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时,该命令可能就无法正常工作. 为了 ...
- PHP 高级过滤器
PHP 高级过滤器 检测一个数字是否在一个范围内 以下实例使用了 filter_var() 函数来检测一个 INT 型的变量是否在 1 到 200 内: 实例 <?php$int = 122; ...
- MongoDB $type条件操作符
描述 在本章节中,我们将继续讨论MongoDB中条件操作符 $type. $type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果. MongoDB 中可以使用的类型如下表所示: 类型 ...
- 这交互炸了(四) :一分钟让你拥有微信拖拽透明返回PhotoView
本文已授权微信公众号:鸿洋(hongyangAndroid)原创首发 <交互炸了>或许是一系列高端特效教程, 文中会介绍一些比较炫酷的特效,以及实现的思路.特效实现本身也许不会有太大的难度 ...