无限滚动AdapterViews and RecyclerView

应用的一个共同的特点就是当用户欢动时自动加载更多的内容,这是通过用户滑动触发一定的阈值时发送数据请求实现的。

相同的是:信息实现滑动的效果需要定义在列表中最后一个可见项,和某些类型的阈值以便于开始在最后一项到达之前开始抓取数据,实现无限的滚动。

实现无限滚动的现象的重要之处就在于在用户滑动到最低端之前就行数据的获取,所以需要加上一个阈值来帮助实现获取数据的预期。

使用ListView和GridView实现

每个AdapterView 例如ListViewGridView 当用户开始进行滚动操作时候都会触发OnScrollListener .使用这个系统我们就可以定义一个基本的EndlessScrollListener ,通过创造继承OnScrollListener 的类来支持大多数情况下的使用。

package com.codepath.customadapter;

import android.widget.AbsListView;

/**
 * Created by Administrator on 2016/7/11.
 */
public abstract class EndlessScrollListener implements AbsListView.OnScrollListener {
    //在你滑动项下最少为多少时开始加载数据
    private int visibleThreshold = 5;
    //已经加载数据的当前页码
    private int currentPage = 0;
    //上一次加载数据后数据库的数据量
    private int previousTotalItemCount = 0;
    //我们是否在等待最后一组数据的加载
    private boolean loading = true;
    //设置开始页的下标
    private int startingPageIndex = 0;
    public EndlessScrollListener() {

    }
    public EndlessScrollListener(int visibleThreshold) {
        this.visibleThreshold = visibleThreshold;
    }
    public EndlessScrollListener(int visibleThreshold, int startingPageIndex) {
        this.visibleThreshold = visibleThreshold;
        this.startingPageIndex = startingPageIndex;
    }
    //这个方法可能会在滑动调用很多次,所以在设计时要保持谨慎
    //我们需要一些有用的参数来帮助我们,当我们需要加载更多数据的时候
    //但是我们首先要检查是否我们在等待先前的加载结束

//onScroll()当列表或网格视图被滚动后将会调用,参数一:报告状态的视图参数二:第一个可以看见的项的下标,参数三:可见项的数量参数四:listAdapter中所有的项数
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

        //如果总项数为0,而且先前没有项,那么这个列表是无效的应该被设定为初始状态
        if (totalItemCount < previousTotalItemCount) {
            this.currentPage = this.startingPageIndex;
            this.previousTotalItemCount = totalItemCount;
            if (totalItemCount == 0) {this.loading = true;}
        }
        //如果仍在加载中我们可以检查一下数据集合是否改变了,如果改变的话那就是已经完成了loading需要更新当前
        //页数和数据总量
        if (loading && (totalItemCount > previousTotalItemCount)) {
            loading = false;
            previousTotalItemCount = totalItemCount;
            currentPage++;
        }
        //如果当前没有加载,我们需要检查当前是否达到了阈值,如果是的话我们需要
        //加载更多的数据,执行onLoadMore
        if (!loading && (firstVisibleItem + visibleItemCount + visibleThreshold) >= totalItemCount) {
            loading = onLoadMore(currentPage + 1, totalItemCount);
        }
    }
    //定义实际加载数据的过程,如果数据加载完成返回false,如果正在加载返回true;
    public abstract boolean onLoadMore(int page, int totalItemCount);

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        //不采取动作
    }
}

要注意的是这是一个抽象的类,为了要使用这些,必须继承这个基本的类并且实现onLoadMore()方法实际的获取数据, 我们在一个活动中定义一个匿名的类来继承EndlessScrollListener然后将其连接AdapterView上

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstance) {
        //向平常一样
            ListView lvItems = (ListView) findViewById(R.id.lvItens);
            //将监听器绑定到上面
             lvItems.setOnScrollListener(new EndlessScrollListener() {
          @Override
          public boolean onLoadMore(int page, int totalItemsCount) {
              // 当新数据需要绑定到列表上的时候触发
              // 加载数据需要的代码Add whatever code is needed to append new items to your AdapterView
              customLoadMoreDataFromApi(page);
              // or customLoadMoreDataFromApi(totalItemsCount);
              return true; //数据加载中为true,不然为false; ONLY if more data is actually being loaded; false otherwise.
          }
        });
    }
    //加载更多的数据
    public void customLoadMoreDataFromApi(int offset) {
        //这个方法通常会发起一些网络请求,然后向适配器添加更多的数据
        //将偏移量数据作为参数附在请求里来获得一个数据的分页
        //解析API返回的值并且获得新的对象构建适配器
    }
}

现在当用户滑动并且触发阈值时会自动触发onloadMore() 方法,而且监听器给予了对于页数和数据总量的访问权限。

实现RecyclerView的无限滑动

我们可以使用相同的方法来定义一个接口EndlessRecyclerViewScrollListener 然后定义一个onLoadMore() 的方法来进行实现。由于LayoutManager负责项的生成和滑动的管理,我们需要一个LayoutManage的实例来收集必要的信息。

实现必要的分页需要这样的步骤:

1.直接复制EndlessRecyclerViewScrollListener.java

2.调用addOnScrollListener(...)RecyclerView 中来实现无限的分页,传递EndlessRecyclerViewScrollListener的实例来实现onLoadMore 方法来决定什么时候来加载新的数据

3.在onLoadMore 方法中通过发送网络请求或者从源数据加载来获得更多item。

 protected void onCreate(Bundle savedInstanceState) {
       // Configure the RecyclerView获得RecylerView的实例
       RecyclerView rvItems = (RecyclerView) findViewById(R.id.rvContacts);
       LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
       recyclerView.setLayoutManager(linearLayoutManager);
       // Add the scroll listener
       rvItems.addOnScrollListener(new EndlessRecyclerViewScrollListener(linearLayoutManager) {
           @Override
           public void onLoadMore(int page, int totalItemsCount) {
               // Triggered only when new data needs to be appended to the list
               // Add whatever code is needed to append new items to the bottom of the list
               customLoadMoreDataFromApi(page);
           }
      });
  }

  // Append more data into the adapter
  // This method probably sends out a network request and appends new data items to your adapter.
  public void customLoadMoreDataFromApi(int page) {
      // Send an API request to retrieve appropriate data using the offset value as a parameter.
      //  --> Deserialize API response and then construct new objects to append to the adapter
      //  --> Notify the adapter of the changes
  }
}

EndlessRecyclerView

public abstract class EndlessRecyclerViewScrollListener extends RecyclerView.OnScrollListener {
    // The minimum amount of items to have below your current scroll position
    // before loading more.
    private int visibleThreshold = 5;
    // The current offset index of data you have loaded
    private int currentPage = 0;
    // The total number of items in the dataset after the last load
    private int previousTotalItemCount = 0;
    // True if we are still waiting for the last set of data to load.
    private boolean loading = true;
    // Sets the starting page index
    private int startingPageIndex = 0;

    RecyclerView.LayoutManager mLayoutManager;

    public EndlessRecyclerViewScrollListener(LinearLayoutManager layoutManager) {
        this.mLayoutManager = layoutManager;
    }

    public EndlessRecyclerViewScrollListener(GridLayoutManager layoutManager) {
        this.mLayoutManager = layoutManager;
        visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
    }

    public EndlessRecyclerViewScrollListener(StaggeredGridLayoutManager layoutManager) {
        this.mLayoutManager = layoutManager;
        visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
    }

    public int getLastVisibleItem(int[] lastVisibleItemPositions) {
        int maxSize = 0;
        for (int i = 0; i < lastVisibleItemPositions.length; i++) {
            if (i == 0) {
                maxSize = lastVisibleItemPositions[i];
            }
            else if (lastVisibleItemPositions[i] > maxSize) {
                maxSize = lastVisibleItemPositions[i];
            }
        }
        return maxSize;
    }

    // This happens many times a second during a scroll, so be wary of the code you place here.
    // We are given a few useful parameters to help us work out if we need to load some more data,
    // but first we check if we are waiting for the previous load to finish.
    @Override
    public void onScrolled(RecyclerView view, int dx, int dy) {
        int lastVisibleItemPosition = 0;
        int totalItemCount = mLayoutManager.getItemCount();

        if (mLayoutManager instanceof StaggeredGridLayoutManager) {
            int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null);
            // get maximum element within the list
            lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions);
        } else if (mLayoutManager instanceof LinearLayoutManager) {
            lastVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
        } else if (mLayoutManager instanceof GridLayoutManager) {
            lastVisibleItemPosition = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition();
        }

        // If the total item count is zero and the previous isn‘t, assume the
        // list is invalidated and should be reset back to initial state
        if (totalItemCount < previousTotalItemCount) {
            this.currentPage = this.startingPageIndex;
            this.previousTotalItemCount = totalItemCount;
            if (totalItemCount == 0) {
                this.loading = true;
            }
        }
        // If it’s still loading, we check to see if the dataset count has
        // changed, if so we conclude it has finished loading and update the current page
        // number and total item count.
        if (loading && (totalItemCount > previousTotalItemCount)) {
            loading = false;
            previousTotalItemCount = totalItemCount;
        }

        // If it isn’t currently loading, we check to see if we have breached
        // the visibleThreshold and need to reload more data.
        // If we do need to reload some more data, we execute onLoadMore to fetch the data.
        // threshold should reflect how many total columns there are too
        if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) {
            currentPage++;
            onLoadMore(currentPage, totalItemCount);
            loading = true;
        }
    }

    // Defines the process for actually loading more data based on page
    public abstract void onLoadMore(int page, int totalItemsCount);

}

注意问题

  1. 对于ListView,确定将绑定监听器的步骤放在onCreate()的方法中
  2. 为可加可靠的进行分页,需要确定在向列表中添加新数据的时候先清理适配器中的数据

    对于RecyclerView来说在通知适配器时推荐更细致的更新。

  3. 对于RecyclerView来说确保在清除列表中的数据的时候迅速的通知适配器内容更新了,以便于可以触发新的onScroll事件,重置自己

展示进度条

为了在底部展示进度条证明ListView正在加载。我们可以在Adapter中进行设置,我们可以定义两类,可以是进度条类型或者是文本表明达到了最底行,参考:http://guides.codepath.com/android/Endless-Scrolling-with-AdapterViews-and-RecyclerView

时间: 2024-08-22 23:47:43

无限滚动AdapterViews and RecyclerView的相关文章

LoopBar: Tap酒吧与无限滚动

相约工具栏 - 标签栏与无限滚动为Android由Cleveroad 在Cleveroad我们最近认识到通过使用任何一个应用程序类别的导航,导航面板是很无聊和琐碎.这就是为什么我们的设计师的创意武装,我们向您介绍了基于Android的应用,我们的新组件 - LoopBar.当时的想法是让导航菜单就在指纹,在标签栏.更重要的是认为有一些特定的功能,使其从类似的人群中脱颖而出.因此,尝试在你的应用程序的LoopBar库,你会看到其中的差别. 如果你努力创造不寻常的外观和导航的应用程序,欢迎你使用循环

利用递归 实现UIScrollView无限滚动的效果

项目需求 利用递归 实现UIScrollView无限滚动的效果. 上机试题, #import "ViewController.h" @interface ViewController (){ UIScrollView *mainScroll; BOOL isFinish; int x; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; x=0; isFinish = YES;

用原生的javascript 实现一个无限滚动的轮播图

说一下思路:和我上一篇博客中用JQ去写的轮播图有相同点和不同点 相同点: 首先页面布局是一样的 同样是改变.inner盒子的位置去显示不同的图片 不同点: 为了实现无限滚动需要多添加两张重复的图片 左右切换和前面的方法有所不同,前面是获取当前的索引值乘以-600px当做位移距离,现在是需要获取当前.inner的位置来加上或者减去-600来实现 下面来一步步的去实现轮播图: 首先是html <!DOCTYPE html> <html lang="en"> <

Android 高级UI设计笔记09:Android如何实现无限滚动列表

ListView和GridView已经成为原生的Android应用实现中两个最流行的设计模式.目前,这些模式被大量的开发者使用,主要是因为他们是简单而直接的实现,同时他们提供了一个良好,整洁的用户体验. 对于ListView和GridView一个共同基本要求是:当用户向下滚动时可以动态加载数据支持无限滚动.下面教你如何在自己的应用中实现这个特性. 具体流程如下: (1)我们需要的一个主要组件是InfiniteScrollListener类,它实现了OnScrollListener接口.让我们直接

基于HTML5+CSS3的图片旋转、无限滚动、文字跳动特效

本文分享几种基于HTML5+CSS3实现的一些动画特效:图片旋转.无限滚动.文字跳动;实现起来均比较容易,动手来试试! 一.图片旋转 效果图如下: 这个效果实现起来其实并不困难.代码清单如下: <style type="text/css"> #liu{ width:280px; height: 279px; background: url(shishi.png) no-repeat; border-radius:140px; -webkit-animation:run 6s

Cocos2d-x 《雷电大战》-双层地图无限滚动

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 本文要实现飞机射击游戏中的地图无限滚动的功能,这里分为两个层,一个层无限向下滚动,一个层无限向上滚动,这样子结合起来效果就非常有层次感,也非常逼真,这里我把地图层都写成一个类,自己把地图改下,就可以成为你自己的了!下面,我们开始吧 先来看看效果: Cocos2d-x版本:3.4 工程环境:VS30213 一.实现思路 其实就是两张图片,然后同时一起向下(向上)滚动,当一张图片完全出视野后,就

masonry结合json 制作无限滚动的瀑布流

做前端这行的 能直接贴代码就直接贴代码了,不用多说什么别的 效果需要引入jquery和jquery.masonry.min.js这两个JS JS代码如下: $(document).ready(function(){ var $container = $('.space_main_con1 .con'); var animated; var i=1; //判断是否需要 masonry.js这个里面提供动画 如果是IE浏览器并且版本低于9.0 则添加动画 否则就用 css3的动画(css3的动画代码

ios开发——实用技术篇&amp;图片无限滚动和自动滚动

初始化项目 1:自定义一个集成自Collection的Cell .h 1 #import <UIKit/UIKit.h> 2 @class iCocosNews; 3 @interface iCocosNewsCell : UICollectionViewCell 4 @property (nonatomic, strong) iCocosNews *news; 5 @end .m 1 #import "iCocosNewsCell.h" 2 #import "i

Cocos2d-x《雷电大战》-双层地图无限滚动

本文要实现飞机射击游戏中的地图无限滚动的功能,这里分为两个层,一个层无限向下滚动,一个层无限向上滚动,这样子结合起来效果就非常有层次感,也非常逼真,这里我把地图层都写成一个类,自己把地图改下,就可以成为你自己的了!下面,我们开始吧 先来看看效果: Cocos2d-x版本:3.4 工程环境:VS30213 一.实现思路 其实就是两张图片,然后同时一起向下(向上)滚动,当一张图片完全出视野后,就把它调到最上面.形成两个图片交替出现,不过,一般为游戏中我们都感觉像是一张图片,那是因为两张图片的头尾连接