WanAndroid实战——刷新加载

前情回顾:

1.WanAndroid实战——首页Banner

2.WanAndroid实战——首页文章

3.WanAndroid实战——内容显示

在完成了上面的一系列操作之后终于可以看到文章的内容了,但是却只能看到“最近”的内容,无法刷新,也无法看到之前的内容,这篇文章将解决这个问题。按照惯例,先上效果图:

完成效果.gif

下拉刷新,上拉加载

目前周围的同事在做有关刷新加载的功能都推荐使用开源框架SmartRefreshLayout,实现起来简单,效果也是很炫酷,我这里使用的是经典的形式。

1.添加依赖,在gradle里面添加如下内容

    implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0-alpha-21'
    implementation 'com.scwang.smartrefresh:SmartRefreshHeader:1.1.0-alpha-21'

2.在布局文件中添加进去,因为我们要刷新的是recycleView,所以在它外面加。

    

        

        

        
    

ClassicsHeader和ClassicsFooter是经典的头部和尾部,如果想使用默认的效果,那么这两个可以不写。

3.接口中添加加载,刷新的相关内容

Contract.java这个类中,添加IBaseView,这里面用来定义错误和完成的接口,同时让IMainView继承IBaseView,通过监听这两种状态,来完成相关的操作,例如隐藏刷新/加载的Header和Footer。新增了刷新的接口。

package com.tom.wanandroid.contract;

import com.tom.wanandroid.bean.ArticleBean;
import com.tom.wanandroid.bean.BannerBean;

import java.util.List;

import io.reactivex.Observable;

/**
 * 

Title: Contract

*

Description:

* * @author tom * @date 2019/3/7 10:13 **/ public class Contract { public interface IBaseView { /** *

加载错误回调

* @param e Throwable */ void onError(Throwable e); /** *

加载完成

*/ void onComplete(); } public interface IMainModel { /** *

获取banner数据

* @return banner数据 */ Observable loadBanner(); /** *

加载首页文章

* @param number 页码 * @return 首页文章 */ Observable> loadArticle(int number); /** *

刷新首页文章

* @return 首页文章 */ Observable> refreshArticle(); } public interface IMainView extends IBaseView{ /** *

View 获取到数据后进行显示

* @param bean banner的数据 */ void loadBanner(BannerBean bean); /** *

加载首页文章信息

* @param bean 首页文章数据 */ void loadArticle(List bean); /** *

刷新首页文章

* * @param bean */ void refreshArticle(List bean); } public interface IMainPresenter{ /** *

首页banner

*/ void loadBanner(); /** *

加载首页文章

* @param number 页码 */ void loadArticle(int number); /** *

刷新首页文章

*/ void refreshArticle(); } }

4.处理报错

因为接口新增,所有实现这些接口的类都会报错,接下来就是处理各种报错就好了,这里以Model为例,其它的都比较简单。

首先将从网站获取的json数据对应的bean更名为ArticleBeanOriginal,然后新建了一个ArticleBean,这里仅保存用到的属性,后续如果有需要添加的地方,可以直接进行修改。

package com.tom.wanandroid.bean;

/**
 * 

Title: ArticleBean

*

Description:处理后的文章bean

* * @author tom * @date 2019/3/29 13:39 **/ public class ArticleBean { public int id; public String title; public String author; public String niceDate; public long publishTime; public String chapterName; public String superChapterName; public boolean collect; public String link; @Override public String toString() { return "ArticleBean{" + "id=" + id + ", title='" + title + '\'' + ", author='" + author + '\'' + ", niceDate='" + niceDate + '\'' + ", publishTime=" + publishTime + ", chapterName='" + chapterName + '\'' + ", superChapterName='" + superChapterName + '\'' + ", collect=" + collect + ", link='" + link + '\'' + '}'; } }

然后在IRetrofitData.java中增加刷新的方法,刷新固定获取第0页的数据,因此不需要传递参数。添加后的文件内容如下:

package com.tom.wanandroid.retrofit;

import com.tom.wanandroid.bean.BannerBean;
import com.tom.wanandroid.bean.ArticleBeanOriginal;

import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.Path;

/**
 * 

Title: IRetrofitData

*

Description:

* * @author tom * @date 2019/3/7 11:34 **/ public interface IRetrofitData { /** *

获取首页轮播图数据

* @return 首页轮播图数据 */ @GET("banner/json") Observable loadBanner(); /** *

获取首页文章数据

* @param number 页码 * @return 返回首页文章 */ @GET("article/list/{number}/json") Observable loadMainArticle(@Path("number") int number); /** *

刷新首页文章数据

* @return 返回首页文章 */ @GET("article/list/0/json") Observable refreshMainArticle(); }

接着修改MainModel,刷新时固定获取第0页数据,如果数据相同,则不处理,如果不相同,则放置到已有数据的上面,因此需要对获取到的数据进行倒叙排序,即让获取到的数据为按照发表时间倒叙排序,这样,在调用了mArticleBeans.add(0, articleBean);方法后,能够保证文章的数据的顺序是正确的。修改后的文件如下。

package com.tom.wanandroid.model;

import com.tom.wanandroid.bean.ArticleBean;
import com.tom.wanandroid.bean.BannerBean;
import com.tom.wanandroid.contract.Contract;
import com.tom.wanandroid.retrofit.IRetrofitData;

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

import io.reactivex.Observable;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * 

Title: MainModel

*

Description:

* * @author tom * @date 2019/3/7 10:54 **/ public class MainModel implements Contract.IMainModel { private static final String BASE_URL = "http://www.wanandroid.com"; List mArticleBeans = new ArrayList<>(); @Override public Observable loadBanner() { IRetrofitData retrofitData = getIRetrofitData(); return retrofitData.loadBanner(); } @Override public Observable> loadArticle(int number) { return getIRetrofitData().loadMainArticle(number).filter(a ->a.getErrorCode() == 0) .map(original -> { original.getData().getDatas().stream().sorted((o1,o2) -> (int)(o2.getPublishTime() - o1.getPublishTime())) .forEach(datas -> { long count = mArticleBeans.stream().filter(b -> b.id == datas.getId()).count(); if (count <= 0) { ArticleBean articleBean = new ArticleBean(); articleBean.id = datas.getId(); articleBean.title = datas.getTitle(); articleBean.author = datas.getAuthor(); articleBean.niceDate = datas.getNiceDate(); articleBean.publishTime = datas.getPublishTime(); articleBean.chapterName = datas.getChapterName(); articleBean.superChapterName = datas.getSuperChapterName(); articleBean.collect = datas.isCollect(); articleBean.link = datas.getLink(); mArticleBeans.add(articleBean); } }); return mArticleBeans; }); } @Override public Observable> refreshArticle() { return getIRetrofitData().refreshMainArticle().filter(a ->a.getErrorCode() == 0) .map(original -> { original.getData().getDatas().stream().sorted((o1, o2) -> (int) (o1.getPublishTime() - o2.getPublishTime())) .forEach(datas -> { //如果数据是新数据 long count = mArticleBeans.stream().filter(b -> b.id == datas.getId()).count(); if (count <= 0) { ArticleBean articleBean = new ArticleBean(); articleBean.id = datas.getId(); articleBean.title = datas.getTitle(); articleBean.author = datas.getAuthor(); articleBean.niceDate = datas.getNiceDate(); articleBean.publishTime = datas.getPublishTime(); articleBean.chapterName = datas.getChapterName(); articleBean.superChapterName = datas.getSuperChapterName(); articleBean.collect = datas.isCollect(); articleBean.link = datas.getLink(); mArticleBeans.add(0, articleBean); } }); return mArticleBeans; }); } private IRetrofitData getIRetrofitData() { Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); return retrofit.create(IRetrofitData.class); } }

顺带一句,在MainPresenter中,refreshArticle()方法如下,loadArticle同样修改即可。

    @Override
    public void refreshArticle() {
        mModel.refreshArticle()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        mCompositeDisposable.add(d);
                    }

                    @Override
                    public void onNext(List articleBeans) {

                        if (isViewAttached()) {
                            getView().refreshArticle(articleBeans);
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        e.printStackTrace();
                        if (isViewAttached()) {
                            getView().onError(e);
                        }

                    }

                    @Override
                    public void onComplete() {
                        if (isViewAttached()) {
                            getView().onComplete();
                        }

                    }
                });

    }

5.在View中调用刷新和加载方法

在View中,设置加载和刷新的监听,分别实现监听的方法。

        mRefreshLayout.setOnLoadMoreListener(this);
        mRefreshLayout.setOnRefreshListener(this);
        
    @Override
    public void onLoadMore(@NonNull RefreshLayout refreshLayout) {

        mCurPage++;
        mPresenter.loadArticle(mCurPage);

    }

    @Override
    public void onRefresh(@NonNull RefreshLayout refreshLayout) {
        mCurPage = 0;
        mPresenter.refreshArticle();
    }

还要考虑加载或者刷新失败时,需要将头部和尾部隐藏掉,整个MainActivity的代码如下。

package com.tom.wanandroid.view;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.ToastUtils;
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
import com.scwang.smartrefresh.layout.constant.RefreshState;
import com.tom.wanandroid.Constant;
import com.tom.wanandroid.R;
import com.tom.wanandroid.adapter.ArticleAdapter;
import com.tom.wanandroid.base.BaseActivity;
import com.tom.wanandroid.bean.ArticleBean;
import com.tom.wanandroid.bean.BannerBean;
import com.tom.wanandroid.contract.Contract;
import com.tom.wanandroid.presenter.MainPresenter;
import com.tom.wanandroid.utils.GlideImageLoader;
import com.tom.wanandroid.utils.Utils;
import com.youth.banner.Banner;
import com.youth.banner.BannerConfig;
import com.youth.banner.listener.OnBannerListener;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import butterknife.BindView;

/**
 * 

Title: MainActivity

*

Description: 主页activity

* * @author tom * @date 2019/3/9 10:15 */ public class MainActivity extends BaseActivity implements Contract.IMainView, OnBannerListener, ArticleAdapter.OnItemClickListener, com.scwang.smartrefresh.layout.listener.OnLoadMoreListener, com.scwang.smartrefresh.layout.listener.OnRefreshListener { @BindView(R.id.main_banner) Banner mMainBanner; @BindView(R.id.article_content) RecyclerView mArticleContent; @BindView(R.id.refresh_layout) SmartRefreshLayout mRefreshLayout; ArticleAdapter mArticleAdapter; List mArticleDatas = new ArrayList<>(); List mBannerUrls; List mTitles; /** * 当前页 */ int mCurPage = 0; /** * 总页数,默认只有一页 */ int mPageCount = 1; @Override protected int getContentViewId() { return R.layout.activity_main; } @Override protected void init(Bundle savedInstanceState) { initBanner(); initArticleRecycle(); initListener(); initData(); } /** *

获取数据

*/ private void initData() { mPresenter.loadBanner(); mPresenter.refreshArticle(); } /** *

初始化listener

*/ private void initListener() { //设置监听 mMainBanner.setOnBannerListener(this); mArticleAdapter.setListener(this); mRefreshLayout.setOnLoadMoreListener(this); mRefreshLayout.setOnRefreshListener(this); } /** *

初始化文章adapter

*/ private void initArticleRecycle() { //设置adapter mArticleContent.setLayoutManager(new LinearLayoutManager(this)); mArticleAdapter = new ArticleAdapter(mArticleContent, mArticleDatas); mArticleContent.setAdapter(mArticleAdapter); } /** *

初始化Banner

*/ private void initBanner() { mMainBanner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE); mMainBanner.setImageLoader(new GlideImageLoader()); mMainBanner.setImages(new ArrayList()); mMainBanner.setBannerTitles(new ArrayList<>()); mMainBanner.start(); } @Override protected MainPresenter createPresenter() { return new MainPresenter(); } @Override protected void onResume() { super.onResume(); } @Override public void loadBanner(BannerBean bean) { if (bean.getErrorCode() == Constant.BANNER_SUCCESS) { //获取图片路径 List images = bean.getData() .stream() .map(BannerBean.DataBean::getImagePath) .collect(Collectors.toList()); mMainBanner.setImages(images); //获取URL mBannerUrls = bean.getData() .stream() .map(BannerBean.DataBean::getUrl) .collect(Collectors.toList()); //获取title mTitles = bean.getData() .stream() .map(BannerBean.DataBean::getTitle) .collect(Collectors.toList()); mMainBanner.setBannerTitles(mTitles); mMainBanner.start(); } else { LogUtils.d("bean 获取失败"); } } @Override public void loadArticle(List bean) { mArticleAdapter.setBeans(bean); mArticleDatas = bean; } @Override public void refreshArticle(List bean) { mArticleAdapter.setBeans(bean); mArticleDatas = bean; } @Override public void OnBannerClick(int position) { if (mBannerUrls != null) { String url = mBannerUrls.get(position); String title = mTitles.get(position); Utils.startWebView(this, title, url); } } @Override public void onItemClick(View view, int position) { if (mArticleDatas != null) { ArticleBean bean = mArticleDatas.get(position); Utils.startWebView(this, bean.title, bean.link); } } @Override public void onCollectionClick(View view, int position) { ToastUtils.setBgColor(getResources().getColor(R.color.colorPrimaryDark, null)); ToastUtils.setMsgColor(getResources().getColor(R.color.white, null)); ToastUtils.showShort(R.string.coming_soon); } @Override public void onLoadMore(@NonNull RefreshLayout refreshLayout) { mCurPage++; mPresenter.loadArticle(mCurPage); } @Override public void onRefresh(@NonNull RefreshLayout refreshLayout) { mCurPage = 0; mPresenter.refreshArticle(); } @Override protected void onPause() { super.onPause(); mRefreshLayout.finishRefresh(); mRefreshLayout.finishLoadMore(); } @Override public void onError(Throwable e) { //加载 if (mRefreshLayout.getState() == RefreshState.Loading) { mRefreshLayout.finishLoadMore(); mCurPage--; } //刷新 if (mRefreshLayout.getState() == RefreshState.Refreshing) { mRefreshLayout.finishRefresh(); } } @Override public void onComplete() { LogUtils.d("mRefreshLayout.getState():" + mRefreshLayout.getState()); //加载 if (mRefreshLayout.getState() == RefreshState.Loading) { mRefreshLayout.finishLoadMore(); } //刷新 if (mRefreshLayout.getState() == RefreshState.Refreshing) { mRefreshLayout.finishRefresh(); } } }

上滑隐藏Banner

Banner虽然提供了很好的内容展示形式,但是在上滑的过程中,我希望能够看到更多的文章列表内容,这个时候选择将Banner隐藏是最好的办法,将Banner隐藏的方法有很多,这里选择使用官方的控件来处理,实现起来也是非常简单,只需要在布局文件中进行修改即可。

1.引入依赖库

    //design
    implementation 'com.android.support:design:28.0.0'

2.修改布局文件

这里使用CoordinatorLayout作为根布局,结合AppBarLayoutCollapsingToolbarLayout来实现预期效果。






    

        

            
        

    


    

        

        

        
    



全部修改完成后,运行程序,满足要求,剩下的内容后面慢慢加。

5.WanAndroid实战——网络判断

你可能感兴趣的:(WanAndroid实战——刷新加载)