前言
MVP是一种MVC的变形,相比MVC它大大减少了Activity的责任将主要的逻辑操作集中在Presenter层中,从而使得MVP的代码结构变得更清晰。
MVP结构
MVP主要由Model、View、Presenter三部分构成。
Model:负责数据的处理,即数据的获取、存储、解析、分发、修改等操作;
View:即Activity和Fragment,负责页面的绘制,刷新;
Presenter:负责连接Model层和View层,从Model中获取数据然后控制View刷新页面,是主要逻辑操作的地方。
这里借用此文的一张图片来表示Model、View和Presenter三者的关系
MVP案例
这是一个简单的菜谱App,主要包括列表和详情,下面以列表页威力展示一下MVP的运用。
列表页视图:
项目结构:
(1)Model层
这里的Model主要负责从网络获取数据
public interface ITypeListModel {
void loadList(String url, Map<String,String> paraMap, TypeListListener listener);
void cancle();
interface TypeListListener{
void loadSuccess(String json,Map<String,String> hMap);
void loadFail(Exception e,Map<String,String> hMap);
}
}
public class TypeListModel implements ITypeListModel {
private RequestCall call;
@Override
public void loadList(String url, final Map<String, String> paraMap, final ITypeListModel.TypeListListener listener) {
call= OkHttpUtils.post()
.url(url)
.params(paraMap)
.build();
call.execute(new StringCallback() {
@Override
public void onError(Call call, Exception e, int id) {
listener.loadFail(e,paraMap);
}
@Override
public void onResponse(String response, int id) {
listener.loadSuccess(response,paraMap);
}
});
}
@Override
public void cancle() {
if(call!=null)
call.cancel();
}
}
(2)View层
View层管理点击页面上控件的状态和控件的点击事件
我将View和Presenter的两个接口都放在了Contract中方便管理
interface View{
void showLoading();
void hideLoading();
void showLoadFail();
void hideLoadFail();
void showContent();
void hideContent();
void toastMessage(String message);
void setContentData(List<FoodTypeGroup> list);
}
public class FoodTypeActivity extends AppCompatActivity implements FoodTypeContract.View, View.OnClickListener, ExpandableListView.OnChildClickListener {
@Bind(R.id.tv_head_back)
View tvBack;
@Bind(R.id.tv_head_title)
TextView tvTitle;
@Bind(R.id.elv_content)
ExpandableListView elvContent;
@Bind(R.id.progressBar)
View progressBar;
@Bind(R.id.tv_fail)
View tvFail;
private FoodTypeAdapter adapter;
private List<FoodTypeGroup> adapterList;
private FoodTypeContract.Presenter foodTypePresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_food_type);
ButterKnife.bind(this);
foodTypePresenter=new FoodTypePresenter(this);
initView();
initAdapter();
foodTypePresenter.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
foodTypePresenter.onDestroy();
}
private void initView() {
tvBack.setVisibility(View.GONE);
tvTitle.setText("菜品分类");
tvFail.setOnClickListener(this);
elvContent.setOnChildClickListener(this);
}
private void initAdapter(){
adapterList=new ArrayList<>();
adapter=new FoodTypeAdapter(this,adapterList);
elvContent.setAdapter(adapter);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.tv_fail://重新加载
foodTypePresenter.start();
break;
}
}
@Override
public boolean onChildClick(ExpandableListView expandableListView, View view, int i, int i1, long l) {
Intent intent=new Intent(this,TypeListActivity.class);
intent.putExtra("id",adapterList.get(i).getList().get(i1).getId());
intent.putExtra("type",adapterList.get(i).getList().get(i1).getName());
startActivity(intent);
return true;
}
@Override
public void showLoading() {
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
progressBar.setVisibility(View.GONE);
}
@Override
public void showLoadFail() {
tvFail.setVisibility(View.VISIBLE);
}
@Override
public void hideLoadFail() {
tvFail.setVisibility(View.GONE);
}
@Override
public void showContent() {
elvContent.setVisibility(View.VISIBLE);
}
@Override
public void hideContent() {
elvContent.setVisibility(View.GONE);
}
@Override
public void toastMessage(String message){
ToastUtil.showToast(this,message);
}
@Override
public void setContentData(List<FoodTypeGroup> list) {
adapterList.clear();
adapterList.addAll(list);
adapter.notifyDataSetChanged();
}
}
(3)Presenter层
从View中接收到获取列表请求后调用Model获取列表数据,根据列表数据的状态控制View作不同的显示
interface Presenter extends BasePresenter{
@Override
void start();
@Override
void onDestroy();
}
public class FoodTypePresenter implements FoodTypeContract.Presenter{
private FoodTypeContract.View foodTypeView;
private IFoodTypeModel iFoodTypeModel;
public FoodTypePresenter(FoodTypeContract.View foodTypeView){
this.foodTypeView=foodTypeView;
iFoodTypeModel=new FoodTypeModel();
}
@Override
public void start() {
foodTypeView.hideContent();
foodTypeView.hideLoadFail();
foodTypeView.showLoading();
final Map<String,String> paraMap=new HashMap<>();
paraMap.put("key",BaseUrl.KEY);
iFoodTypeModel.loadList(BaseUrl.FOODTYPE, paraMap, new IFoodTypeModel.FoodTypeListener() {
@Override
public void loadSuccess(String json) {
LoadSuccess.onSuccess(json, new LoadSuccess.LoadSuccessCallBack() {
@Override
public void code200(JSONObject json) throws JSONException {
if(foodTypeView!=null) {
List<FoodTypeGroup> list = JSON.parseArray(json.getJSONArray("result").toString(), FoodTypeGroup.class);
foodTypeView.hideLoading();
foodTypeView.showContent();
foodTypeView.setContentData(list);
}
}
@Override
public void codeOther(String errorMessage) {
if(foodTypeView!=null) {
foodTypeView.hideLoading();
foodTypeView.showLoadFail();
foodTypeView.toastMessage(errorMessage);
}
}
});
}
@Override
public void loadFail(Exception e) {
L.e("NET",e.getMessage());
if (foodTypeView!=null) {
foodTypeView.hideLoading();
foodTypeView.showLoadFail();
foodTypeView.toastMessage("网络连接异常");
}
}
});
}
@Override
public void onDestroy(){
iFoodTypeModel.cancle();
foodTypeView=null;
}
}