WanAndroid实战——刷新加载

作者: Tom_Ji | 来源:发表于2019-04-04 15:46 被阅读11次

    前情回顾:

    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,所以在它外面加。

        <com.scwang.smartrefresh.layout.SmartRefreshLayout
            android:id="@+id/refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            >
    
            <com.scwang.smartrefresh.layout.header.ClassicsHeader
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/article_content"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
    
                />
    
            <com.scwang.smartrefresh.layout.footer.ClassicsFooter
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
        </com.scwang.smartrefresh.layout.SmartRefreshLayout>
    

    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;
    
    /**
     * <p>Title: Contract</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 10:13
     **/
    public class Contract {
    
        public interface IBaseView {
            /**
             * <p>加载错误回调</p>
             * @param e Throwable
             */
            void onError(Throwable e);
    
            /**
             * <p>加载完成</p>
             */
            void onComplete();
        }
    
    
    
        public interface IMainModel {
    
            /**
             * <p>获取banner数据</p>
             * @return banner数据
             */
            Observable<BannerBean> loadBanner();
    
            /**
             * <p>加载首页文章</p>
             * @param number 页码
             * @return 首页文章
             */
            Observable<List<ArticleBean>> loadArticle(int number);
    
    
            /**
             * <p>刷新首页文章</p>
             * @return 首页文章
             */
            Observable<List<ArticleBean>> refreshArticle();
    
        }
    
        public interface IMainView extends IBaseView{
            /**
             * <p>View 获取到数据后进行显示</p>
             * @param bean banner的数据
             */
            void loadBanner(BannerBean bean);
    
    
            /**
             * <p>加载首页文章信息</p>
             * @param bean 首页文章数据
             */
            void loadArticle(List<ArticleBean> bean);
    
            /**
             * <p>刷新首页文章</p>
             *
             * @param bean
             */
            void refreshArticle(List<ArticleBean> bean);
    
    
        }
    
        public interface IMainPresenter{
    
            /**
             * <p>首页banner</p>
             */
            void loadBanner();
    
    
            /**
             * <p>加载首页文章</p>
             * @param number 页码
             */
            void loadArticle(int number);
    
            /**
             * <p>刷新首页文章</p>
             */
            void refreshArticle();
    
        }
    }
    
    

    4.处理报错

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

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

    package com.tom.wanandroid.bean;
    
    /**
     * <p>Title: ArticleBean</p>
     * <p>Description:处理后的文章bean </p>
     *
     * @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;
    
    /**
     * <p>Title: IRetrofitData</p>
     * <p>Description: </p>
     *
     * @author tom
     * @date 2019/3/7 11:34
     **/
    public interface IRetrofitData {
        /**
         * <p>获取首页轮播图数据</p>
         * @return 首页轮播图数据
         */
        @GET("banner/json")
        Observable<BannerBean> loadBanner();
    
        /**
         * <p>获取首页文章数据</p>
         * @param number 页码
         * @return 返回首页文章
         */
        @GET("article/list/{number}/json")
        Observable<ArticleBeanOriginal> loadMainArticle(@Path("number") int number);
    
    
        /**
         * <p>刷新首页文章数据</p>
         * @return 返回首页文章
         */
        @GET("article/list/0/json")
        Observable<ArticleBeanOriginal> 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;
    
    /**
     * <p>Title: MainModel</p>
     * <p>Description: </p>
     *
     * @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<ArticleBean> mArticleBeans = new ArrayList<>();
    
        @Override
        public Observable<BannerBean> loadBanner() {
            IRetrofitData retrofitData = getIRetrofitData();
    
            return retrofitData.loadBanner();
        }
    
        @Override
        public Observable<List<ArticleBean>> 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<List<ArticleBean>> 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<List<ArticleBean>>() {
                        @Override
                        public void onSubscribe(Disposable d) {
                            mCompositeDisposable.add(d);
                        }
    
                        @Override
                        public void onNext(List<ArticleBean> 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;
    
    /**
     * <p>Title: MainActivity</p>
     * <p>Description: 主页activity</p>
     *
     * @author tom
     * @date 2019/3/9 10:15
     */
    public class MainActivity extends BaseActivity<Contract.IMainView, MainPresenter> 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<ArticleBean> mArticleDatas = new ArrayList<>();
        List<String> mBannerUrls;
        List<String> 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();
        }
    
        /**
         * <p>获取数据</p>
         */
        private void initData() {
            mPresenter.loadBanner();
            mPresenter.refreshArticle();
        }
    
        /**
         * <p>初始化listener</p>
         */
        private void initListener() {
            //设置监听
            mMainBanner.setOnBannerListener(this);
            mArticleAdapter.setListener(this);
            mRefreshLayout.setOnLoadMoreListener(this);
            mRefreshLayout.setOnRefreshListener(this);
        }
    
        /**
         * <p>初始化文章adapter</p>
         */
        private void initArticleRecycle() {
            //设置adapter
            mArticleContent.setLayoutManager(new LinearLayoutManager(this));
            mArticleAdapter = new ArticleAdapter(mArticleContent, mArticleDatas);
            mArticleContent.setAdapter(mArticleAdapter);
        }
    
        /**
         * <p>初始化Banner</p>
         */
        private void initBanner() {
    
            mMainBanner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE);
            mMainBanner.setImageLoader(new GlideImageLoader());
            mMainBanner.setImages(new ArrayList<String>());
            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<String> 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<ArticleBean> bean) {
            mArticleAdapter.setBeans(bean);
            mArticleDatas = bean;
    
        }
    
        @Override
        public void refreshArticle(List<ArticleBean> 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来实现预期效果。

    
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".view.MainActivity">
    
    
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            >
    
            <android.support.design.widget.CollapsingToolbarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_scrollFlags="scroll|snap">
    
                <com.youth.banner.Banner
                    android:id="@+id/main_banner"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/banner_height"/>
            </android.support.design.widget.CollapsingToolbarLayout>
    
        </android.support.design.widget.AppBarLayout>
    
    
        <com.scwang.smartrefresh.layout.SmartRefreshLayout
            android:id="@+id/refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
    
            <com.scwang.smartrefresh.layout.header.ClassicsHeader
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/article_content"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
    
                />
    
            <com.scwang.smartrefresh.layout.footer.ClassicsFooter
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
        </com.scwang.smartrefresh.layout.SmartRefreshLayout>
    
    
    </android.support.design.widget.CoordinatorLayout>
    

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

    相关文章

      网友评论

        本文标题:WanAndroid实战——刷新加载

        本文链接:https://www.haomeiwen.com/subject/vmqqiqtx.html