手把手教你轻松实现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"去掉 ...
随机推荐
- 上传本地项目到Github
进入要上传的本地文件夹,右键打开Git Bash Here,然后进行以下步骤: 1.在命令行中,输入"git init",使Test文件夹加入git管理: 2.输入"gi ...
- CentOS7快速配置nginx node mysql8.0
目录: (一)基础准备 (二)安装node (三)安装nginx (四)安装mySql8.0 (五)整体配置 (六)安装PM2守护进程 (一)基础准备1.1 概述 服务器操作系统为 centos7.4 ...
- Delphi 日期时间函数
DateUtils.IncYear();DateUtils.IncMonth();DateUtils.IncWeek();DateUtils.IncDay();DateUtils.IncHour(); ...
- Unique-paths (动态规划)
题目描述 A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below) ...
- Day 1 Python简单程序
一.高级语言和低级语言 最初的计算机程序都是用0和1的序列表示的,程序员直接使用的是机器指令,无需翻译,从纸带打孔输入即可执行得到结果.后来为了方便记忆,就将用0.1序列表示的机器指令都用符号助记 ...
- 使用CSS让多出来的字变为省略号
<style> .text1 { width:200px; overflow:hidden; text-overflow:ellipsis; -o-text-overflow:ellips ...
- 聊聊LightProbe原理实现以及对LightProbe数据的修改
0x00 前言 最近工作比较忙,所以文章已经很久没有更新了.这篇小文的主题也是在出差的高铁上想到,因为最近和一些朋友聊天,发现他们中很多人的项目中都使用了多个实时光源.细问之下主要是某些物体,例如角色 ...
- acm几何
fzu 2231,N个点求构成的平行四边行个数. 题意简重点在优化上 #include <cstdio> #include <iostream> #include <cs ...
- FJUT第四周寒假作业[JL]最后的晚餐(动态规划)
题目来源:http://210.34.193.66:8080/vj/Contest.jsp?cid=163#P4 [JL]最后的晚餐 TimeLimit:1000MS MemoryLimit:100 ...
- GDAL C#中文路径,中文属性名称乱码问题
昨天写的博客,将C#读取shp中文属性值乱码的问题应该可以解决,博客地址为:http://blog.csdn.net/liminlu0314/article/details/54096119,然后又测 ...