Android中listView的下拉加载功能实现

  • 今天给大家讲讲android开发中比较常见的listView的下拉加载,其实也可以叫做分页加载。为什么会有这个叫法呢?说说我的理解吧!

    • 从字面上很好理解。当你滑动一个列表到底部的时候,这个时候就会出现正在加载的底部加载布局去加载更多的数据。这里拿微信作为一个例子,如下图所示:
    • 如上图所示红色方框的部分就是底部的加载布局。从一定程度上这样做是必要的,为了优化用户的体验。你可以想想,假如你点开微信的朋友圈的时候,如果没有做分页加载,那么你需要等待很久的时间才能够看到你和朋友发的一些状态。这是因为这个时候代码需要给服务器发送请求获取数据并呈现这些数据。这个时候获取数据就成了最耗时的问题。为什么这么说?如果没有分页加载的功能,那么,你肯定需要获取全部的数据,你可以想想,好几年的数据,数据量可不少啊!发请求去获取这些数据并呈现它们是会很耗时间的。所以分页加载就很好的解决了这个问题。你先获取其中的十几条数据并呈现它们。当你一直从头浏览这十几条数据到结束的时候,这个时候再去加载更多的数据并呈现它们。如上图所示,这样才是好的用户体验。

  • 概念清晰后,具体实现的部分到了。这里实现微信的下拉加载效果(个人感觉很不错,简洁明了),大概需要以下几个步骤:

    • first — 首先构建下拉加载时的底部加载布局(这里命名为view_more)
    • second — 获取数据并给listView设置adapter;设置好adapter后,调用方法:listView.addFooterView(view_more);// TODO 添加底部记载布局再往listView的底部添加底部加载布局。最后对listView设置滑动监听器:listView.setOnScrollListener(this);// TODO listView这是滑动监听 并重写相关方法:

      public void onScrollStateChanged(AbsListView view, int scrollState) {}public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {} 这两个重写的方法一个是监听listView的滑动状态改变,另一个是监听listView的滑动。

    • third — 当滑动监听设置好后,在重写的两个方法里面判断listView是否滑动到底部并且停止滑动(即滑动状态为停止滑动)。如果此时满足这个条件,那么就加载更多的数据。数据加载完成后就刷新数据源,此时会有更多的数据在底部出现。
    • fourth — 最后当数据全部加载好后,移除第二步中添加的底部加载布局view_more,调用方法:listView.removeFooterView(view_more);// TODO 移除底部的加载布局

下面写代码一步一步来实现:

  • 1、首先准备一个底部加载布局,命名为view_more
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:background="#8888" >

    <TextView
        android:id="@+id/tv_Load"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="正在加载..."
        android:textSize="14sp"
        android:visibility="visible" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toLeftOf="@id/tv_Load"
        android:padding="10dp"
        android:visibility="visible" />

</RelativeLayout>

很简单的布局,显示了一个进度条和一个文本控件。如图:

  • 第二,给listView设置数据并设置滑动监听,设置好后判断是否滑动到listView的底部并停止滑动,如果是那么加载更多的数据:
package com.example.drop_down_load;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends Activity implements OnScrollListener {

    private ListView listView;
    private int totalCount;// 数据总条数
    private List<String> lists = new ArrayList<String>();
    private ArrayAdapter<String> adapter;
    // 创建handler接收消息并处理消息
    private Handler handler = new Handler() {

        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case 0:
                // 创建adapter
                adapter = new ArrayAdapter<String>(MainActivity.this,
                        android.R.layout.simple_list_item_1, lists);
                // 设置adapter
                listView.setAdapter(adapter);
                // 添加底部加载布局
                listView.addFooterView(view_more);
                // 设置监听
                setListeners();
                break;
            }
        };
    };
    private View view_more;
    private ProgressBar pb;
    private TextView tvLoad;
    private int lastVisibleIndex;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 控件初始化
        initViews();
        // 初始化数据
        initData();
    }

    private void initData() {
        // 模拟网络请求获取数据,一次获取15条
        new Thread() {
            public void run() {
                try {
                    totalCount = 100;// 假设数据一共有100条,将来调接口可以获取到这个值
                    for (int i = 0; i < 15; i++) {
                        lists.add("数据" + (i + 1));
                    }
                    // 给handler发消息更新UI,子线程不可以更新UI
                    Message message = new Message();
                    message.what = 0;
                    handler.sendMessage(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }

    private void setListeners() {
        if (totalCount > 15) {
            // listView设置滑动简监听
            listView.setOnScrollListener(this);
        } else {
            // 假如数据总数少于等于15条,直接移除底部的加载布局,不需要再加载更多的数据
            listView.removeFooterView(view_more);
        }
    }

    private void initViews() {
        listView = (ListView) findViewById(R.id.listView);
        // 构建底部加载布局
        view_more = (View) getLayoutInflater()
                .inflate(R.layout.view_more, null);
        // 进度条
        pb = (ProgressBar) view_more.findViewById(R.id.progressBar);
        // “正在加载...”文本控件
        tvLoad = (TextView) view_more.findViewById(R.id.tv_Load);
    }

    /**
     * 监听listView的滑动状态的改变
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        Log.e("TAG", "lastVisibleIndex = " + lastVisibleIndex);
        Log.e("TAG", "adapter.getCount() = " + adapter.getCount());
        // 滑到底部后自动加载,判断listView已经停止滚动并且最后可视的条目等于adapter的条目
        // 注意这里在listView设置好adpter后,加了一个底部加载布局。
        // 所以判断条件为:lastVisibleIndex == adapter.getCount()
        if (scrollState == SCROLL_STATE_IDLE
                && lastVisibleIndex == adapter.getCount()) {
            /**
             * 这里也要设置为可见,是因为当你真正从网络获取数据且获取失败的时候。
             * 我在失败的方法里面,隐藏了底部的加载布局并提示用户加载失败。所以再次监听的时候需要
             * 继续显示隐藏的控件。因为我模拟的获取数据,失败的情况这里不给出。实际中简单的加上几句代码就行了。
             */
            pb.setVisibility(View.VISIBLE);
            tvLoad.setVisibility(View.VISIBLE);
            loadMoreData();// 加载更多数据
        }
    }

    private void loadMoreData() {

    }

    /**
     * 监听listView的滑动
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        // 计算最后可见条目的索引
        lastVisibleIndex = firstVisibleItem + visibleItemCount - 1;
        // 当adapter中的所有条目数已经和要加载的数据总条数相等时,则移除底部的View
        if (totalItemCount == totalCount + 1) {
            // 移除底部的加载布局
            listView.removeFooterView(view_more);
        }

    }

}

以上代码就是我上面步骤2和步骤2中所说的东西。加上我写了注释,所以相信大家看起来很简单。关键点是怎么判断listView是否滑动到了底部并停止了滑动。我在代码中添加了两行log,可以打印日志信息。大家去自己调试就能明白我对关键点是怎么判断的了,但是也有聪明的!嘿嘿!大家都懂,自己人,我就不接话了。如图:

  • 最后就是加载更多的数据了,即完成第二步中未完成的方法:
loadMoreData();// 加载更多数据

具体代码实现为:

private void loadMoreData() {
        // 获取此时adapter中的总条目数
        int count = adapter.getCount();
        // 一次加载15条数据,即下拉加载的执行
        if (count + 15 < totalCount) {
            start = count;
            end = start + 15;
            initData(start, end);// 模拟网络获取数据操作
        } else {// 数据不足15条直接加载到结束
            start = count;
            end = totalCount;
            initData(start, end);// 模拟网络获取数据曹祖
            // 数据全部加载完成后,移除底部的view
            listView.removeFooterView(view_more);
            Toast.makeText(MainActivity.this, "数据已经全部加载", 1).show();
        }

    }

private void initData(final int start, final int end) {
        // 模拟网络请求获取数据,一次获取15条
        new Thread() {
            public void run() {
                try {
                    Thread.sleep(4000);// 模拟获取数据时的耗时3s
                    for (int i = start; i < end; i++) {
                        lists.add(i, "数据" + (i + 1));
                    }
                    // 给handler发消息更新UI,子线程不可以更新UI
                    Message message = new Message();
                    message.what = 1;
                    handler.sendMessage(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }

此时handler中还要再加一个case语句,用来刷新数据源,如图红色方框标注地方,大家自己加一下。最后我会给项目源码,大家可以下载看我的代码:

好了下面给出几张项目的展示图片:

  • 15条数据后加载更多数据,如图:

  • 30条数据后加载更多数据,如图:

  • 45条数据后加载更多数据,如图:

  • 数据全部加载完后,移除底部加载布局,如图:

谈谈我的感悟:你需要有自己的想法思路,别人的东西可以借鉴,但是你需要从中学到点什么。要是什么都没学到,那就失去了分享的意义了!分享是为了让大家学到更多,收获更多。希望我的这篇能够给大家一点点收获!

项目的源码下载地址:点击我下载项目源码

每天进步一点点!加油!

时间: 2024-10-12 08:34:21

Android中listView的下拉加载功能实现的相关文章

PullToRefreshRecyclerView——带上拉刷新下拉加载功能的RecyclerView

PullToRefreshRecyclerView——带上拉刷新下拉加载功能的RecyclerView

jquery实现下拉加载功能

<script> var str = ''; if(page=="") page=1; var stop=true;//触发开关,防止多次调用事件 $(window).scroll( function(event){ //当内容滚动到底部时加载新的内容 100当距离最底部100个像素时开始加载. if ($(this).scrollTop() + $(window).height() + 10 >= $(document).height() && $(

vue之better-scroll的封装,包含下拉刷新,上拉加载功能及UI(核心为借鉴,我仅仅是给轮子套上了外胎...)

先发原文作者.地址等信息.我把内容全部搬过来了,也可以去看原文.内容绝对是满满的干货,给原作者点赞!(我添加的内容在转载过来的后面,内容不多) 作者: ustbhuangyi 链接:http://www.imooc.com/article/18232 来源:慕课网 在我们日常的移动端项目开发中,处理滚动列表是再常见不过的需求了,以滴滴为例,可以是这样竖向滚动的列表,如图所示: 也可以是横向滚动的导航栏,如图所示: 可以打开"微信 -> 钱包->滴滴出行"体验效果. 我们在实

下拉刷新,上拉加载功能--dropload.js的使用

这段时间工作太忙了,没时间更新博客内容,在这段时间,也学习到了不少新的知识.今天先整理一下dropload.js的使用方法吧,这个是在为项目中使用过的插件,很好用,但是真正用到项目中还是会有一些小小的问题,这个要具体问题具体分析了. 插件下载地址:https://github.com/ximan/dropload.大家也可以直接看源码        我的代码地址: https://github.com/dreamITGirl/projectStudy,大家可以在里面查找. 首先,这个插件是依赖于

Android中自定义ListView实现上拉加载更多和下拉刷新

ListView是Android中一个功能强大而且很常用的控件,在很多App中都有ListView的下拉刷新数据和上拉加载更多这个功能.这里我就简单记录一下实现过程. 实现这个功能的方法不止一个,GitHub上有一些开源库可以使用,但是本着学习的精神,我做的是使用自定义ListView实现这个功能. 思路:谷歌提供的ListView是不能提供下拉刷新和下拉加载的,所以我们就需要重写ListView.在ListView的头部和尾部加上我们的布局文件(progressbar). 先说上拉加载更多实现

Android MVP设计框架模板 之 漂亮ListView上拉刷新下拉加载更多

mvp的全称为Model-View-Presenter,Model提供数据,View负责显示,Controller/Presenter负责逻辑的处理.MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller. 项目中大部分是面对接口编程,通过P层可以预先将所有需要的接口功能

Android之 RecyclerView,CardView 详解和相对应的上拉刷新下拉加载

随着 Google 推出了全新的设计语言 Material Design,还迎来了新的 Android 支持库 v7,其中就包含了 Material Design 设计语言中关于 Card 卡片概念的实现 -- CardView.RecyclerView也是谷歌V7包下新增的控件,用来替代ListView的使用,在RecyclerView标准化了ViewHolder类似于ListView中convertView用来做视图缓存. RecyclerView的优点就是,他可以通过设置LayoutMan

Windows Phone 8.1开发:如何让ListView下拉加载更多?

Windows Phone 8.1开发中使用ListView作为数据呈现载体时,经常需要一个下拉(拇指向上滑动)加载更多的交互操作.如何完成这一操作呢?下面为您阐述. 思路是这样的: 1.在ListView的loaded事件中,获取ListView中的ScrollView对象. 如何获得ScrollViewer,这篇文章说的很清楚! Windows Phone 8.1开发:如何从ListView中,获取ScrollViewer对象 2. 给ScrollViewer创建事件,上篇文章也说了. 3.

WP &amp; Win10开发:实现ListView下拉加载的两种方法

1.通过ListView控件的ContainerContentChanging方法.该方法在列表项被实例化时触发,在列表项最后一个项目实例化的时候触发刷新数据逻辑就可以实现下拉加载了. 代码如下://list_ContainerContentChanging在列表项被实例化是触发. private void list_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args) { if