效果图:
效果图描述:
使用RxJava结合Retrofit,继承封装好的Activity与Presenter,使用MVP,请求网络数据;
使用Fresco加载图片,RecyclerView展示数据。
首先在项目Model的build.gradle里面导入依赖
//butterknife在Studio3.0版本上需使用以下8.8.1版本(下面2行代码都要加) compile 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' compile 'com.android.support:recyclerview-v7:26.+' //(SDK版本需为26) //rxjava2结合retrofit2 compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxjava:2.1.7' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' compile 'com.facebook.fresco:fresco:1.5.0' //由于Retrofit是基于OkHttp,所以还需要添加OkHttp库依赖 compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-scalars:2.3.0' compile 'com.squareup.okhttp3:okhttp:3.9.0' //如果采用了 Gson 解析,需要在 Gradle加入retrofit2类库中的gson依赖 compile 'com.squareup.retrofit2:converter-gson:2.3.0' //较新版本
新建MyApplication 继承Application ,并在清单文件中配置
/** * 用于全局配置初始化异步加载类 */ public class MyApplication extends Application { //设置公共变量 public static GetDataInterface request; @Override public void onCreate() { super.onCreate(); //1. 用于全局配置初始化Fresco 图片加载 Fresco.initialize(this); //2. 用于全局配置初始化Retrofit 网络请求 RxJava结合Retrofit //构建Retrofit类,初始化参数 Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://api.tianapi.com") .addConverterFactory(GsonConverterFactory.create()) // call 转化成 Observerable .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); //创建网络请求接口实例 request = retrofit.create(GetDataInterface.class); } }
清单文件中加入权限
<uses-permission android:name="android.permission.INTERNET" />
请求网络获得的数据生成实体类
import java.util.List; /** * http://api.tianapi.com/nba/?key=71e58b5b2f930eaf1f937407acde08fe&num=10 */ public class Bean { /** * code : 200 * msg : success * newslist : [{"ctime":"2016-12-04 13:00","title":"格林:理解科尔吸食大麻 不过我从没吸过","description":"NBA新闻","picUrl":"http://www.51tyw.com/uploads/allimg/161204/1-161204120131.jpg","url":"http://www.51tyw.com/nba/2421.html"},{"ctime":"2016-12-04 00:00","title":"三分纪录延续!火箭队连续19场比赛命中10+三分球","description":"NBA新闻","picUrl":"http://www.51tyw.com/uploads/allimg/161203/1-161203233J3.jpg","url":"http://www.51tyw.com/nba/2417.html"},{"ctime":"2016-12-04 00:00","title":"詹姆斯谈三连败:是时候紧起来了 必须打得男人点","description":"NBA新闻","picUrl":"http://www.51tyw.com/uploads/allimg/161203/1-161203234010.jpg","url":"http://www.51tyw.com/nba/2418.html"},{"ctime":"2016-12-03 12:00","title":"骑士输赢都靠三分?那还要詹姆斯做什么?","description":"NBA新闻","picUrl":"http://www.51tyw.com/uploads/allimg/161203/1-161203104344.jpg","url":"http://www.51tyw.com/nba/2407.html"},{"ctime":"2016-12-03 00:00","title":"公牛vs骑士直播看点:詹伟兄弟对决","description":"NBA新闻","picUrl":"http://www.51tyw.com/uploads/allimg/161202/1-161202221205.jpg","url":"http://www.51tyw.com/nba/2395.html"},{"ctime":"2016-12-02 22:00","title":"火箭和勇士联手创NBA三分纪录","description":"NBA新闻","picUrl":"http://www.51tyw.com/uploads/allimg/161202/1-161202214212.jpg","url":"http://www.51tyw.com/nba/2392.html"},{"ctime":"2016-12-02 22:00","title":"巴克利:勇士打得像女式篮球,太软了!","description":"NBA新闻","picUrl":"http://www.51tyw.com/uploads/allimg/161202/1-161202215032.jpg","url":"http://www.51tyw.com/nba/2394.html"},{"ctime":"2016-12-02 20:00","title":"詹姆斯完成月最佳球员4连霸的壮举!","description":"NBA新闻","picUrl":"http://www.51tyw.com/uploads/allimg/161202/1-1612021PI0.jpg","url":"http://www.51tyw.com/nba/2388.html"},{"ctime":"2016-12-01 00:00","title":"威少再下三双战书!詹皇会不会先认怂?","description":"NBA新闻","picUrl":"http://www.51tyw.com/uploads/allimg/161128/1-16112Q13251.jpg","url":"http://www.51tyw.com/nba/2374.html"},{"ctime":"2016-12-01 00:00","title":"骑士这样的防守想夺冠?也许卫冕只是空谈!","description":"NBA新闻","picUrl":"http://www.51tyw.com/uploads/allimg/161130/1-161130222229.jpg","url":"http://www.51tyw.com/nba/2375.html"}] */ private int code; private String msg; private Listnewslist; 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 getNewslist() { return newslist; } public void setNewslist(List newslist) { this.newslist = newslist; } public static class NewslistBean { /** * ctime : 2016-12-04 13:00 * title : 格林:理解科尔吸食大麻 不过我从没吸过 * description : NBA新闻 * picUrl : http://www.51tyw.com/uploads/allimg/161204/1-161204120131.jpg * url : http://www.51tyw.com/nba/2421.html */ 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; } } @Override public String toString() { return "Bean{" + "code=" + code + ", msg='" + msg + '\'' + ", newslist=" + newslist + '}'; } }
新建接口类GetDataInterface ,这里提供了get传参方法(@QueryMap注解)
import bean.Bean; import java.util.Map; import io.reactivex.Observable; import retrofit2.Call; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.POST; import retrofit2.http.QueryMap; /** * 网络接口数据的请求类 * 接口:APIKEY=‘71e58b5b2f930eaf1f937407acde08fe’ http://api.tianapi.com/nba/?key=APIKEY&num=10 */ public interface GetDataInterface { /* * 简单使用Retrofit的get请求数据 不传递参数 */ @GET("/nba/?key=18e883dd6b316eb1d97fd86338abbf06&num=10") Callget1(); /** * 使用Retrofit的get请求数据,使用@QueryMap注解传递集合参数 */ @POST("/nba") Call get2(@QueryMap Map map); /** * 使用Retrofit的get请求数据,使用@QueryMap注解传递集合参数 */ @GET("/nba") Observable get(@QueryMap Map map); /** * 使用Retrofit的post请求数据,使用@FieldMap注解传递集合参数 * 但是此接口不能使用post请求,因为该接口返回的数据只有get方式,所以下面的注解不能用(可以参照引用能用post方式的接口) */ @FormUrlEncoded @POST("/nba") Observable post(@FieldMap Map map); }
自定义Activity抽象类,持有View层和Presenter层
import android.app.Activity; import android.os.Bundle; /** * Activity抽象类,持有View层和Presenter层 * @param表示持有的View层 * @param表示持有的Presenter层 */ public abstract class BaseMvpActivity<V,T extends BasePresenter<V>> extends Activity { //声明持有的p层 public T p; public abstract T initPresenter(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base_mvp); p = initPresenter(); } //视图运行获取焦点时,连接view层 @Override protected void onResume() { super.onResume(); p.attach((V) this); } //试图销毁时释放内存 @Override protected void onDestroy() { super.onDestroy(); p.detach(); } }
自定义一个Presenter类,持有view层接口
/** * 自定义一个Presenter层,持有view层接口 */ public class BasePresenter<V> { // V 相当于V的接口 public V view; /** * 自定义Presenter 持有View 的接口 * @param v */ public void attach(V v){ this.view = v; } /** * 自定义Presenter 释放持有View的接口, 防止内存泄漏 */ public void detach(){ this.view = null; } }
使用MVP三层编写数据
1.model层
import java.util.HashMap; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; //model接口实现类 public class MyModel { /** * 简单使用Retrofit的get请求, 不传递参数 * @param callBack */ public void getData1(final ModelCallBack callBack){ //设置接口请求的key值 Callmodel层接口call = MyApplication.request.get1(); //发起异步请求 call.enqueue(new Callback () { @Override public void onResponse(Call call, Response response) { //获取响应的数据 Bean bean = response.body(); //请求成功时返回数据 callBack.onSuccess(bean); } @Override public void onFailure(Call call, Throwable t) { //请求失败时返回数据 callBack.onFailure(new Exception("")); } }); } /** * 简单使用Retrofit的get请求, 使用@QueryMap注解传递集合参数 * @param callBack */ public void getData2(final ModelCallBack callBack){ //设置接口请求的集合参数 HashMap map = new HashMap<>(); map.put("key", "18e883dd6b316eb1d97fd86338abbf06"); map.put("num", "10"); Call call = MyApplication.request.get2(map); //发起异步请求 call.enqueue(new Callback () { @Override public void onResponse(Call call, Response response) { //获取响应的数据 Bean bean = response.body(); //请求成功时返回数据 callBack.onSuccess(bean); } @Override public void onFailure(Call call, Throwable t) { //请求失败时返回数据 callBack.onFailure(new Exception("")); } }); } /** * 使用Retrofit结合RxJava的get请求数据,使用@QueryMap注解传递集合参数 * @param callBack */ public void getData(final ModelCallBack callBack) { //设置接口请求的集合参数 HashMap map = new HashMap<>(); map.put("key", "18e883dd6b316eb1d97fd86338abbf06"); map.put("num", "10"); MyApplication.request.get(map) //获取Observable对象 .subscribeOn(Schedulers.io()) //请求完成后在io线程中执行 .observeOn(AndroidSchedulers.mainThread()) //最后在主线程中执行 //进行事件的订阅,使用Consumer实现 .subscribe(new Consumer () { @Override public void accept(Bean bean) throws Exception { //请求成功时返回数据 callBack.onSuccess(bean); System.out.println(bean.toString()); } }); } }
/** * model层接口,成功和失败的方法 */ public interface ModelCallBack { public void onSuccess(Bean bean); public void onFailure(Exception e); }2.view层
/** * view层接口类,请求成功与失败的方法 */ public interface MyView { public void onSuccess(Bean bean); public void onFailure(Exception e); }3.presenter层
// Presenter层,进行view层与model数据的交互
public class MyPresenter extends BasePresenter{ private MyModel myModel; public MyPresenter() { this.myModel = new MyModel(); } /** * 简单使用Retrofit的get请求, 不传递参数 */ public void get1(){ myModel.getData1(new ModelCallBack() { @Override public void onSuccess(Bean bean) { //数据交互时,为防止内存泄露,设置view层数据为空 if (view != null){ view.onSuccess(bean); } } @Override public void onFailure(Exception e) { //数据交互时,为防止内存泄露,设置view层数据为空 if (view != null){ view.onFailure(new Exception("e")); } } }); } /** * 简单使用Retrofit的get请求, 使用@QueryMap注解传递集合参数 */ public void get2(){ myModel.getData2(new ModelCallBack() { @Override public void onSuccess(Bean bean) { //数据交互时,为防止内存泄露,设置view层数据为空 if (view != null){ view.onSuccess(bean); } } @Override public void onFailure(Exception e) { //数据交互时,为防止内存泄露,设置view层数据为空 if (view != null){ view.onFailure(new Exception("e")); } } }); } /** * 简单使用Retrofit结合RxJava的get请求数据, 使用@QueryMap注解传递集合参数 */ public void get(){ myModel.getData(new ModelCallBack() { @Override public void onSuccess(Bean bean) { //数据交互时,为防止内存泄露,设置view层数据为空 if (view != null){ view.onSuccess(bean); } } @Override public void onFailure(Exception e) { //数据交互时,为防止内存泄露,设置view层数据为空 if (view != null){ view.onFailure(new Exception("e")); } } }); } //取消p层与v层的绑定,防止内存泄露 @Override public void detach(){ this.view = null; } }
回调到view层MainActivity
//主页面继承自定义Activity,持有p与c层 public class MainActivity extends BaseMvpActivityimplements MyView { @BindView(R.id.recyclerView) RecyclerView recyclerView; private MyAdapter adapter; //声明presenter层,与view层交互 @Override public MyPresenter initPresenter() { return new MyPresenter(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); //p层的get请求方式 // p.get1(); //不传递参数 // p.get2(); //只使用Retrofit的get请求数据,使用@QueryMap注解传递集合参数 p.get(); //使用Retrofit+RxJava的get请求数据,使用@QueryMap注解传递集合参数 } @Override public void onSuccess(Bean bean) { //设置布局管理器以及布局适配器 LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); recyclerView.setLayoutManager(manager); adapter = new MyAdapter(MainActivity.this); adapter.addData(bean); recyclerView.setAdapter(adapter); Toast.makeText(MainActivity.this,"数据:"+bean.toString(),Toast.LENGTH_SHORT).show(); } @Override public void onFailure(Exception e) { System.out.println("数据出错:"+e); } }
activity_main.xml
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="match_parent" > <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent">android.support.v7.widget.RecyclerView> LinearLayout>适配器MyAdapter
/** * //RecyclerView展示数据适配器 */ public class MyAdapter extends RecyclerView.Adapteradapter.xml{ private Context context; private List list; public MyAdapter(Context context) { this.context = context; } //声明数据来源,添加数据 public void addData(Bean bean) { if (this.list == null) { this.list = new ArrayList<>(); } this.list.addAll(bean.getNewslist()); notifyDataSetChanged(); } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //创建视图 View view = LayoutInflater.from(context).inflate(R.layout.adapter, parent, false); return new MyViewHolder(view); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { //加载布局 holder.draweeView.setImageURI(list.get(position).getPicUrl()); holder.title.setText(list.get(position).getTitle()); } @Override public int getItemCount() { return list == null ? 0: list.size(); } static class MyViewHolder extends RecyclerView.ViewHolder { @BindView(R.id.draweeView) SimpleDraweeView draweeView; @BindView(R.id.title) TextView title; public MyViewHolder(View itemView) { super(itemView); ButterKnife.bind(this,itemView); } } }
xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:fresco="http://schemas.android.com/apk/res-auto" android:padding="6dp" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <com.facebook.drawee.view.SimpleDraweeView android:id="@+id/draweeView" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginLeft="6dp" fresco:placeholderImage="@drawable/app_default" fresco:placeholderImageScaleType="fitCenter" fresco:actualImageScaleType="focusCrop" fresco:failureImage="@drawable/load_error_image" fresco:failureImageScaleType="fitCenter" fresco:roundAsCircle="true"/> <TextView android:text="标题" android:id="@+id/title" android:textSize="18sp" android:textStyle="bold" android:layout_marginLeft="36dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical"/> LinearLayout>
Fresco加载图片的素材:
1. app_deafult.jpg
2. load_error_image.jpg