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

来源:互联网 发布:美好的每一天剧情知乎 编辑:程序博客网 时间:2024/05/16 16:11

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<DataBean> getDataBean(@FieldMap Map<String,String> 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<String,String> 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<DataBean>() {                           @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文件中:

<android.support.v4.widget.SwipeRefreshLayout       android:id="@+id/srfl_main"       android:layout_width="match_parent"       android:layout_height="match_parent">      <android.support.v7.widget.RecyclerView          android:id="@+id/rv_main"          android:layout_width="match_parent"          android:layout_height="match_parent">      </android.support.v7.widget.RecyclerView></android.support.v4.widget.SwipeRefreshLayout>

界面布局很简单就是一个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 <T> 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 <T> 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
代码

0 0
原创粉丝点击