Android实现RecyclerView的下拉刷新和上拉载入很多其它

需求

先上效果图, Material Design风格的下拉刷新和上拉载入很多其它。

源代码地址(欢迎star) https://github.com/studychen/SeeNewsV2

假设对于RecyclerView还不熟悉,參见这篇 Android Material Design学习之RecyclerView取代 ListView

本文链接 http://blog.csdn.net/never_cxb/article/details/50759109 转载请注明出处

下拉刷新

效果图

上拉时候会有一个圆形动画。刷新载入数据。

思路

使用Google官方的android.support.v4.widget.SwipeRefreshLayout

列表RecyclerView的xml布局

给原来的RecyclerView添加一个SwipeRefreshLayout的父布局

<?xml version="1.0" encoding="utf-8"?

>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?

attr/listBackground"
    android:orientation="vertical">
    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swiperefreshlayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <!-- 新闻列表展示-->
        <android.support.v7.widget.RecyclerView
            android:id="@+id/rcv_article_origin"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>

java代码

这儿有几个注意点:

setColorSchemeColors()能够控制圆形动画的颜色,最多设置4个。

setOnRefreshListener 设置下拉刷新的回调事件。

下拉刷新后。使用 AsyncTask 依据当前RecyclerView中首个Item的id来载入很多其它数据。

数据载入完成后,使用setRefreshing(false);取消动画。

假设刷新后得到0条记录,提示没有数据更新。若得到>0条数据。把数据加到RecyclerView中

mSwipeRefreshLayout.setColorSchemeColors(Color.RED, Color.BLUE);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    @Override
    public void onRefresh() {
        new MoreArticleTask().execute(mAdapter.getTopArticleId());
    }
});

// Integer 是输入參数
// 得到比某个id大的新闻数组
class MoreArticleTask extends AsyncTask<Integer, Void, List<SimpleArticleItem>> {
    @Override
    protected List<SimpleArticleItem> doInBackground(Integer... params) {
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getMoreById(mColumn, params[0]);
    }

    @Override
    protected void onPostExecute(List<SimpleArticleItem> simpleArticleItems) {
        super.onPostExecute(simpleArticleItems);

        if (mSwipeRefreshLayout != null) {
            mSwipeRefreshLayout.setRefreshing(false);
        }
        //没有新的数据。提示消息
        if (simpleArticleItems == null || simpleArticleItems.size() == 0) {
            Snackbar.with(mActivity.getApplicationContext()) // context
                    .text(mActivity.getResources().getString(R.string.list_more_data)) // text to display
                    .duration(Snackbar.SnackbarDuration.LENGTH_SHORT) // make it shorter
                    .show(mActivity); // activity where it is displayed
        } else {
            mArticleList.addAll(simpleArticleItems);
            mAdapter.notifyDataSetChanged();
        }
    }

}

首次进入页面就显示载入ing的动画

效果例如以下:

直接使用 mSwipeRefreshLayout.setRefreshing(true);载入动画初始状态并不显示。

看了 http://stackoverflow.com/questions/26858692/swiperefreshlayout-setrefreshing-not-showing-indicator-initially 的解答。

改用以下的代码,初始状态就有载入动画显示。

mSwipeRefreshLayout.post(new Runnable() {
    @Override
    public void run() {
        mSwipeRefreshLayout.setRefreshing(true);
        new MoreArticleTask().execute(mAdapter.getTopArticleId());
    }
});

上拉载入很多其它

RecyclerView 展示列表是个通用需求。那么当数据较多时候。怎样实现分页载入呢?

比方笔者的项目中,先载入15条新闻,当滑动究竟部时候,再载入15条。

效果图

思路

在RecyclerView底部添加一个Footer的ViewHolder。数据载入完成取出底部的ViewHolder。

底部Footer的xml布局文件

非常easy,就是一个ProgressBar。本项目为了Material Design。使用了一个开源ProgressBar https://github.com/Todd-Davies/ProgressWheel,你也能够使用原生的ProgressBar。

<?

xml version="1.0" encoding="utf-8"?><!--上拉载入很多其它 RecyclerView 底部-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:wheel="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <com.pnikosis.materialishprogress.ProgressWheel
        android:id="@+id/rcv_load_more"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_horizontal"
        wheel:matProg_barColor="@color/accent"
        wheel:matProg_progressIndeterminate="true" />
</LinearLayout>

底部Footer的ViewHolder

/**
 * 底部载入很多其它
 */
class FooterViewHolder extends RecyclerView.ViewHolder {
    @InjectView(R.id.rcv_load_more)
    ProgressWheel rcvLoadMore;

    public FooterViewHolder(View itemView) {
        super(itemView);
        ButterKnife.inject(this, itemView);
    }
}

在Fragment中给RecyclerView添加滑动监听

layoutManager.getItemCount() 能够得到当前RecyclerView中Item的总数目

layoutManager.findLastVisibleItemPosition() 得到最后一个可见的Item的位置position

假设 totalItemCount < (lastVisibleItem + Constant.VISIBLE_THRESHOLD)

比方一共15个Item,当前到达第13个, Constant.VISIBLE_THRESHOLD设为3

总数小于最后一个+阈值,就载入很多其它新闻数据。同一时候把loading标记为 true 。

private boolean loading = false;

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    mRecyclerView.setLayoutManager(new LinearLayoutManager(mActivity));

    mAdapter = new OriginArticleAdapter(mActivity, mArticleList);

    mRecyclerView.setAdapter(mAdapter);

    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

            super.onScrolled(recyclerView, dx, dy);

            LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();

            int totalItemCount = layoutManager.getItemCount();

            int lastVisibleItem = layoutManager.findLastVisibleItemPosition();

            if (!loading && totalItemCount < (lastVisibleItem + Constant.VISIBLE_THRESHOLD)) {
                new ArticleTask(mActivity).execute(mAdapter.getBottomArticleId());
                loading = true;
            }
        }
    });
}

在Fragment中控制底部Footer

我们依旧使用AsyncTask。

注意 onPreExecute() 给 mArticleList 添加了一个null标记Footer。假设是第一次进入页面(mArticleList为空)不须要加Footer。

当数据载入完成后。用 mArticleList.remove(mArticleList.size() - 1);把最以下的Footer删除。

再用 mArticleList.addAll(moreArticles); 添加新增的新闻数据。

并用 mAdapter.notifyDataSetChanged(); 通知 RecyclerView.Adapter 有数据改变。

private List<SimpleArticleItem> mArticleList = new ArrayList<SimpleArticleItem>();

class ArticleTask extends AsyncTask<Integer, Void, List<SimpleArticleItem>> {

    private Context mContext;

    public ArticleTask(Context context) {
        mContext = context;
    }

    /**
     * Runs on the UI thread before {@link #doInBackground}.
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        if (mArticleList != null && mArticleList.size() > 0) {
            mArticleList.add(null);

            // notifyItemInserted(int position)。这种方法是在第position位置
            // 被插入了一条数据的时候能够使用这种方法刷新,
            // 注意这种方法调用后会有插入的动画,这个动画能够使用默认的,也能够自定义。

mAdapter.notifyItemInserted(mArticleList.size() - 1);
        }
    }

    /**
     * @param params 偏移量 aid
     * @return
     */
    @Override
    protected List<SimpleArticleItem> doInBackground(Integer... params) {
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getArticleList(mColumn, params[0]);
    }

    @Override
    protected void onPostExecute(final List<SimpleArticleItem> moreArticles) {
        // 新增新闻数据
        super.onPostExecute(moreArticles);
        if (mArticleList.size() == 0) {
            mArticleList.addAll(moreArticles);
            mAdapter.notifyDataSetChanged();
        } else {
            //删除 footer
            mArticleList.remove(mArticleList.size() - 1);
            mArticleList.addAll(moreArticles);
            mAdapter.notifyDataSetChanged();
            loading = false;
        }
    }
}

Override RecyclerView.Adapter 中的 getItemViewType

extends RecyclerView.Adapter<RecyclerView.ViewHolder> 假设是null,返回Footer的Type;否则。返回正常新闻的Type。

本文,为了UI美观,把新闻分为两种:大于3幅图片 、小于3幅图片,对应返回不同的Type和ViewHolder。

public final static int TYPE_MULTI_IMAGES = 2; // 多个图片的文章
public final static int TYPE_FOOTER = 3;//底部--往往是loading_more
public final static int TYPE_NORMAL = 1; // 正常的一条文章
@Override
public int getItemViewType(int position) {

    SimpleArticleItem article = articleList.get(position);
    if (article == null) {
        return TYPE_FOOTER;
    } else if (article.getImageUrls().length >= 3) {
        return TYPE_MULTI_IMAGES;
    } else {
        return TYPE_NORMAL;
    }

}

Override RecyclerView.Adapter 中的 onCreateViewHolder

在该方法中利用 switch (viewType) 返回不同的xml布局文件及ViewHolder

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    RecyclerView.ViewHolder vh;
    View view;
    switch (viewType) {
        //其它无法处理的情况使用viewholder_article_simple
        default:
        case TYPE_NORMAL:
            view = mLayoutInflater.inflate(
                    R.layout.item_article_normal, parent, false);
            vh = new ItemArticleViewHolder(view);
            return vh;
        case TYPE_FOOTER:
            view = mLayoutInflater.inflate(
                    R.layout.recyclerview_footer, parent, false);
            vh = new FooterViewHolder(view);
            return vh;
        case TYPE_MULTI_IMAGES:
            view = mLayoutInflater.inflate(
                    R.layout.item_article_multi_images, parent, false);
            vh = new MultiImagesViewHolder(view);
            return vh;
    }
}

Override RecyclerView.Adapter 中的 onBindViewHolder

利用 instanceof 推断是何种类型的ViewHolder。来给控件赋值、绑定数据。

注意:由于Footer是用null表示的,为了防止NullPointerException,

我们先用if把FooterViewHolder的情况处理了。

@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {

    //这时候 article是 null。先把 footer 处理了
    if (holder instanceof FooterViewHolder) {
        ((FooterViewHolder) holder).rcvLoadMore.spin();
        return;
    }

    SimpleArticleItem article = articleList.get(position);
    String[] imageUrls = article.getImageUrls();

    if (holder instanceof ItemArticleViewHolder) {
        ItemArticleViewHolder newHolder = (ItemArticleViewHolder) holder;
        newHolder.rcvArticleTitle.setText(article.getTitle());
        newHolder.rcvArticleDate.setText(article.getPublishDate());
        //当图片小于3张时候 选取第1张图片
        if (imageUrls.length > 0) {
            newHolder.rcvArticlePhoto.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME
                    + imageUrls[0]));
        } else {
            newHolder.rcvArticlePhoto.setImageURI(Uri.parse(ApiUrl.randomImageUrl(article.getId())));
        }
        //注意这个阅读次数是 int 类型。须要转化为 String 类型
        newHolder.rcvArticleReadtimes.setText("浏览: " + article.getReadTimes());

        newHolder.rcvArticleSummary.setText(article.getSummary());
    } else {
        MultiImagesViewHolder newHolder = (MultiImagesViewHolder) holder;
        newHolder.articleTitle.setText(article.getTitle());
        newHolder.articlePic1.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME + imageUrls[0]));
        newHolder.articlePic2.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME + imageUrls[1]));
        newHolder.articlePic3.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME + imageUrls[2]));
        newHolder.countPics.setText("图片: " + imageUrls.length);
        newHolder.countRead.setText("浏览: " + article.getReadTimes());
    }
}

下拉刷新和上拉载入很多其它的源代码地址(欢迎star) https://github.com/studychen/SeeNewsV2

本文链接 http://blog.csdn.net/never_cxb/article/details/50759109 转载请注明出处

一些坑

注意给RecyclerView setLayoutManager,不然RecyclerView可能不显示

// 1. get a reference to recyclerView
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);

// 2. set layoutManger
recyclerView.setLayoutManager(new LinearLayoutManager(this));

參考文章

时间: 2024-12-21 01:43:41

Android实现RecyclerView的下拉刷新和上拉载入很多其它的相关文章

Android 5.X新特性之为RecyclerView添加下拉刷新和上拉加载及SwipeRefreshLayout实现原理

RecyclerView已经写过两篇文章了,分别是Android 5.X新特性之RecyclerView基本解析及无限复用 和 Android 5.X新特性之为RecyclerView添加HeaderView和FooterView,既然来到这里还没学习的,先去学习下吧. 今天我们的主题是学习为RecyclerView添加下拉刷新和上拉加载功能. 首先,我们先来学习下拉刷新,google公司已经为我们提供的一个很好的包装类,那就是SwipeRefreshLayout,这个类可以支持我们向下滑动并进

Android实现RecyclerView的下拉刷新和上拉加载更多

Android实现RecyclerView的下拉刷新和上拉加载更多 原文地址:https://www.cnblogs.com/zhujiabin/p/8206176.html

RecyclerView 下拉刷新和上拉加载

RecyclerView 下拉刷新和上拉加载 RecyclerView已经出来很久了,许许多多的项目都开始从ListView转战RecyclerView,那么,上拉加载和下拉刷新是一件很有必要的事情. 在ListView上,我们可以通过自己添加addHeadView和addFootView去添加头布局和底部局实现自定义的上拉和下拉,或者使用一些第三方库来简单的集成,例如android-pulltorefresh或者android-Ultra-Pull-to-Refresh,后者的自定义更强,但需

Android 使用PullToRefresh实现下拉刷新和上拉加载(ExpandableListView)

PullToRefresh是一套实现非常好的下拉刷新库,它支持: 1.ListView 2.ExpandableListView 3.GridView 4.WebView 等多种常用的需要刷新的View类型,而且使用起来也十分方便. (下载地址:https://github.com/chrisbanes/Android-PullToRefresh) 下载完成,将它导入到eclipse中,作为一个library导入到你的工程中就好了. 一.废话少说,下拉刷新Go. 1.在你的布局文件中加上你想用的

自定义下拉刷新和上拉加载的recycleview

自定义下拉刷新和上拉加载的recycleview 先看一下效果(因为模拟器的原因,看不到进度条,大家可以下载demo,进行查看) demo地址 首先看我们自定义的HeadView package yuan.kuo.yu.view; import android.animation.ValueAnimator; import android.content.Context; import android.os.Handler; import android.util.AttributeSet; i

使用PullToRefresh实现下拉刷新和上拉加载

使用PullToRefresh实现下拉刷新和上拉加载 分类: Android2013-12-20 15:51 78158人阅读 评论(91) 收藏 举报 Android下拉刷新上拉加载PullToRefresh PullToRefresh是一套实现非常好的下拉刷新库,它支持: 1.ListView 2.ExpandableListView 3.GridView 4.WebView 等多种常用的需要刷新的View类型,而且使用起来也十分方便. (下载地址:https://github.com/ch

安卓开发笔记——关于开源组件PullToRefresh实现下拉刷新和上拉加载(一分钟搞定,超级简单)

前言 以前在实现ListView下拉刷新和上拉加载数据的时候都是去继承原生的ListView重写它的一些方法,实现起来非常繁杂,需要我们自己去给ListView定制下拉刷新和上拉加载的布局文件,然后添加ScrollView和OnTouch监听,设置回调接口获取数据,为了更好的交互体验,我们还不能直接利用setVisibility隐藏显示布局等等一大堆的操作,累都累死了.(题外话:关于下拉刷新在新版的android-support-v4.jar里,其实谷歌已经为我们提供了一个控件叫SwipeRef

IOS 开发下拉刷新和上拉加载更多

IOS 开发下拉刷新和上拉加载更多 简介 1.常用的下拉刷新的实现方式 (1)UIRefreshControl (2)EGOTTableViewrefresh (3)AH3DPullRefresh (4)MJRefresh (5)自己实现 2.AH3DPullRefresh实现下拉刷新和上拉下载的步骤 添加UIScrollView+AH3DPullRefresh.h 和UIScrollView+AH3DPullRefresh.m两个文件,由此可知,它是基于UIScrollView的方法. 在bu

iOS学习之路--下拉刷新和上拉加载更多

iOS学习之路--下拉刷新和上拉加载更多 简介 本文中笔者将和大家分享应用app中常用到的表单内容的下拉刷新和上拉加载更多的功能实现的方法. 内容 1.有哪些实现方法与各方法的优劣 使用过美团,大众点评的朋友们应该有注意到,当你向上滑动表单的时候会有更多的店铺加载进你的表单中,而当你下滑表单顶的时候,则会刷新表单的内容并从网络上获取最新的信息.通过下拉刷新和上拉加载更多的功能,使app可以获取更多用户想要的信息和获取最新的信息.那么这种功能如何实现呢,还请跟着笔者继续往下看. 目前来说,主要的实