MVP(面向接口編程)
1.爲了解耦,MVC中V-C耦合
- Model:Bean+業務邏輯
- View:Activity、Fragment
-
Presenter:起到橋樑作用,相當於經紀人的作用
-
登錄模塊:
- 先創建數據模型(bean),如登錄模塊的User(bean)
- 然後創建業務邏輯的接口,包含登錄的業務邏輯,包含所需的參數和回調接口(接口回調判斷登錄邏輯的結果處理),
- 然後創建具體的Model層實現類,實現該接口,處理具體的業務邏輯
-
package com.itheima.mvpdemo.model;
/**
* 登錄用戶
*/
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User() {
}
}
Model業務邏輯層: -
-
/**
* Model層:登錄業務邏輯
*/
public interface IUserLoginModel {
/**
* 登錄業務邏輯
* @param username 用戶名
* @param password 密碼
* @param listener 回調接口
*/
void login(String username, String password, OnUserLoginListener listener);
}
Model登錄回調接口層 -
-
package com.itheima.mvpdemo.model;
/**
* 登錄回調接口
*/
public interface OnUserLoginListener {
/**
* 登錄成功回調
* @param user
*/
void loginSuccess(User user);
/**
* 登錄失敗回調
*/
void loginFailed();
}
Model業務邏輯具體實現層
package com.itheima.mvpdemo.model;
import android.os.SystemClock;
/**
* Model層具體實現
*/
public class UserLoginModel implements IUserLoginModel {
@Override
public void login(final String username, final String password, final OnUserLoginListener listener) {
//模擬子線程
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(1500);
//登錄:判斷用戶名和密碼
if("heima27".equals(username) && "123456".equals(password)){//登錄成功
User user = new User(username, password);
listener.loginSuccess(user);
}else{//登錄失敗
listener.loginFailed();
}
}
}).start();
}
}
- (1)有什麼操作,該操作需要什麼?:1.獲取用戶名、 2.獲取密碼;
- (2)這些操作結果是什麼?對應的反饋是什麼?:1.登錄成功跳轉到主頁、 2.登錄失敗提示用戶;
- (3)有什麼用戶交互:1.顯示滾動框、 2.隱藏滾動框。
package com.itheima.mvpdemo.view;
import com.itheima.mvpdemo.model.User;
/**
* View層接口:Activity/Fragment實現該接口
* View層如何寫代碼?看頁面哪些是View,通過下面幾個方面分析
*
* 1.有哪些操作?獲取用戶名、獲取密碼
* 2.用哪些用戶交互(爲了提高用戶體驗):顯示滾動條、隱藏滾動條
* 3.上面的操作和用戶結合帶來上面結果(有什麼結果):成功跳轉主頁、失敗提示用戶
*/
public interface IUserLoginView {
/**獲取用戶名*/
String getUsername();
/**獲取密碼*/
String getPassword();
/**顯示滾動條*/
void showProgressbar();
/**隱藏滾動條*/
void hideProgressbar();
/**成功跳轉主頁*/
void jumpToMain(User user);
/**失敗提示用戶*/
void showLoginError();
}
Presenter層:Activity/Fragment初始化Presenter層對象,調用Presenter中M層和View層通信的方法,Presenter層需要考慮兩個問題:
-
1.既然P層是M和V的橋樑,那麼P層類中肯定有M和V層對象;
- 2.P層是橋樑,那麼肯定要提供方法,讓M層和V層在橋樑上“對話”。
-
其實也是主要看該功能有什麼操作
package com.itheima.mvpdemo.presenter;
import android.os.Handler;
import com.itheima.mvpdemo.model.IUserLoginModel;
import com.itheima.mvpdemo.model.OnUserLoginListener;
import com.itheima.mvpdemo.model.User;
import com.itheima.mvpdemo.model.UserLoginModel;
import com.itheima.mvpdemo.view.IUserLoginView;
/**
* presenter:起到橋樑作用
* 1.既然p層起到橋樑的作用:p層必須持有view層和model層對象,聯繫view和model
* 2.既然p層起到橋樑作用,p層必須提供橋樑方法
*/
public class UserLoginPresenter implements OnUserLoginListener {
private IUserLoginModel userLoginModel;//model層
private IUserLoginView userLoginView;//view層
/**
* 問題:兩個參數的構造方法對嗎?
* 1.p層是View層調用的,如果是兩個參數構造方法,說明view層持有model層對象,view-model耦合,不符合MVP架構
*
p層構造方法只能傳入View層對象,Model層對象是通過創建Model層的具體實現類來獲取的* @param userLoginView
*/
/*public UserLoginPresenter(IUserLoginModel model, IUserLoginView userLoginView) {
this.model = model;
this.userLoginView = userLoginView;
}*/
public UserLoginPresenter(IUserLoginView userLoginView) {
this.userLoginModel = new UserLoginModel();
this.userLoginView = userLoginView;
}
/**
* presenter層橋樑方法:model層和view層在該方法通信
*/
public void login(){
//1.顯示滾動條
userLoginView.showProgressbar();
//2.調用model層登錄業務:下面一行代碼集中體現了p層起到橋樑的作用
userLoginModel.login(userLoginView.getUsername(), userLoginView.getPassword(), this);
}
@Override
public void loginSuccess(final User user) {
//放到UI線程
mHandler.post(new Runnable() {
@Override
public void run() {
//1.隱藏滾動條
userLoginView.hideProgressbar();
//2.跳轉主頁
userLoginView.jumpToMain(user);
}
});
}
@Override
public void loginFailed() {
mHandler.post(new Runnable() {
@Override
public void run() {
//1.隱藏滾動條
userLoginView.hideProgressbar();
//2.提示用戶登錄失敗
userLoginView.showLoginError();
}
});
}
private Handler mHandler = new Handler();
}
public class LoginActivityMVP extends Activity implements OnClickListener, IUserLoginView {
private EditText etUsername;
private EditText etPassword;
private ProgressBar progressBar;
private Context context;
private Button btnLogin;
private UserLoginPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_activity_mvp);
context = this;
initViews();
btnLogin.setOnClickListener(this);
//創建presenter對象
presenter = new UserLoginPresenter(this);
}
private void initViews() {
etUsername = (EditText) findViewById(R.id.et_username);
etPassword = (EditText) findViewById(R.id.et_password);
progressBar = (ProgressBar) findViewById(R.id.progressBar1);
btnLogin = (Button) findViewById(R.id.btnLogin);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnLogin:
presenter.login();
break;
}
}
@Override
public String getUsername() {
return etUsername.getText().toString().trim();
}
@Override
public String getPassword() {
return etPassword.getText().toString().trim();
}
@Override
public void showProgressbar() {
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideProgressbar() {
progressBar.setVisibility(View.GONE);
}
@Override
public void jumpToMain(User user) {
//模擬跳轉
Toast.makeText(LoginActivityMVP.this, "登錄成功,跳轉主頁", Toast.LENGTH_SHORT).show();
}
@Override
public void showLoginError() {
Toast.makeText(LoginActivityMVP.this, "用戶名或者密碼錯誤,重新登錄!", Toast.LENGTH_SHORT).show();
}
}
gradle文件
說明:請求網絡使用async-http
package com.example.chuxiong.mvpdemo.bean;
/*
* @項目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.model
* @文件名: MainModelBean
* @創建者: chuxiong
* @創建時間: 2017/3/21 14:41
* @描述: TODO
*/
public class MainModelBean {
private static final String TAG = "MainModelBean";
private String city;
private String wd;
private String ws;
private String time;
public String getCity() {
return city+"\n";
}
public void setCity(String city) {
this.city = city;
}
public String getWd() {
return wd+"\n";
}
public void setWd(String wd) {
this.wd = wd;
}
public String getTime() {
return time+"\n";
}
public void setTime(String time) {
this.time = time;
}
public String getWs() {
return ws+"\n";
}
public void setWs(String ws) {
this.ws = ws;
}
}
package com.example.chuxiong.mvpdemo.model;
/*
* @項目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.model
* @文件名: IMainLoading
* @創建者: chuxiong
* @創建時間: 2017/3/21 15:50
* @描述: TODO
*/
public interface IMainLoading {
void load(IMainLoadingListener listener);
}
package com.example.chuxiong.mvpdemo.model;
import com.example.chuxiong.mvpdemo.bean.MainModelBean;
/*
* @項目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.model
* @文件名: IMainLoadingListener
* @創建者: chuxiong
* @創建時間: 2017/3/21 15:51
* @描述: TODO
*/
public interface IMainLoadingListener {
void loadDataSuccess(MainModelBean mainModelBean);
void loadDataFailure();
}
package com.example.chuxiong.mvpdemo.model;
import com.example.chuxiong.mvpdemo.bean.MainModelBean;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.JsonHttpResponseHandler;
import org.json.JSONException;
import org.json.JSONObject;
import cz.msebera.android.httpclient.Header;
/*
* @項目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.model
* @文件名: MainLoadModel
* @創建者: chuxiong
* @創建時間: 2017/3/21 15:53
* @描述: TODO
*/
public class MainLoadModel
implements IMainLoading
{
@Override
public void load(final IMainLoadingListener listener) {
AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
asyncHttpClient.get("http://www.weather.com.cn/adat/sk/101010100.html",
new JsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode,
Header[] headers,
JSONObject response)
{
super.onSuccess(statusCode, headers, response);
try {
MainModelBean mainModelBean = new MainModelBean();
JSONObject weatherinfo = response.getJSONObject(
"weatherinfo");
mainModelBean.setCity(weatherinfo.optString("city"));
mainModelBean.setWd(weatherinfo.optString("WD"));
mainModelBean.setWs(weatherinfo.optString("WS"));
mainModelBean.setTime(weatherinfo.optString("time"));
//加載成功
listener.loadDataSuccess(mainModelBean);
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(int statusCode,
Header[] headers,
Throwable throwable,
JSONObject errorResponse)
{
super.onFailure(statusCode, headers, throwable, errorResponse);
//加載失敗
listener.loadDataFailure();
}
});
}
}
package com.example.chuxiong.mvpdemo.presenter;
/*
* @項目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.presenter
* @文件名: IPresenter
* @創建者: chuxiong
* @創建時間: 2017/3/21 16:04
* @描述: TODO
*/
public interface IPresenter<V> {
void attachView(V view);
void detachView();
}
-
1.既然P層是M和V的橋樑,那麼P層類中肯定有M和V層對象;
- 2.P層是橋樑,那麼肯定要提供方法,讓M層和V層在橋樑上“對話”。
package com.example.chuxiong.mvpdemo.presenter;
import android.os.Handler;
import com.example.chuxiong.mvpdemo.model.IMainLoading;
import com.example.chuxiong.mvpdemo.model.IMainLoadingListener;
import com.example.chuxiong.mvpdemo.model.MainLoadModel;
import com.example.chuxiong.mvpdemo.bean.MainModelBean;
import com.example.chuxiong.mvpdemo.view.IMainView;
/*
* @項目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.presenter
* @文件名: MainPresenter
* @創建者: chuxiong
* @創建時間: 2017/3/21 16:02
* @描述: TODO
*/
public class MainPresenter
implements IPresenter<IMainView>, IMainLoadingListener
{
private static final String TAG = "MainPresenter";
private IMainView mIMainView;//view層
private IMainLoading mIMainLoading;//model層
private Handler mHandler = new Handler();
public MainPresenter(IMainView IMainView) {
attachView(IMainView);
this.mIMainLoading = new MainLoadModel();
}
@Override
public void attachView(IMainView view) {
this.mIMainView = view;
}
@Override
public void detachView() {
this.mIMainView = null;
}
/**
* presenter層橋樑方法:model層和view層在該方法通信
*/
public void loadData() {
mIMainView.showProgress();
//製造延遲效果
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mIMainLoading.load(MainPresenter.this);
}
},3000);
}
@Override
public void loadDataSuccess(MainModelBean mainModelBean) {
mIMainView.showData(mainModelBean);
mIMainView.hideProgress();
}
@Override
public void loadDataFailure() {
mIMainView.hideProgress();
mIMainView.showLoginError();
}
}
- (1)有什麼操作:1.加載數據;
- (2)這些操作結果是什麼:1.加載成功,顯示信息、 2.加載失敗提示用戶;
- (3)有什麼用戶交互:1.顯示滾動框、 2.隱藏滾動框。
package com.example.chuxiong.mvpdemo.view;
import com.example.chuxiong.mvpdemo.bean.MainModelBean;
/*
* @項目名: MVP Demo
* @包名: com.example.chuxiong.mvpdemo.view
* @文件名: IMainView
* @創建者: chuxiong
* @創建時間: 2017/3/21 14:39
* @描述: TODO
*/
public interface IMainView {
void showData(MainModelBean mainModelBean);
void showProgress();
void hideProgress();
/**失敗提示用戶*/
void showLoginError();
}
package com.example.chuxiong.mvpdemo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.example.chuxiong.mvpdemo.bean.MainModelBean;
import com.example.chuxiong.mvpdemo.presenter.MainPresenter;
import com.example.chuxiong.mvpdemo.view.IMainView;
public class MainActivity extends AppCompatActivity implements IMainView, View.OnClickListener {
private Button mBtnLoad;
private TextView mTv;
private ProgressBar mProgressBar;
private MainPresenter mMainPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
mBtnLoad = (Button) findViewById(R.id.load_btn);
mTv = (TextView) findViewById(R.id.tv);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mBtnLoad.setOnClickListener(this);
mMainPresenter = new MainPresenter(this);
}
@Override
public void showData(MainModelBean mainModelBean) {
String showData = getResources().getString(R.string.city) + mainModelBean.getCity()
+ getResources().getString(R.string.wd) + mainModelBean.getWd()
+ getResources().getString(R.string.ws) + mainModelBean.getWs()
+ getResources().getString(R.string.time) + mainModelBean.getTime();
mTv.setText(showData);
}
@Override
public void showProgress() {
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideProgress() {
mProgressBar.setVisibility(View.GONE);
}
@Override
public void showLoginError() {
Toast.makeText(this,"加載失敗",Toast.LENGTH_SHORT).show();
}
@Override
public void onClick(View v) {
/* new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mMainPresenter.loadData();
}
}, 5000);*/
mMainPresenter.loadData();
}
@Override
protected void onDestroy() {
super.onDestroy();
mMainPresenter.detachView();
}
}