最近學習了挺多東西,所以想寫個demo把最近所學的全都用到,于是就有了這篇博客,這個demo實現的功能特別簡單,就是一個查詢天氣,并且放到textview及recyclerview上顯示出來,用的mvp設計框架,也是這兩天才稍微看懂的東西,如果有錯,還請各路大佬指出,那么廢話不多說,先上效果圖

從圖中能看出有以下功能:
1. 顯示PRogress2. 隱藏progress3. 請求數據成功或失敗彈出toast提示4. 將獲取到的數據設置到TextView上5. 將獲取到的數據設置到recyclerview上6. 如果查詢出錯清空所有的數據
確定了功能那么就是如何實現了,根據mvp的思想,V層只負責初始化view,并將view層接收到的數據(即EditText等)傳遞到P層,P層再傳遞給M層,讓M層操作數據,包括存儲和耗時操作等,然后M層再將數據傳回給P層,P層來更新UI,說得再明白點,mvp就是面向接口,那么m,v,p層的接口該怎么寫呢?很簡單,根據上面我們列出的功能來寫相應的接口,直接來看代碼:
public interface IMainActivity { //顯示progress void showProgress(); //隱藏progress void hideProgress(); /* * 設置文本 * @param temperaturestr:今日溫度,@param coldstr:今日建議,@param citystr:城市 * @param yesterdayflstr:昨日風力,@param yesterdayfxstr:昨日風向,@param yesterdayhighstr:昨日最高溫 * @param yesterdaylowstr:昨日最低溫,@param yesterdaytypestr:昨日天氣類型,@param yesterdaydatestr:昨日時間 * * */ void setText(String temperaturestr, String coldstr, String citystr, String yesterdayflstr, String yesterdayfxstr, String yesterdayhighstr, String yesterdaylowstr, String yesterdaytypestr, String yesterdaydatestr); //設置item void setItem(MyAdapter adapter); //Toast提示 void showMsg(String msg); //輸入錯誤時清空textview,recyclerview的item void clearAll();}那么現在V層,也就是Activity只需要實現這個接口即可,看代碼:public class MainActivity extends Activity implements IMainActivity { @BindView(R.id.temperature) TextView temperature; @BindView(R.id.rv) RecyclerView rv; @BindView(R.id.citykey) EditText citykey; @BindView(R.id.query) Button query; @BindView(R.id.progress) ProgressBar progress; private WeatherPresenter weatherPresenter; //初始化控件,并new出P層實例 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); weatherPresenter = new WeatherPresenter(this); } //點擊監聽,用P層實例去調用請求數據的接口 @OnClick(R.id.query) void query() { if (citykey.getText().toString().equals("")) { citykey.setError("不能為空"); } weatherPresenter.validateCredentials(citykey.getText().toString()); } @Override public void showProgress() { progress.setVisibility(View.VISIBLE); } @Override public void hideProgress() { progress.setVisibility(View.GONE); } @Override public void setText(String temperaturestr, String coldstr, String citystr, String yesterdayflstr, String yesterdayfxstr, String yesterdayhighstr, String yesterdaylowstr, String yesterdaytypestr, String yesterdaydatestr) { temperature.setText("城市:" + citystr + "/n" + "今日溫度:" + temperaturestr + "/n" + "今日建議:" + coldstr + "/n" + "昨日時間:" + yesterdaydatestr + "/n" + "昨日天氣:" + yesterdaytypestr + "/n" + "昨日最" + yesterdayhighstr + "/n" + "昨日最" + yesterdaylowstr + "/n" + "昨日風力:" + yesterdayflstr + "/n" + "昨日風向:" + yesterdayfxstr ); } //設置適配器,顯示recyclerview,adapter由P層傳回 @Override public void setItem(MyAdapter adapter) { rv.setLayoutManager(new LinearLayoutManager(this)); rv.setAdapter(adapter); } @Override public void showMsg(String msg) { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); } @Override public void clearAll() { rv.setAdapter(null); temperature.setText(""); }這里注意一下,我在onCreate方法中實例化了P層,原因就是要讓P層拿到V層的實例,這樣才能讓P層去操作UI,那么P層需要做哪些操作呢,前面說了,M層去操作數據,P層操作UI,再看看我們的效果圖,V層獲取到了EditText輸入的字符串,并且點擊查詢后獲取到了相應的數據,現在要做的,就是把V層獲取到的數據傳遞給M層,讓M層去做耗時操作,但是MVP模式,V層是不能直接跟M層進行交互的,怎么辦?很簡單,我們還有P層沒用到,P層的作用就是V層和M層的橋梁,讓V層把數據通過P層傳遞給M層即可,具體怎么做?我們再來分析一下,V層需要傳遞數據給M層,那么P層就需要一個方法,來接收V層的數據,這個方法就是點擊查詢按鈕后要執行的方法,那么P層現在要做的工作就很簡單了,let me show you code:public interface IWeatherPresenter { //請求數據 void validateCredentials(String citykey);}接下來在定義一個類去實現該接口//進行數據請求,請求時顯示progress @Override public void validateCredentials(String citykey) { view.showProgress(); model.getWeather(citykey, this); }有同學就要問了,model.getWeather又是從哪里來的方法,都是什么鬼,不要急,前面說了,P層需要一個方法來接收V層傳遞過來數據,然后才能讓P層把數據傳遞給M層,那model.getWeather肯定就是M層的方法了,沒錯,M層需要做耗時操作,而具體要查詢哪一個城市的天氣數據,就是從V層傳遞過來的,邏輯是不是一下就通了,再來分析下M層,M層需要的就是耗時操作,那么耗時操作就有兩種可能:1、請求成功
2、請求失敗
所以我們M層的接口寫成這樣:
public interface IWeatherModel { interface OnLinstener { //請求失敗 void onFailed(); //請求成功 void onSuccess(); } //請求數據 void getWeather(String citykey, OnLinstener linstener);}現在萬事具備,就剩個M層的類來實現這個接口就行了,但是,先暫停一下,看看我們的標題,基于retrofit+rxjava,前面一直沒用,當然就是在這個時候來用的,先給個完整的URL,現在來看看retrofit用來實現get請求的接口:
public interface APIService { @GET("/weather_mini") Observable<Bean> getApi(@Query("citykey") String citykey);}有同學要問,返回的Observable是個啥,這個是rxjava特有的,我能力不足,只限于能用的階段。。還不能很好的解釋其中的原理,有興趣的可以去看看這位大佬rxjava的系列文章,接下來是用于解析json的bean類,太長就不放了。。有興趣可以下載源碼看看,現在,要開始寫M層的邏輯了,M層就是用來操作數據的,說白了就是耗時操作,存儲數據,看代碼:public class WeatherModel implements IWeatherModel { private static final String TAG = "WeatherModel"; private Retrofit retrofit; private OkHttpClient client; private MyBean bean; //進行耗時操作,訪問數據 @Override public void getWeather(String citykey, final OnLinstener linstener) { Interceptor interceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request().newBuilder().build(); return chain.proceed(request); } }; client = new OkHttpClient.Builder().addInterceptor(interceptor).build(); retrofit = new Retrofit.Builder() .baseUrl("http://wthrcdn.etouch.cn") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .client(client) .build(); APIService api = retrofit.create(APIService.class); api.getApi(citykey) .subscribeOn(Schedulers.io())//在io線程執行 .observeOn(AndroidSchedulers.mainThread())//執行完成后回調給UI線程 .subscribe(new Observer<Bean>() { @Override public void onSubscribe(Disposable d) { Log.i(TAG, "onSubscribe: "); } @Override public void onNext(Bean value) { if (value.getDesc().equals("OK")) { bean = new MyBean(); bean.setWendu(value.getData().getWendu()); bean.setGanmao(value.getData().getGanmao()); bean.setCity(value.getData().getCity()); bean.setYesterdayfl(value.getData().getYesterday().getFl()); bean.setYesterdayfx(value.getData().getYesterday().getFx()); bean.setYesterdayhigh(value.getData().getYesterday().getHigh()); bean.setYesterdaylow(value.getData().getYesterday().getLow()); bean.setYesterdaytype(value.getData().getYesterday().getType()); bean.setYesterdaydate(value.getData().getYesterday().getDate()); bean.setList(value.getData().getForecast()); bean.setMsg("請求數據正確,請稍后"); linstener.onSuccess(); Log.i(TAG, "onNext: "); } else { bean = new MyBean(); bean.setMsg("請求數據錯誤,請重新輸入"); linstener.onFailed(); } } @Override public void onError(Throwable e) { linstener.onFailed(); Log.e(TAG, "onError: "); } @Override public void onComplete() { Log.i(TAG, "onComplete: "); } }); }我寫了另一個bean類來存儲所有數據,這樣P層才能直接把數據拿來用,但是在M層存儲好說,怎么在P層取出來呢,如果兩個類持有的不是同一個對象,那P層肯定是取不出數據的,很簡單,有什么需求就寫什么接口,P層需要M層存儲的bean類,那么我們在M層的接口再寫個getBean的方法,然后讓M層去實現不就行了,P層拿到M層的實例直接調用方法就可以拿到我們想要的bean類了,所以完整的M層接口代碼:public interface IWeatherModel { interface OnLinstener { //請求失敗 void onFailed(); //請求成功 void onSuccess(); } //請求數據 void getWeather(String citykey, OnLinstener linstener); //獲取bean類 MyBean getBean();}完整的M層代碼:public class WeatherModel implements IWeatherModel { private static final String TAG = "WeatherModel"; private Retrofit retrofit; private OkHttpClient client; private MyBean bean; //進行耗時操作,訪問數據 @Override public void getWeather(String citykey, final OnLinstener linstener) { Interceptor interceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request().newBuilder().build(); return chain.proceed(request); } }; client = new OkHttpClient.Builder().addInterceptor(interceptor).build(); retrofit = new Retrofit.Builder() .baseUrl("http://wthrcdn.etouch.cn") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .client(client) .build(); APIService api = retrofit.create(APIService.class); api.getApi(citykey) .subscribeOn(Schedulers.io())//在io線程執行 .observeOn(AndroidSchedulers.mainThread())//執行完成后回調給UI線程 .subscribe(new Observer<Bean>() { @Override public void onSubscribe(Disposable d) { Log.i(TAG, "onSubscribe: "); } @Override public void onNext(Bean value) { if (value.getDesc().equals("OK")) { bean = new MyBean(); bean.setWendu(value.getData().getWendu()); bean.setGanmao(value.getData().getGanmao()); bean.setCity(value.getData().getCity()); bean.setYesterdayfl(value.getData().getYesterday().getFl()); bean.setYesterdayfx(value.getData().getYesterday().getFx()); bean.setYesterdayhigh(value.getData().getYesterday().getHigh()); bean.setYesterdaylow(value.getData().getYesterday().getLow()); bean.setYesterdaytype(value.getData().getYesterday().getType()); bean.setYesterdaydate(value.getData().getYesterday().getDate()); bean.setList(value.getData().getForecast()); bean.setMsg("請求數據正確,請稍后"); linstener.onSuccess(); Log.i(TAG, "onNext: "); } else { bean = new MyBean(); bean.setMsg("請求數據錯誤,請重新輸入"); linstener.onFailed(); } } @Override public void onError(Throwable e) { linstener.onFailed(); Log.e(TAG, "onError: "); } @Override public void onComplete() { Log.i(TAG, "onComplete: "); } }); } //將保存的MyBean類返回給P層 @Override public MyBean getBean() { return this.bean; }}P層完整的代碼:public class WeatherPresenter implements IWeatherPresenter, IWeatherModel.OnLinstener { private IMainActivity view; private IWeatherModel model; private List<Bean.Data.forecast> list; //構造器,需要傳入v層實例,并new一個m層實例,以達到讓P層實現橋梁的作用 public WeatherPresenter(IMainActivity view) { this.view = view; this.model = new WeatherModel(); } //進行數據請求,請求時顯示progress @Override public void validateCredentials(String citykey) { view.showProgress(); model.getWeather(citykey, this); } //數據請求失敗時隱藏progress @Override public void onFailed() { view.clearAll(); MyBean bean = model.getBean(); view.showMsg(bean.getMsg()); view.hideProgress(); } //數據請求成功后將數據顯示在控件上,且在請求成功后隱藏progress @Override public void onSuccess() { MyBean bean = model.getBean(); if (bean.getMsg().equals("請求數據正確,請稍后")) { view.showMsg(bean.getMsg()); view.setText(bean.getWendu(), bean.getGanmao(), bean.getCity(), bean.getYesterdayfl(), bean.getYesterdayfx(), bean.getYesterdayhigh(), bean.getYesterdaylow(), bean.getYesterdaytype(), bean.getYesterdaydate()); list = bean.getList(); MyAdapter adapter = new MyAdapter((Context) view, list); view.setItem(adapter); view.hideProgress(); } }}recyclerview的adapter非常簡單,就不貼出來了,有興趣的話就下載源碼自己看看吧,歡迎各位大佬提出建議demo源碼
新聞熱點
疑難解答