背景:
之前看到有朋友在项目中用mvp模式,他推荐我去看看google托管在github上的一个demo,然后我就下载下来看,结果是看得稀里糊涂的,感觉几个接口调来调去的,让我不够用的大脑更加混乱了。然后,我又在网上搜索一些文章看,结果绝大部分文章标题是:Retrofit+RxJava+Dragger2+MVP模式,一下子包装这么多框架进去,更是让看了之后,更是不会了。后面自己想:既然整体看不懂,为什么不一个一个的看呢?那就先只看纯MVP的demo,后面的Retrofit,RxJava, Dragger2排队一个一个来学习好了。在化繁为简的轻情况下,终于对MVP有所理解,下面我讲解下自己当时学习的过程,一是加深自己的理解,二是希望别人也能容易看懂。我会便用2个例子来构建,见如下结构:
案例一:
在MainActivty里面做用户登录的操作:
view: IUserLoginView MainActivity
module: IUser(接口), IUserImpl(接口的实现类)OnLoginListener(登录结果的回调)
presenter: UserLoginPresenter
1.先说module层要写些什么:大家对这个可能有点疑惑,因为我当初也是不知道这个moudle到底该写些什么好,只知道概念上说:业务逻辑在这里做。后面思索:在没有MVP的时候,我们要从网络上获取数据,或是将用户数据传递给服务端,不就是具体的业务逻辑吗?只不过以前是写在一个类中,现在将其划分给module就好了。以注册和登录为例,我们要将用户数据交给服务端,那么,我猜想你应该知道要怎么写了,参考如下:
public interface IUser {
void login(String name,String psw,OnLoginListener loginListener);
}
/**
* 登录的具体业务类
*/
public class IUserImpl implements IUser{
@Override
public void login(String name, String psw, OnLoginListener loginListener) {
if("123".equals(name)&&"123".equals(psw)){
User user = new User(name,psw);
loginListener.onloginSucced(user);
}else{
loginListener.onloginFailed();
}
}
}
2.再来看view层怎么写:看这个的时候,又难倒我了,再次回想没有MVP的时候,我们要拿到拿到view上的内容如用户的姓名和密码,登录之后页面要跳转,吐司提示等。所以,和view相关的,界面展示相关的,就都写在这里,另外,也不怕写漏掉,接口编程,想往里面添加就添加。
/**
* 只和view相关的逻辑
*/
public interface IUserLoginView {
String getUserName();
String getPassword();
void toDetailActivity(User user);
}
3.最后是Module层:连接view和module层,将module中获取的数据设置在view上,完成交互。(我的理解是:presenter调view和module完成交互)。
/**
* presenter 主要看要完成什么样的交互
*
*/
public class UserLoginPresenter {
//1. 登录:
private IUser iUser;
private IUserLoginView userLoginView;
public UserLoginPresenter(IUserLoginView view){
this.userLoginView = view;
this.iUser = new IUserImpl();
}
//登录过程:
public void login(){
iUser.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() {
@Override
public void onloginSucced(User user) {
userLoginView.toDetailActivity(user);
}
@Override
public void onloginFailed() {
}
});
}
}
4.最后看MainActivity如何做登录处理:
public class MainActivity extends AppCompatActivity implements IUserLoginView {
private EditText et_name;
private EditText et_psw;
private Button btn_login;
//调用Presenter:
private UserLoginPresenter presenter = new UserLoginPresenter(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
et_name = (EditText) findViewById(R.id.et_name);
et_psw = (EditText) findViewById(R.id.et_psw);
btn_login = (Button) findViewById(R.id.btn_login);
btn_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//View 要做的事情:交给presenter去做:
presenter.login();
}
});
}
@Override
public String getUserName() {
return et_name.getText().toString();
}
@Override
public String getPassword() {
return et_psw.getText().toString();
}
@Override
public void toDetailActivity(User user) {
Intent intent = new Intent(this,DetailActivity.class);
startActivity(intent);
}
}
//布局:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mvptest.view.MainActivity"
android:orientation="vertical"
>
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="40dp" />
<EditText
android:id="@+id/et_psw"
android:layout_width="match_parent"
android:layout_height="40dp"
/>
<Button
android:id="@+id/btn_login"
android:layout_width="match_parent"
android:layout_height="50dp" />
</LinearLayout>
5.整个调用过程如下: activity中点击登录按钮的时候,调用presenter中的login方法,而presenter中的login又是调用module中的login,module中login方法需要的参数来源又是通过view中的方法得来。
好了,第一个用MVP构建的登录页面完成了,现在我们再接着练习下一个例子:在recycleview页面中如何用MVP来做:
案例二:
view: IDetailView
module:IDetailData , IDetailDataImpl
presenter: DetailDataPresenter
bean: DetailDataBean(只有name,age,addresss三个字段)
1. moudle层:获取数据
//接口类:
public interface IDetailData {
ArrayList<DetailDataBean> getAllDetailData();
}
//接口的实现类:
public class IDetailDataImpl implements IDetailData {
@Override
public ArrayList<DetailDataBean> getAllDetailData() {
ArrayList<DetailDataBean> list = new ArrayList<>();
for(int i = 0;i<20;i++){
DetailDataBean bean = new DetailDataBean("name"+i,i,"address"+i);
list.add(bean);
}
return list;
}
}
2. view层:
public interface IDetailView {
void setAdapter(ArrayList<DetailDataBean> lists);
}
3.presenter层:
/**
*
* view 和 module 层的交互:
*/
public class DetailDataPresenter {
private IDetailData iDetailData;//数据源
private IDetailView iDetailView;//将其绑定到activity
public DetailDataPresenter(DetailActivity view){
this.iDetailData = new IDetailDataImpl();
this.iDetailView = view;
}
public void getData(){
// iDetailData.getAllDetailData();
//view层做业务:
iDetailView.setAdapter(iDetailData.getAllDetailData());
}
}
4.Activity:
public class DetailActivity extends AppCompatActivity implements IDetailView {
private RecyclerView mRecyclerView;
private DetailDataPresenter presenter = new DetailDataPresenter(this);
private HomeAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
initView();
presenter.getData();
}
private void initView() {
mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
}
@Override
public void setAdapter(ArrayList<DetailDataBean> lists) {
mAdapter = new HomeAdapter(lists);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mAdapter);
}
public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>{
private ArrayList<DetailDataBean> mData;
public HomeAdapter(ArrayList<DetailDataBean> lists) {
this.mData = lists;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder holder = new MyViewHolder(LayoutInflater.from(DetailActivity.this).inflate(R.layout.item_home, parent,
false));
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.tv1.setText(mData.get(position).getName());
holder.tv2.setText(mData.get(position).getAge()+"");
holder.tv3.setText(mData.get(position).getAddress());
}
@Override
public int getItemCount() {
return mData.size();
}
class MyViewHolder extends RecyclerView.ViewHolder
{
TextView tv1;
TextView tv2;
TextView tv3;
public MyViewHolder(View view)
{
super(view);
tv1 = (TextView) view.findViewById(R.id.tv_name);
tv2 = (TextView) view.findViewById(R.id.tv_age);
tv3 = (TextView) view.findViewById(R.id.tv_address);
}
}
}
}
//布局很简单:activity中放置一个recycleview. recucleview的item布局是放3个textview,就不贴出来了。
5.调用过程:activity中需要获取数据展示在adapter中,所以先调用presenter中的getData方法,而这个方法又是先调用moudle中的getAllDetailData方法,得到一个数据集合作为参数,返回给view中setAdapter方法。
以上是自己学习mvp的整体过程,google中的todoapp -mvp搭建如下:看个人的习惯吧,只要是符合MVP思想,都可以的。
public interface TaskDetailContract {
interface View extends BaseView<Presenter> {
void showTask(Task task);
void showError();
void showTaskDeleted();
void showTaskMarkedComplete();
void showTaskMarkedActive();
boolean isActive();
}
interface Presenter extends BasePresenter {
void getTask();
void deleteTask();
void completeChanged(Task task, boolean isChecked);
}
}