Android入门学习——RxJava+Retrofit+MVP学习

RxJava+Retrofit+MVP学习笔记

本篇笔记是对上一篇Android入门学习——Retrofit+MVP模式学习的补充。这次加上了RxJava的简单使用,并在上一篇中特别简单的Demo的基础上加上了Swiperefreshlayout+RecyclerView的配合使用。加上了下拉刷新以及上拉加载更多。但上拉加载更多也只是个简单的思路,实现的并不好,需要以后再进行优化封装。本人菜鸟,讲解不了啥知识点,只是为了记录学习的过程。想学RxJava可以多看看扔物线大神的给Android开发者的RxJava详解
项目添加的依赖库:
java
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.android.support:recyclerview-v7:23.3.0'
compile 'com.google.code.gson:gson:2.2.4'
compile 'com.android.support:cardview-v7:23.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
**compile 'io.reactivex:rxjava:1.1.5'**
**compile 'io.reactivex:rxandroid:1.2.0'**
**compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'**
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:design:23.3.0'

比上篇多了io.reactivex:rxjava:1.1.5、io.reactivex:rxandroid:1.2.0、com.squareup.retrofit2:adapter-rxjava:2.0.2。使用RxJava这三个都需要添加,并且retrofit2、rxjava、rxandroid版本号要一致,我这里用的都是2.0.2。RxAndroid就是RxJava针对Android推出的。


RxJava的加入

与上一篇相比,工程的目录结构基本没大变化。还是继续尝试学习MVP模式下的目录分层。View和Presenter层基本没啥变化,请求数据的接口依然是上一篇中使用的接口。变化大的是使用Retrofit所创建的接口RetrofitService。
下面贴出RetrofitService的代码:

public interface RetrofitService {
    @FormUrlEncoded
    @POST(AppConfigs.URL_DATA)
    Observable getDataBean(@FieldMap Map map);
}

代码很简单,就3行。和不用RxJava的区别就是接口中方法的返回值,不再是Retrofit中的Call而是换成了RxJava的Ovservable(被观察者)。


理所当然,Model层中的Retrofit网路请求就发生了重大改变。

public class ShowContentModel implements ShowContentBiz {
    private  Subscription sub;
    private boolean state = false;
    public boolean isState() {
        return state;
    }

    @Override
    public void onResult(final onShowContentListener onShowContentListener ,int p) {
        Retrofit retrofit = new Retrofit.Builder().baseUrl(AppConfigs.URL_DATA)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        RetrofitService retrofitService = retrofit.create(RetrofitService.class);

        Map map  = new HashMap<>();
        map.put(AppConfigs.APPID_NAME,AppConfigs.APPID);
        map.put(AppConfigs.SECRECT_NAME,AppConfigs.SECRECT);
        map.put(AppConfigs.PAGE_NAME,""+p);
        map.put(AppConfigs.NUM_NAME,AppConfigs.NUM);
        //Log.e("page","--"+p);
        state = true;//设置为正在请求数据

        sub = retrofitService.getDataBean(map)
                 .subscribeOn(Schedulers.io())//网络请求在io线程
                 .observeOn(AndroidSchedulers.mainThread())//subscribe()在UI线程
                 .subscribe(new Subscriber() {
                           @Override
                           public void onCompleted() {
                               state = false;
                           }

                           @Override
                           public void onError(Throwable e) {
                                state = false;
                                onShowContentListener.onFailed(e.getMessage());
                           }

                           @Override
                           public void onNext(DataBean dataBean) {
                               if (dataBean != null)
                                   onShowContentListener.onSuccess(dataBean);
                           }
                       });
    }

    public void cancelRequest(){
         if ( sub != null && !sub.isUnsubscribed()){
               sub.unsubscribe();
         }
    }
}

Retrofit对象的创建加入了.addCallAdapterFactory(RxJavaCallAdapterFactory.create())这行代码。这行代码必须加,否则会报错。
retrofitService.getDataBean()这个方法的返回对象其实是Observable。只是方法链最后调用了subscribe()这个方法,返回对象也就变就变成了Subscription。这里返回Subsciption这个对象的目的是为了使用isUnsubscribed()以及unsubscribe()这两个方法。
isUnsubscribed()这个方法的返回值为Boolean类型,这个方法是用来判断被观察者是否已经取消了订阅。
unsubscribe()这个方法则是取消订阅,也就是取消了网络请求。
subscribe()这个方法会先执行onNext()这个方法。onCompleted()onError这两个方法则是势不两立的,一个执行另外一个就不再执行。
RxJava和Retrofit简单配合使用就完成了。但RxJava最令人拍手叫好的flatMap,这里并没有用到。以后深入的学习后,再记录补充。


Swiperefreshlayout+RecyclerView的配合使用

下拉刷新

Swiperefreshlayout的使用相对比较方便,几行的代码量就可以实现下拉刷新。
在xml文件中:

"@+id/srfl_main"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
      "@+id/rv_main"
          android:layout_width="match_parent"
          android:layout_height="match_parent">
      

界面布局很简单就是一个SwipeRefreshLayout包裹一个RecyclerView。

在MainActvity中:

swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.srfl_main);
swipeRefreshLayout.setColorSchemeColors(R.color.colorAccent);
swipeRefreshLayout.post(new Runnable() {
            @Override
            public void run() {
                swipeRefreshLayout.setRefreshing(true);
            }
        });

.setColorSchemeColors()设置下拉刷新圆圈的颜色。
swipeRefreshLayout.setRefreshing(true)设置下拉刷新状态,出现小圆圈在界面转。在数据请求完成的时候设置为false,小圆圈就会消失。

swipeRefreshLayout.post()这个方法是为了实现有第一次进入界面的时候,有一种自动刷新的效果。


RecyclerView添加FooterView,实现上拉加载更多

我尝试的是通过RecyclerView的Adapter来添加FooterView,也有通过自定义RecyclerView来添加的思路。
下面是完整的BaseRecyclerAdapter代码:

public abstract class BaseRecyclerAdapter <T> extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
    protected List  datas = new ArrayList<>();
    protected final int mLayoutItemId;
    protected boolean isScrolling;
    protected Context mContext;
    private OnRecyclerItemClickListener mListener;
    //填充布局类型
    private final int TYPE_NORMAL = 0 ;
    private final int TYPE_HEADER = 1;
    private final int TYPE_FOOTER = 2 ;

    //FooterView 和 HeaderView
    private View footerView;
    private View headerView;

    public BaseRecyclerAdapter(RecyclerView rv,int mLayoutItemId) {
        this. mContext =rv.getContext();
        this.mLayoutItemId = mLayoutItemId;
        rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                isScrolling = !(newState == RecyclerView.SCROLL_STATE_IDLE);
                if (!isScrolling) {
                    notifyDataSetChanged();
                }
            }
        });
    }

    public void setHeaderView(View headerView) {
        this.headerView = headerView;
        notifyItemInserted(0);
    }

    public void setFooterView(View footerView) {
        this.footerView = footerView;
        notifyItemInserted(getItemCount() - 1);
    }

    public void removedFooterView(){
        if (footerView != null){
            notifyItemRemoved(getItemCount()-1);
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (headerView !=null && position ==0){
            return  TYPE_HEADER;
        }
        if (footerView != null && position + 1 == getItemCount()) {
            return TYPE_FOOTER ;
        }
        return TYPE_NORMAL ;
    }

    @Override
    public int getItemCount() {
        return datas.size() == 0? 0 :( footerView ==null ? 0:datas.size() + 1 );
    }


    public void setDataList(List  datas){
        if (datas != null){
           this.datas.clear();
           this.datas.addAll(datas);
           notifyDataSetChanged();
        }
    }

    public void setItemListener(OnRecyclerItemClickListener mListener){
        this.mListener = mListener;
    }
    private View.OnClickListener getOnClickListener(final int position){
         return new View.OnClickListener(){
             @Override
             public void onClick(View v) {
                 if (mListener != null & v != null){
                     mListener.onRecyclerItemClick(v,datas.get(position),position);
                 }
             }
         };
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        RecyclerView.ViewHolder holder = null;
        if (viewType == TYPE_FOOTER && footerView != null){
            holder = new FooterOrHeaderViewHolder(footerView);
        }else if(viewType == TYPE_HEADER && headerView != null){
            holder = new FooterOrHeaderViewHolder(headerView);
        }else{
            View view  = inflater.inflate(mLayoutItemId,parent,false);
            holder =new RecyclerHolder(view) ;
        }
        holder.setIsRecyclable(true);
        return holder;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (getItemViewType(position) == TYPE_FOOTER || getItemViewType(position) == TYPE_HEADER){
            return;
        }
        position = getRealPosition(holder);
        bindViewData((RecyclerHolder) holder,datas.get(position),position,isScrolling);
        holder.itemView.setOnClickListener(getOnClickListener(position));
    }

    private int getRealPosition(RecyclerView.ViewHolder viewHolder){
        int position = viewHolder.getLayoutPosition();
        return headerView == null ? position : (position - 1);
    }

    public abstract void bindViewData(RecyclerHolder holder, T item, int position, boolean isScrolling);

    public interface OnRecyclerItemClickListener {
        void onRecyclerItemClick(View view, Object data, int position);
    }

    private class FooterOrHeaderViewHolder extends RecyclerView.ViewHolder {
        public FooterOrHeaderViewHolder(View view) {
            super(view);
        }
    }

    /**
     * 当使用GridLayoutManager时添加HeaderView和FooterView会调用这个方法
     */
    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if(manager instanceof GridLayoutManager) {
            final GridLayoutManager gridManager = ((GridLayoutManager) manager);
            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    if (getItemViewType(position) == TYPE_FOOTER || getItemViewType(position) == TYPE_HEADER){
                        return gridManager.getSpanCount();
                    }else{
                        return 1;
                    }
                }
            });
        }
    }
    /**
     * 当使用瀑布流添加HeaderView的时候会调用这个方法
     */
    @Override
    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
        super.onViewAttachedToWindow(holder);
        ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
        if(lp != null&& lp instanceof StaggeredGridLayoutManager.LayoutParams
                && (holder.getLayoutPosition() == 0||holder.getLayoutPosition() == (getItemCount()-1))) {
            StaggeredGridLayoutManager.LayoutParams slp = (StaggeredGridLayoutManager.LayoutParams) lp;
            slp.setFullSpan(true);
        }
    }
}

onAttachedToRecyclerView()onViewAttachedToWindow这两个方法中的代码是固定的,是为了让添加的HeaderView或者FooterView能够单独占据一行。可以尝试在使用GridLayoutManager和瀑布流的时候不添加这两个方法,看看会出现啥情况。一下子就会搞明白这两个方法干嘛用的了。


在MainActivity中:

rv.setOnScrollChangeListener(new RecyclerView.OnScrollChangeListener(){
            @Override
            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                RecyclerView rView = (RecyclerView) v;
                int lastVisible = ((GridLayoutManager)rView.getLayoutManager()).findLastVisibleItemPosition();

                if (!presenter.getRefreshState() && rView.getAdapter().getItemCount() - 1 == lastVisible && lastVisible != mLastVisibleItem){
                    mLastVisibleItem = lastVisible;
                    presenter.getListData(++ page);
                }
            }
        });

就是给RecyclerView设置滑动监听,判断条件是,是否正在进行数据的请求,最后一个item是否出现,最后一个itme是否为第一次出现。

最后

这篇博客只是记载我的学习以及尝试。代码中的不足以后会修补,下面是源码。
又修改了一部分代码,加入了HeaderView
代码

你可能感兴趣的:(android-入门,mvp,rxJava,retrofit,上拉加载更多)