上篇讲了如何简单快速的的实现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上拉加载的更多相关文章

  1. 手把手教你实现Android RecyclerView上拉加载功能

    摘要 一直在用到RecyclerView时都会微微一颤,因为一直都没去了解怎么实现上拉加载,受够了每次去Github找开源引入,因为感觉就为了一个上拉加载功能而去引入一大堆你不知道有多少BUG的代码, ...

  2. ListView 上拉加载更多

    ListView 上拉加载更多 首先来个效果图 界面布局 <?xml version="1.0" encoding="utf-8"?> <Re ...

  3. 手把手教你轻松实现listview下拉刷新

    很多人觉得自定义一个listview下拉刷新上拉加载更多是一件很牛x的事情,不是大神写不出来,我想大多数童鞋都是做项目用到时就百度,什么pulltorefresh,xlistview...也不看原理, ...

  4. android ListView上拉加载更多 下拉刷新功能实现(采用pull-to-refresh)

    Android实现上拉加载更多功能以及下拉刷新功能, 采用了目前比较火的PullToRefresh,他是目前实现比较好的下拉刷新的类库. 目前他支持的控件有:ListView, ExpandableL ...

  5. ListView上拉加载,下拉刷新 PullToRefresh的使用

    PullToRefresh是一套实现非常好的下拉刷新库,它支持:ListViewExpandableListViewGridViewWebViewScrollViewHorizontalScrollV ...

  6. google官方的下拉刷新+自定义上拉加载更多

    转载请标注转载:http://blog.csdn.net/oqihaogongyuan/article/details/50949118 google官方的下拉刷新+自定义上拉加载更多 现在很多app ...

  7. ListView实现Item上下拖动交换位置 并且实现下拉刷新 上拉加载更多

    ListView实现Item上下拖动交换位置  并且实现下拉刷新  上拉加载更多 package com.example.ListViewDragItem; import android.app.Ac ...

  8. android ListView下拉刷新 上拉加载更多

    背景 最近在公司的项目中要使用到ListView的下拉刷新和上拉加载更多(貌似现在是个项目就有这个功能!哈哈),其实这个东西GitHub上很多,但是我感觉那些框架太大,而且我这个项目只用到了ListV ...

  9. ListView下拉刷新上拉加载更多实现

    这篇文章将带大家了解listview下拉刷新和上拉加载更多的实现过程,先看效果(注:图片中listview中的阴影可以加上属性android:fadingEdge="none"去掉 ...

随机推荐

  1. java开发笔记——表映射实体类代码示例

    package com.special.ipmsdm; import java.io.Serializable; import javax.persistence.Column; import jav ...

  2. python2.7入门---变量类型

      这篇文章呢,主要是用来记录python中的变量类型学习内容的.接下来就来看一下变量类型,那么什么是变量呢.变量存储在内存中的值.这就意味着在创建变量时会在内存中开辟一个空间.基于变量的数据类型,解 ...

  3. prop与attr的区别

    与prop一样attr也可以用来获取与设置元素的属性. 区别在于,对于自定义属性和选中属性的处理. 选中属性指的是 checked,selected 这2种属性 1. 对于自定义属性 attr能够获取 ...

  4. js中对象的自定义排序

    //并返回一个可以用来对包含该成员的对象数组进行排序的比较函数 var compareAsc = function (prop) { return function (obj1, obj2) { va ...

  5. 重写轮子之 ID3

    这是半成品, 已完成了 fit() 部分, 形成了包含一棵完整树的 node 对象. 后续工作是需解析该 node对象, 完成 predict() 工作. # !/usr/bin/python # - ...

  6. web缓存之--http缓存机制

    一.web缓存可以分为数据库缓存.代理服务器缓存.浏览器缓存. 其中浏览器缓存又包含很多内容:http缓存.indexDb.cookie.localStorage等.本片只讨论http缓存相关内容. ...

  7. solr服务器搭建

    百度百科定义:Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口.用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引:也可以通过Ht ...

  8. Docker常见仓库MongoDB

    MongoDB 基本信息 MongoDB 是开源的 NoSQL 数据库实现. 该仓库提供了 MongoDB 2.2 ~ 2.7 各个版本的镜像. 使用方法 默认会在 27017 端口启动数据库. $ ...

  9. 为什么《Dive into Python》不值得推荐

    2010 年 5 月 5 日更新:我翻译了一篇<<Dive Into Python>非死不可>作为对本文观点的进一步支持和对评论的回复,请见:http://blog.csdn. ...

  10. 集群技术(三)MySQL集群深度解析

    什么是MySQL集群 MySQL集群是一个无共享的(shared-nothing).分布式节点架构的存储方案,其目的是提供容错性和高性能. 数据更新使用读已提交隔离级别(read-committedi ...