MVP+xRecyclerView+Retrofit+OkHttp+RxJava (Observable)

本文介绍了一款NBA新闻App的开发过程,包括使用Retrofit进行网络请求、XRecyclerView实现分页加载、Butterknife简化视图操作、RxJava处理异步任务、Fresco进行图片加载等关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现思路:

1. 主页面显示一个xRecycleveiw实现分页加载

2. Retrofit访问网络接口获取数据

3. xRecycleview 实现多条目展示上面效果

4. MVP分层;P调M  ;V实现P

5. 自定义搜索框

6. 在构造方法中初始化Fresco并做配置

7. 配置图片的显示方式为圆形显示

8. 在适配器显示视图的方法中通过Fresco来显示图片

接口:APIKEY=71e58b5b2f930eaf1f937407acde08fe

http://api.tianapi.com/nba/?key=APIKEY&num=10

1.依赖

//Butterknife依赖(黄油刀)compile 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

//XRecyclerview依赖》
compile 'com.jcodecraeer:xrecyclerview:1.3.2'

//Okhttp网络请求依赖》
compile 'com.squareup.okhttp3:okhttp:3.10.0'

//Retrofit网络请求依赖》
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
compile 'com.squareup.retrofit2:converter-gson:2.4.0'

//Fresco图片框架依赖》
compile 'com.facebook.fresco:fresco:0.14.1'

//RXjava2的适配器》
compile 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'

//Rxjava2compile 'io.reactivex.rxjava2:rxjava:2.1.7'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

//Gson解析依赖》
compile 'com.google.code.gson:gson:2.2.4'

2.权限

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

3.网络获取接口的数据Bean

package com.example.rikao0419.entity;

import java.util.List;

public class MyNewsListBean {

    private int code;
    private String msg;
    private List<NewslistBean> newslist;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public List<NewslistBean> getNewslist() {
        return newslist;
    }

    public void setNewslist(List<NewslistBean> newslist) {
        this.newslist = newslist;
    }

    public static class NewslistBean {

        private String ctime;
        private String title;
        private String description;
        private String picUrl;
        private String url;

        public String getCtime() {
            return ctime;
        }

        public void setCtime(String ctime) {
            this.ctime = ctime;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public String getPicUrl() {
            return picUrl;
        }

        public void setPicUrl(String picUrl) {
            this.picUrl = picUrl;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }
    }
}
4.接口
package com.example.rikao0419.utils;

//创建方法 保存接口
public class StringUrl {
    public static final String BASE_URL="http://api.tianapi.com/";
}

5.拼接接口

package com.example.rikao0419.service;

import com.example.rikao0419.entity.MyNewsListBean;
import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface ApiService {

    //拼接数据接口
    //请求次数多用:Flowable(背压) 请求次数少用:Observable(无背压)
    @GET("nba/")
    Observable<MyNewsListBean> getData(@Query("key")String key, @Query("num")String num);
}
6.RetrofitUtils的封装
package com.example.rikao0419.utils;

import com.example.rikao0419.service.ApiService;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

//Retrofit 网络请求框架
public class RetrofitUtils {
    //1创建一个单列模式
    private static volatile RetrofitUtils instance;
    private final Retrofit retrofit;

    //2创建一个私有的无参构造
    private RetrofitUtils(){

        //3创建Retrofit
        retrofit = new Retrofit.Builder()
                .baseUrl(StringUrl.BASE_URL)//接口
                .addConverterFactory(GsonConverterFactory.create())//默认Gson解析
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//使用RxJava2的适配器
                .build();
    }

    //4.创建一个静态方法,得到instance 判断是否为空
    public static RetrofitUtils getInstance(){
        if(instance==null){
            //添加同步锁
            synchronized (RetrofitUtils.class){
                if(null==instance){
                    instance = new RetrofitUtils();
                }
            }
        }
        //5返回创建的instance
        return instance;
    }

    //6创建方法 方便调用
    public ApiService getservice(){
        return retrofit.create(ApiService.class);
    }

}

7.M层接口

package com.example.rikao0419.model;

public interface DataModel {

    //定义请求数据的方法
    void getModelData(String key,String num);
}

8.M层实现类

package com.example.rikao0419.model;

import com.example.rikao0419.entity.MyNewsListBean;
import com.example.rikao0419.presenter.MyDataPresenter;
import com.example.rikao0419.utils.RetrofitUtils;
import io.reactivex.Observable;

public class MyDataModel implements DataModel{

    MyDataPresenter presenter;

    //创建构造方法,将p层传入M    public MyDataModel(MyDataPresenter presenter){
        this.presenter=presenter;
    }

    //重写请求数据的方法
    @Override
    public void getModelData(String key, String num) {
        //RetrofitUtils工具类 调用getservice,得到ApiService,再调用其数据方法
        Observable<MyNewsListBean> data = RetrofitUtils.getInstance().getservice().getData(key, num);
        //调用P层方法,将数据传给P        presenter.getPresenterData(data);
    }
}

9.P层的接口

package com.example.rikao0419.presenter;

import com.example.rikao0419.entity.MyNewsListBean;
import io.reactivex.Observable;

public interface DataPresenter {
    //定义请求数据的方法
    void getPresenterData( Observable<MyNewsListBean> data);

}

10.P层的实现类

package com.example.rikao0419.presenter;

import com.example.rikao0419.entity.MyNewsListBean;
import com.example.rikao0419.model.MyDataModel;
import com.example.rikao0419.view.DataView;
import java.util.List;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

public class MyDataPresenter implements DataPresenter{

    //创建构造器 将M层传入P    DataView dataView;
    private final MyDataModel model;
    public MyDataPresenter(DataView dataView){
        this.dataView=dataView;

        //实例化M层 将P层传给M        model = new MyDataModel(this);
    }

    //重写请求数据的方法
    @Override
    public void getPresenterData( Observable<MyNewsListBean> data) {
        //改变线程  RXjava异步
        data.subscribeOn(Schedulers.io())//Io子线程 网络请求
                .observeOn(AndroidSchedulers.mainThread())//主线程 更新控件

                //subscribe:赋观察者的方法 Observer:观察者
                .subscribe(new Observer<MyNewsListBean>() {

                    //onSubscribe:订阅者
                    @Override
                    public void onSubscribe(Disposable d) {}

                    //下一个
                    @Override
                    public void onNext(MyNewsListBean myNewsListBean) {
                        //返回集合 判断若集合不等于空
                        if(myNewsListBean!=null) {
                            List<MyNewsListBean.NewslistBean> newslist =
                                    myNewsListBean.getNewslist();

                            //调用V层方法,将数据传给V                            dataView.Success(newslist);
                        }
                    }

                    //异常
                    @Override
                    public void onError(Throwable e) {
                        //调用V层方法,将异常数据传给V                          dataView.Error(e);
                    }

                    //成功
                    @Override
                    public void onComplete() {}
                });
    }

    //创建一个方法 用于接收V层传来的KEYNUM
    public void getKeyorNum(String key,String num){
        //调用M层方法 将keynum传给M        model.getModelData(key, num);

    }

    //V层置空 防止内存泄露
    public void DreyView(){
        if(dataView!=null){
            dataView=null;
        }
    }
}

11.V层接口

package com.example.rikao0419.view;

import com.example.rikao0419.entity.MyNewsListBean;
import java.util.List;

public interface DataView {
    //成功与失败的方法
    void Success(List<MyNewsListBean.NewslistBean> newslist);
    void Error(Throwable t);

}

12.V层接口的实现类

package com.example.rikao0419.view;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import com.example.rikao0419.R;
import com.example.rikao0419.adapter.MyAdapter;
import com.example.rikao0419.entity.MyNewsListBean;
import com.example.rikao0419.presenter.MyDataPresenter;
import com.jcodecraeer.xrecyclerview.ProgressStyle;
import com.jcodecraeer.xrecyclerview.XRecyclerView;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;

public class MainActivity extends AppCompatActivity implements DataView {

    @BindView(R.id.main_xrcv)
    XRecyclerView main_XRcv;
    private MyDataPresenter presenter;
    private MyAdapter myAdapter;
    int num =10;//调用每次请求的条数
    //创建集合 用于存放请求的数据
    private List<MyNewsListBean.NewslistBean> list = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        //设置布局
        main_XRcv.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));

        //实例化P层 ,将V层和keynum传给P        presenter = new MyDataPresenter(this);
        presenter.getKeyorNum("71e58b5b2f930eaf1f937407acde08fe","10");

        //设置上拉加载下拉刷新
        main_XRcv.setLoadingMoreEnabled(true);
        main_XRcv.setPullRefreshEnabled(true);

        //设置上拉下拉的样式
        main_XRcv.setRefreshProgressStyle(ProgressStyle.BallSpinFadeLoader);
        main_XRcv.setLoadingMoreProgressStyle(ProgressStyle.Pacman);

        //设置XRecyclerView的监听器
        main_XRcv.setLoadingListener(new XRecyclerView.LoadingListener() {
            //下拉刷新
            @Override
            public void onRefresh() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //重新调用P层方法,请求数据
                        presenter.getKeyorNum("71e58b5b2f930eaf1f937407acde08fe",num+"");
                        //设置一秒后停止刷新
                        main_XRcv.refreshComplete();
                    }
                },1000);
            }

            //上拉加载
            @Override
            public void onLoadMore() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //每次请求多增加10条数据
                       num+=10;
                        //重新调用P层方法,请求数据
                        presenter.getKeyorNum("71e58b5b2f930eaf1f937407acde08fe",num+"");
                        //设置一秒后停止刷新
                        main_XRcv.refreshComplete();
                    }
                },1000);
            }
        });
    }

    //成功 因其在主线程,可直接更新UI 进入适配器
    @Override
    public void Success(List<MyNewsListBean.NewslistBean> newslist) {
        //清空集合
        list.clear();
        //将数据重新全部添加入集合
        list.addAll(newslist);
        //进入适配器
        getAdapter();
    }

    //设置适配器
     void getAdapter(){
        //判断适配器是否存在
        if(myAdapter == null){
            //不存在则创建适配器
            myAdapter = new MyAdapter(this,list);
            //并设置适配器
            main_XRcv.setAdapter(myAdapter);
        }else{
            //若存在,则只刷新
           myAdapter.notifyDataSetChanged();
        }
    }

    //失败
    @Override
    public void Error(Throwable t) {}
}

13.适配器

package com.example.rikao0419.adapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.example.rikao0419.R;
import com.example.rikao0419.entity.MyNewsListBean;
import com.facebook.drawee.view.SimpleDraweeView;
import java.util.List;

public class MyAdapter extends RecyclerView.Adapter {
    Context context;
    List<MyNewsListBean.NewslistBean> list;
    private View view;
    private int itemViewType;

    public MyAdapter(Context context, List<MyNewsListBean.NewslistBean> list) {
        this.context=context;
        this.list=list;
    }

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

        //获取条目类型
        itemViewType = getItemViewType(viewType);

        //进行判断引入布局
        if(itemViewType ==0){
            view = View.inflate(context, R.layout.item_01, null);
        }else{
            view = View.inflate(context, R.layout.item_02, null);
        }

        //创建myViewHolder 将布局view 传下去
        MyViewHolder myViewHolder = new MyViewHolder(view);
        return myViewHolder;
    }

    @Override
    public int getItemViewType(int position) {
        //获取条目的下标,进行判断
        if(position%2==0){
            return 0;
        }else{
            return 1;
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        MyViewHolder myViewHolder = (MyViewHolder) holder;

        //根据下标得到条目类型
        int itemViewType = getItemViewType(position);

        //进行判断 进行赋值
        if(itemViewType ==0){
            myViewHolder.rcv_item01_title.setText(list.get(position).getTitle());
        }else{
            myViewHolder.rcv_item02_title.setText(list.get(position).getTitle());
            myViewHolder.rcv_item02_img.setImageURI(list.get(position).getPicUrl());
       }
    }

    //条目数量
    @Override
    public int getItemCount() {
        return list.size();
    }

    class MyViewHolder extends RecyclerView.ViewHolder{

        private final TextView rcv_item01_title;
        private final TextView rcv_item02_title;
        private final SimpleDraweeView rcv_item02_img;

        public MyViewHolder(View view) {
            super(view);

            //找到引入布局的控件的ID
            rcv_item01_title = view.findViewById(R.id.rcv_item01_title);
            rcv_item02_title = view.findViewById(R.id.rcv_item02_title);
            rcv_item02_img = view.findViewById(R.id.rcv_item02_img);
        }
    }
}

14.初始化Fresco使用默认配置加载图片

package com.example.rikao0419.app;

import android.app.Application;
import com.facebook.drawee.backends.pipeline.Fresco;

public class MyApp extends Application{
    @Override
    public void onCreate() {
        super.onCreate();

        //初始化Fresco使用默认配置加载图片
        Fresco.initialize(this);
    }
}

15.主布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="com.example.rikao0419.view.MainActivity">

   <!--标题布局-->
   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="80px"
       android:background="#007FFF"
       android:layout_gravity="center_vertical"
       android:gravity="center_vertical"
       android:orientation="horizontal">
      <EditText
          android:layout_width="match_parent"
          android:layout_height="50px"
          android:textColor="#ffffff"
          android:background="#3399FF"
          android:paddingLeft="20px"
          android:hint="搜索"
          />
   </LinearLayout>

   <!--XRecyclerView主布局-->
   <com.jcodecraeer.xrecyclerview.XRecyclerView
       android:id="@+id/main_xrcv"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
   </com.jcodecraeer.xrecyclerview.XRecyclerView>

</LinearLayout>

16.偶数条目布局

<?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="wrap_content"
    android:orientation="vertical"
    android:padding="10px">

    <!--XRecyclerView偶数条目布局-->
    <TextView
        android:id="@+id/rcv_item01_title"
        android:layout_width="match_parent"
        android:layout_height="100px"
        android:layout_gravity="center_vertical"
        android:gravity="center_vertical"
        android:text="格林:理解科尔吸食大麻 不过我从没吸过"/>

</LinearLayout>

17.奇数条目布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    android:padding="10px">

    <!--XRecyclerView奇数条目布局-->
    <TextView
        android:id="@+id/rcv_item02_title"
        android:layout_width="400px"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:gravity="center_vertical"
        android:text="格林:理解科尔吸食大麻 不过我从没吸过"/>


    <!--XRecyclerView奇数条目圆形图片布局-->
    <com.facebook.drawee.view.SimpleDraweeView
        android:layout_marginLeft="20px"
        android:id="@+id/rcv_item02_img"
        android:layout_width="100px"
        android:layout_height="100px"
        fresco:actualImageScaleType="centerCrop"
        fresco:placeholderImageScaleType="centerCrop"
        fresco:roundAsCircle="true"
        android:layout_toRightOf="@id/rcv_item02_title"/>

</RelativeLayout>








评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值