編輯:關於Android編程
MVP模式是當前比較主流的框架,主要是由它的優點來決定的吧。本文結合了MVP+Retrofit+RxJava三大主流框架(MVP應該叫模式吧)寫了一個demo【裡面從簡單的“登錄”例子,之後到“IP地址查詢”,再到相對貼近實際項目的“獲取豆瓣TOP250的電影”】,一開始可能會覺得不容易理解,但是真正理解了之後,開發、維護就不是那麼痛苦的事情了。
讀這篇文章之前,你需要了解的知識有:
1、MVP【點這裡看百度百科介紹】
2、Retrofit 2.0
3、RxJava(點擊這裡看—不錯的一個Retrofit+RxJava結合的例子)
4、Butterknife的使用
5、狀態欄一體化(android狀態欄一體化)
6、CallBack回調思想(說白了就是:成功還是失敗了,你總的告訴我一聲,我好通知V層來處理[這裡可以不用回調,在代碼中使用EventBus或者傳遞一個Handler過來也可以,不建議這樣操作])
1、MVP 高度解耦 提高代碼質量
2、Retrofit+RxJava最強的網絡訪問組合
3、功能易擴展 易維護 易測試
豆瓣TOP250的電影 https://api.douban.com/v2/movie/top250?start=0&count=10
查詢IP地址 http://ip.taobao.com/service/getIpInfo.php?ip=1.1.1.1
1)、MVP的優點:
1、模型與視圖完全分離,我們可以修改視圖而不影響模型
2、可以更高效地使用模型,因為所有的交互都發生在一個地方——Presenter內部
3、我們可以將一個Presenter用於多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因為視圖的變化總是比模型的變化頻繁。
4、如果我們把邏輯放在Presenter中,那麼我們就可以脫離用戶接口來測試這些邏輯(單元測試)【以上優點參考了百度百科的“MVP模式“】
2)、以登錄為例的MVP自理解圖
用熟悉之後你會發現,網絡訪問就是這麼簡單,這麼快.優點我就不一一說了,網上太多的介紹.
不多說,先來看效果圖
分包有很多種分法,我這裡用了我常用的分包方法,僅供參考。 Demo閱讀指南 :先看登錄的Demo(入門),理解之後再看IP地址查詢(深入),最後看獲取豆瓣TOP250的電影(淺出) 布局簡單就不貼了 為什麼要使用接口,之前已經說過了,這裡之所以寫在一個類中,也是為了便於管理,不至於項目看上去有太多的類。 展示數據,處理用戶交互 這樣看來,View層是不是就少寫了很多代碼了! 獲取V層數據,交給M層處理,將處理結果通知給V層 * (看例子之前看一遍下面的直白的解釋,看完之後再看一遍就更明白MVP模式了) * --------V層 負責響應用戶的交互(獲取數據---->提示操作結果) * --------P層 傳遞完數據給M層處理之後,實例化回調對象,成功了就通知V層登錄成功,失敗了就通知V層顯示錯誤信息 * --------M層 對P層傳遞過來的userInfo進行登錄(網絡請求)判斷,處理完成之後將處理結果回調給P層 * Created by HDL on 2016/7/22. */ public class LoginPresenter implements LoginContract.ILoginPresenter { private LoginContract.ILoginView mLoginView; private LoginContract.ILoginModel mLoginModel; public LoginPresenter(LoginContract.ILoginView mLoginView) { this.mLoginView = mLoginView; mLoginModel = new LoginModel(); } /** * 登錄 */ @Override public void login() { mLoginView.showProgress(); //顯示登錄進度條 final int i = 0; mLoginModel.login(mLoginView.getUserLoginInfo(), new OnHttpCallBack 具體的業務處理 Retrofit使用注意事項,baseurl要以“/”結束 在Service中Get(“—”)/Post(“—-“)不能以“/”開始 為了減少文章的篇幅這裡就不貼查詢IP的了:如果你對這個demo感興趣,請下載demo去自己看,代碼裡面的注釋自認為挺清楚的.(Demo下載地址在最後) 總布局: item布局比較簡單就不貼了,需要的可到demo中查看 有一些簡單的代碼就沒有貼出來,可以下demo去看一下 這是demo的GitHub的地址 https://github.com/huangdali/newkjdemo/ 代碼有誤的地方請多多指出,共同進步,有問題的請留言、評論。
說明<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPqO6YWRhcHRlciZuZGFzaDsmZ3Q7ysrF5Mb3tcTA4KOsYmFzZSZuZGFzaDsmZ3Q7u/m0obXExeTWw8Dgo6xiZWFuJm5kYXNoOyZndDvKtczlttTP86OsZXhjZXB0aW9uJm5kYXNoOyZndDvS7LOjtKbA7bbUz/OjrGh0dHAmbmRhc2g7Jmd0O834wufP4LnYo6x1aSZuZGFzaDsmZ3Q7y/nT0LXE0rPD5qOosPzAqGZyYWdtZW50ob4vdWkvZnJhZ21lbnShv6Opo6x1dGlscyZuZGFzaDsmZ3Q7uaS+38Dgo6x3aWRnZXQmbmRhc2g7Jmd0O9fUtqjS5b/YvP7A4KGjPC9wPg0KPGg0IGlkPQ=="33其他說明">3.3、其他說明
使用第三方庫說明 :為了快速寫出這個demo,在一些方便走了捷徑(有時間的可以自己用原生的寫法),比如RecycleView的適配器,我就用了鴻洋大神的通用適配器(CommonAdapter),還有使用Butterknife來代替findviewbyid,但是看過郭霖大神寫的性能優化一篇博客,說是實際開發中最好慎重使用依賴注入,會降低app的性能和效率,我在這裡為了方便就直接使用了.四、登錄demo
4.1、布局
4.2、接口類
/**
* 登錄的關聯類
* Created by HDL on 2016/7/22.
*/
public class LoginContract {
/**
* V視圖層
*/
interface ILoginView {
Context getCurContext();//獲取上下文對象,用於保存數據等
void showProgress();//可以顯示進度條
void hideProgress();//可以隱藏進度條
void showInfo(String info);//提示用戶,提升友好交互
void showErrorMsg(String msg);//發生錯誤就顯示錯誤信息
void toMain();//跳轉到主頁面
void toRegister();//跳轉到注冊頁面
UserInfo getUserLoginInfo();//獲取用戶登錄信息
}
/**
* P視圖與邏輯處理的連接層
*/
interface ILoginPresenter {
void login();//橋梁就是登錄了
}
/**
* 邏輯處理層
*/
interface ILoginModel {
void login(UserInfo userInfo, OnHttpCallBack
4.3、View層(LoginActivity)
/**
* 登錄頁面
* (看例子之前看一遍下面直白的解釋,看完之後再看一遍就更明白MVP模式了)
* --------M層 對P層傳遞過來的userInfo進行登錄(網絡請求)判斷,處理完成之後將處理結果回調給P層
* --------P層 傳遞完數據給M層處理之後,實例化回調對象,成功了就通知V層登錄成功,失敗了就通知V層顯示錯誤信息
* --------V層 負責響應用戶的交互(獲取數據---->提示操作結果)
*/
public class LoginActivity extends BaseActivity implements LoginContract.ILoginView {
//省略ButterKnife綁定頁面控件的代碼
private LoginPresenter mLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.inject(this);
tvTitle.setText("用戶登錄");
mLoginPresenter = new LoginPresenter(this);
}
@Override
public Context getCurContext() {
return mActivity;
}
@Override
public void showProgress() {
pbProgress.setVisibility(View.VISIBLE);
}
@Override
public void hideProgress() {
pbProgress.setVisibility(View.GONE);
}
@Override
public void showInfo(String info) {
ToastUtils.showToast(mActivity, info);
}
@Override
public void showErrorMsg(String msg) {
ToastUtils.showToast(mActivity, msg);
}
@Override
public void toMain() {
startActivity(new Intent(mActivity, QueryIPActivity.class));
}
@Override
public void toRegister() {
startActivity(new Intent(mActivity, RegisterActivity.class));
}
@Override
public UserInfo getUserLoginInfo() {
return new UserInfo(etLoginUserName.getText().toString().trim(), etLoginPwd.getText().toString().trim());
}
public void onRegister(View view) {
toRegister();
}
@OnClick({R.id.iv_title_back, R.id.btn_login})
public void onClick(View view) {
switch (view.getId()) {
case R.id.iv_title_back:
finish();
break;
case R.id.btn_login:
mLoginPresenter.login();
break;
}
}
}
4.4、Presenter層
/**
* 登錄的橋梁(View和Model的橋梁)-------P通過將V拿到的數據交給M來處理 P又將處理的結果回顯到V
* 說白點就是P中new出M和V 然後通過調用它們兩個的方法來完成操作
*
4.5、Model層
/**
* 登錄的業務處理類
* Created by on 2016/7/22.
*/
public class LoginModel implements LoginContract.ILoginModel {
/**
* 登錄的具體業務處理--------網絡請求都在這裡做喽
* --------V層 負責響應用戶的交互(獲取數據---->提示操作結果)
* --------P層 傳遞完數據給M層處理之後,實例化回調對象,成功了就通知V層登錄成功,失敗了就通知V層顯示錯誤信息
* --------M層 對P層傳遞過來的userInfo進行登錄(網絡請求)判斷,處理完成之後將處理結果回調給P層
*
* @param userInfo P層傳遞過來的數據
* @param callBack P層傳遞過來的回調對象----------說白了就是成功還是失敗了,你總的告訴我一聲,我好通知V層來處理[這裡可以不用回調,在代碼中使用EventBus或者傳遞一個Handler過來也可以,不建議這樣操作]
*/
@Override
public void login(final UserInfo userInfo, final OnHttpCallBack
4.6、Retofit網絡請求工具
/**
* Retofit網絡請求工具類
* Created by HDL on 2016/7/29.
*/
public class RetrofitUtils {
private static final int READ_TIMEOUT = 60;//讀取超時時間,單位 秒
private static final int CONN_TIMEOUT = 12;//連接超時時間,單位 秒
private static Retrofit mRetrofit;
private RetrofitUtils() {
}
public static Retrofit newInstence(String url) {
mRetrofit = null;
OkHttpClient client = new OkHttpClient();//初始化一個client,不然retrofit會自己默認添加一個
client.setReadTimeout(READ_TIMEOUT, TimeUnit.MINUTES);//設置讀取時間為一分鐘
client.setConnectTimeout(CONN_TIMEOUT, TimeUnit.SECONDS);//設置連接時間為12s
mRetrofit = new Retrofit.Builder()
.client(client)//添加一個client,不然retrofit會自己默認添加一個
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return mRetrofit;
}
}
4.7、APIService公共請求接口類
/**
* API--接口 服務[這裡處理的是同一的返回格式 resultCode resultInfo Data
五、IP查詢
六、獲取豆瓣TOP250的電影
6.1、效果圖
6.2、布局
6.3、接口類
/**
* 獲取豆瓣top250的關聯類---------這裡主要是抽出MVP中三層的接口,好處就是改需求很方便,使得代碼結構更加清晰
* Created by HDL on 2016/7/22.
*/
public class MovieContract {
/**
* V視圖層
*/
interface IMovieView {
void showBottom(int lastIndex);
Context getCurContext();//獲取上下文對象
void showProgress();//顯示進度條
void hideProgress();//隱藏進度條
void showData(List
6.4、View層
/**
* 豆瓣排名前250名
*/
public class MovieActivity extends BaseActivity implements MovieContract.IMovieView {
MoviePresenter mMoviePresenter;
@InjectView(R.id.tv_title)
TextView tvTitle;
private ProgressDialog mProgressDialog;
@InjectView(R.id.rv_movie_list)
RecyclerView rvMovieList;
@InjectView(R.id.store_house_ptr_frame)
PtrFrameLayout storeHousePtrFrame;
private TextView load_more;//加載更多的按鈕
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
mMoviePresenter = new MoviePresenter(this);
mMoviePresenter.getMovie();//啟動軟件時默認加載
}
/**
* 初始化布局
*/
private void initView() {
setContentView(R.layout.activity_movie);
ButterKnife.inject(this);
tvTitle.setText("豆瓣Top250的電影");
initPtr();
rvMovieList.setLayoutManager(new LinearLayoutManager(mActivity));//設置為listview的布局
rvMovieList.setItemAnimator(new DefaultItemAnimator());//設置動畫
rvMovieList.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));//添加分割線
}
/**
* 初始化(配置)下拉刷新組件
*/
private void initPtr() {
//下面是一些基礎的配置,直接拿來用就可以 不用深究
storeHousePtrFrame.setResistance(1.7f);
storeHousePtrFrame.setRatioOfHeaderHeightToRefresh(1.2f);
storeHousePtrFrame.setDurationToClose(200);
storeHousePtrFrame.setDurationToCloseHeader(1000);
storeHousePtrFrame.setPullToRefresh(false);
storeHousePtrFrame.setKeepHeaderWhenRefresh(true);
StoreHouseHeader header = new StoreHouseHeader(this);
float scale = getResources().getDisplayMetrics().density;
header.setPadding(0, (int) (15 * scale + 0.5f), 0, (int) (15 * scale + 0.5f));
header.initWithString("HDL");//自定義頭顯示的字樣,設置圖片的話看另外的api
header.setTextColor(Color.RED);
header.setBackgroundColor(Color.parseColor("#11000000"));
storeHousePtrFrame.setHeaderView(header);//添加頭
storeHousePtrFrame.addPtrUIHandler(header);//同時也要加上這一句
storeHousePtrFrame.setPtrHandler(new PtrHandler() {
@Override
public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
return PtrDefaultHandler.checkContentCanBePulledDown(frame, content, header);
}
@Override
public void onRefreshBegin(PtrFrameLayout frame) {
mMoviePresenter.loadMoreMovie();//下拉刷新的時候加載更多數據
frame.postDelayed(new Runnable() {
@Override
public void run() {
storeHousePtrFrame.refreshComplete();
}
}, 150);//為了增加用戶體驗 延遲0.15s再通知刷新結束
}
});
}
@Override
public void showBottom(int lastIndex) {
load_more.setText("點擊加載更多");//設置最底下的加載更多顯示的內容 加載中-->點擊加載更多
rvMovieList.scrollToPosition(lastIndex);
}
@Override
public Context getCurContext() {
return this;
}
@Override
public void showProgress() {
mProgressDialog = ProgressDialog.show(this, "提示", "正在獲取中,請稍後...");
}
@Override
public void hideProgress() {
mProgressDialog.hide();
}
/**
* 顯示數據
*
* @param movies
*/
@Override
public void showData(List
6.5、Presenter層
/**
* P層
* Created by HDL on 2016/8/3.
*/
public class MoviePresenter implements MovieContract.IMoviePresenter {
MovieContract.IMovieModel mIMovieModel;//M層
MovieContract.IMovieView mIMovieView;//V層
public int start = 0;//從第幾個開始
public int count = 4;//請求多少個
List
6.6、Model層
/**
* 具體的邏輯(業務)處理了
* Created by HDL on 2016/8/3.
*/
public class MovieModel implements MovieContract.IMovieModel {
@Override
public void getMovie(int start, int count, final OnHttpCallBack
七、Demo下載
屬性動畫簡介Android開發過程中,適當的使用一些動畫可以讓自己的應用看起來更棒更炫。最初的時候Google為了實現動畫,主要提供了兩種基本動畫:幀動畫:將一個完整的動
周末 特地把Android SwipeMenuListView(滑動菜單)的知識資料整理一番,以下是整理內容:SwipeMenuListView(滑動菜單)A swipe
很多應用為了節省空間而又使界面能夠充足的顯示信息,大多數應用都采用了側邊欄的方式,如下圖: 來說說它
第一次接觸樹形ListView是一年前,當時公司做的項目是一個企業的員工管理軟件,在展示員工時用到的,我花了大半天時間,才把樹形ListView搞明白,完成任務後就沒有然