手把手教你轻松实现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"去掉 ...
随机推荐
- ArrayList add方法的实现之扩容
初探ArrayList的1.5倍扩容 add方法是通过在list的尾部追加元素的方法,添加数据的. 其中,调用了一个叫ensureCapacityInternal方法,实现list的容量换算等: 注意 ...
- JMeter如何和Charles进行接口测试
什么是接口测试,接口测试到底如何开展,我相信任何一个软件测试人员都会有这样的疑问, 这里我以接口测试自动化平台的登录接口进行讲解. 一.什么是接口测试? 接口测试是测试系统组件间接口的一种测试.接口测 ...
- Spring Cloud 服务端注册与客户端调用
Spring Cloud 服务端注册与客户端调用 上一篇中,我们已经把Spring Cloud的服务注册中心Eureka搭建起来了,这一章,我们讲解如何将服务注册到Eureka,以及客户端如何调用服务 ...
- Centos 6.5 安装 rar
wget http://www.rarsoft.com/rar/rarlinux-x64-5.4.0.tar.gz tar -zxvf rarlinux-x64-5.4.0.tar.gz cd rar ...
- CSS中display:block属性的作用
display:block可以理解为块,举个简单的例子!比如你做一个超链接,<li><a href="#">超链接</a></li> ...
- PHP date() 函数
实例 格式化本地日期和时间,并返回格式化的日期字符串: <?php // Prints the dayecho date("l") . "<br>&qu ...
- How to Change Default Web ADI Upload Parameters for FlexField Import / Validation
How to Change Default Web ADI Upload Parameters for FlexField Import / Validation (文档 ID 553345.1) 转 ...
- webpack2 配置 示例
// https://github.com/webpack-contrib/extract-text-webpack-plugin var webpack = require("webpac ...
- nginx 日志分析工具goaccess
参考:https://www.goaccess.io/download 安装 $ wget http://tar.goaccess.io/goaccess-1.1.1.tar.gz $ tar -xz ...
- 安卓高级8 SurfaceView案例三 结合mediaplay播放视频
我们知道mediaplay无法直接播放视频所以我们结合Surface package qianfeng.com.mediaplayerdemo; import android.media.MediaP ...