手把手教你轻松实现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"去掉 ...
随机推荐
- Java Servlet 笔记4
Servlet 客户端 HTTP 请求 当浏览器请求网页时,它会向 Web 服务器发送特定信息,这些信息不能被直接读取,因为这些信息是作为 HTTP 请求的头的一部分进行传输的. 读取 HTTP 头的 ...
- Linux之grep命令
概述 所有的类linux系统都会提供一个名为grep(global regular expression print,全局正则表达式输出)的搜索工具.grep命令在对一个或多个文件的内容进行基于模式的 ...
- Java web 前端面试知识点总结
经过几家大厂面试,目前成功拿到唯品会offer,分享一下我的面试知识点总结: 耦合性:也称块间联系.指软件系统结构中各模块间相互联系紧密程度的一种度量.模块之间联系越紧密,其耦合性就越强,模块的独立性 ...
- Json数组删除
有一个json数组,{'people':[{'name':'jetty','sex':'男'},{'name':'lily','sex':'女'}]} 有一个json:var aa={'name':' ...
- jquery easyui combobox 从指定位置开始模糊查询
$("#bzr").combobox({ url: "ClassSave.aspx?opt=bzr&bzr=<%=arrbj[2]%>", ...
- org.apache.maven.archiver.MavenArchiver.getManifest
eclipse导入新的maven项目时,pom.xml第一行报错: org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.mav ...
- 【python标准库模块一】时间模块time学习
本文介绍python的标准库模块time的常见用法 时间模块time 导入时间模块 import time 得到时间戳,这是统计从1970年1月1日0点0分到现在经过了多少秒,一般用于加减法一起用,比 ...
- C++ substr
函数文档:http://www.cplusplus.com/reference/string/string/substr/ /* substr(size_t pos = 0,size_t len = ...
- Java内存泄漏分析系列之三:jstat命令的使用及VM Thread分析
原文地址:http://www.javatang.com 使用jstat命令 当服务器CPU100%的时候,通过定位占用资源最大的线程定位到 VM Thread: "VM Thread&qu ...
- 安卓Tv开发(一)移动智能电视之焦点控制(触控事件)
前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大量使用,但是这些设备上的开发并不是和传统手机开发一样,特别是焦点控制和用户操作体验风格上有很大的区别,本系列博 ...