android mvc例子,GitHub - chinalwb/mvc_mvp_mvvm: For Android MVC / MVP / MVVM patterns

Android MVC / MVP / MVVM

For Android MVC / MVP / MVVM patterns

简单示例演示如何从自由写法改进到MVC再改进到MVP - NoMVX

本来不懂标题说的这些东西(以及MVVM),看了这些文章之后

可能还有一些其他资料没能一一列出,然后感觉大部分说的写的都是很深奥很神秘。然后我自己有点想法,不过:

以下内容纯属个人观点,不喜请喷,谢谢各位大神,走过路过加个评论

先看下这个示例是啥样的。

dec1a161020ce4f7a7190d5200d91f42.gif

然后我再说下这个示例都包括了什么。

主页面就是Android Studio里面新建Project的时候选定了带Navigation Drawer的那个Activity,然后我在里面包含了5个Fragment。

MainFragment - 就是看到的那个Hello MVX

NoMVXFragment - 不使用任何的MVX模式,所有东西直接在Fragment(或Activity,示例中是Fragment) 写代码

MVCFragment - 同样功能改成MVC模式看看怎么写

MVPFragment - 再改成MVP呢?看看啥区别

MVP2Fragment - 换一种MVP实现,对比下哪个好

所有东西就是我要的功能,这里的功能很简单,共有3个,纯是demo用:

User List - 用户列表展示的功能

Add User - 添加用户的功能

EmptyView - 初始状态下,ListView内容为空,内容显示的功能

从User List开始说吧。我直接从Activity进入Fragment,从Fragment开始说,回头再说Activity的事儿。

NoMVXFragment -- 不使用任何的MVX模式

代码如下,关注点在下面:

public class NoMVXUserListFragment extends Fragment implements IMainPresenter.IFabListener {

private ScrollChildSwipeRefreshLayout mRefreshLayout;

private ListView mUserListView;

private UserListAdapter mUserListAdapter;

private List mUserModels;

public NoMVXUserListFragment() {

}

public static NoMVXUserListFragment newInstance() {

return new NoMVXUserListFragment();

}

@Nullable

@Override

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_list, container, false);

init(view);

return view;

}

private void init(View rootView) {

mRefreshLayout = rootView.findViewById(R.id.refresh_layout);

mUserListView = rootView.findViewById(R.id.user_list_view);

mUserListAdapter = new UserListAdapter(null);

mUserListView.setAdapter(mUserListAdapter);

mRefreshLayout.setScrollUpChild(mUserListView);

mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {

@Override

public void onRefresh() {

loadList();

}

});

setListEmptyView(rootView);

}

private void setListEmptyView(View rootView) {

View emptyView = rootView.findViewById(R.id.empty_list_layout);

TextView emptyTextView = emptyView.findViewById(R.id.empty_list_text_view);

String emptyText = getResources().getString(R.string.empty_text, "No MVX");

emptyTextView.setText(emptyText);

mUserListView.setEmptyView(emptyView);

}

private void loadList() {

showLoading();

BackgroundThreadPoster.post(new Runnable() {

@Override

public void run() {

mUserModels = UserManager.callAPIToGetUserList();

updateUI(mUserModels);

}

});

}

private void updateUI(final List userModels) {

MainThreadPoster.post(new Runnable() {

@Override

public void run() {

hideLoading();

updateListView(userModels);

}

});

}

public void updateListView(List userModels) {

mUserListAdapter.setUserModelList(userModels);

mUserListAdapter.notifyDataSetChanged();

Toast.makeText(getActivity(), "Update UI from Fragment: " + userModels.size(), Toast.LENGTH_LONG).show();

}

public void showLoading() {

mRefreshLayout.setRefreshing(true);

}

public void hideLoading() {

mRefreshLayout.setRefreshing(false);

}

@Override

public void onFabClicked() {

addUser();

}

private void addUser() {

showLoading();

BackgroundThreadPoster.post(new Runnable() {

@Override

public void run() {

UserModel userModel = UserManager.addUser();

mUserModels.add(userModel);

updateUI(mUserModels);

}

});

}

}

关注点:

View从哪初始化的?

ListView的下拉监听在哪做的?

ListView的下拉回调是在哪里执行的?

ListView下拉回调(模拟)调用API数据加载完毕之后,更新UI是在哪里执行的?

Add User是怎么执行的?

ListView为空时EmptyView是怎么初始化的?

挨个看看啊:

View从哪初始化的?- 是直接在onCreateView里面初始化了所有引用到的view,换句话说就是Fragment知道所有View的存在。相关代码:

public View onCreateView(..) {

View view = inflater.inflate(R.layout.fragment_list, container, false);

init(view);

return view;

}

private void init(View rootView) {

mRefreshLayout = rootView.findViewById(R.id.refresh_layout);

...

setListEmptyView(rootView);

}

ListView的下拉监听在哪做的? - onCreateView > init方法中

mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {

@Override

public void onRefresh() {

loadList(); // 下拉回调

}

});

ListView的下拉回调是在哪里执行的?- onCreateView > init方法中直接调用了 loadList,而这个方法就是实际开发中调用API或查询数据库的地方,此时也写在了Fragment中

private void loadList() {

showLoading();

BackgroundThreadPoster.post(new Runnable() { // 不用关注 BackgroundThreadPoster, 就是在子线程中执行这段代码

@Override

public void run() {

mUserModels = UserManager.callAPIToGetUserList();

updateUI(mUserModels);

}

});

}

ListView下拉回调(模拟)调用API数据加载完毕之后,更新UI是在哪里执行的?- updateUI() 方法中

private void updateUI(final List userModels) {

MainThreadPoster.post(new Runnable() { // 不用关注 MainThreadPoster, 就是在主线程中执行这段代码

@Override

public void run() {

hideLoading();

updateListView(userModels);

}

});

}

Add User是怎么执行的?- Fragment实现了一个接口方法,这个方法是会被Activity调用的,重点在这个接口的相关逻辑写在了哪里,此处呢也是直接写在了Fragment里面:

@Override

public void onFabClicked() {

addUser();

}

private void addUser() {

showLoading();

BackgroundThreadPoster.post(new Runnable() {

@Override

public void run() {

UserModel userModel = UserManager.addUser();

mUserModels.add(userModel);

updateUI(mUserModels);

}

});

}

ListView为空时EmptyView是怎么初始化的?- 直接在Fragment里面,至于EmptyView(此处是一个TextView) 他的内容是什么,就是Fragment自己处理的,无论是调用API还是直接从资源文件取字符串,他都负责了。也就是说Fragment必须参与初始化字符串的过程

private void setListEmptyView(View rootView) {

...

String emptyText = getResources().getString(R.string.empty_text, "No MVX");

emptyTextView.setText(emptyText);

mUserListView.setEmptyView(emptyView);

}

可见从初始化View,给View加监听,执行API,更新UI,这些功能统统都写在了Fragment中。当这个Fragment功能更多的时候,比如加上ListView点击事件,搜索,过滤,滑动监听等功能的时候,这个Fragment的类会变得越来越大。

那么如果我们不想让他变大,试试 MVC,看看他能不能改善。

MVC

简单示例演示如何从自由写法改进到MVC再改进到MVP - 2 - MVC

改成MVC是啥样?

上一篇中页面和点击事件都写在同一个类中了,所以基本没什么层次可言。 要是说那个UserModel是分出去的,也行,毕竟如果把那个也写在Fragment里。。也可以。。只是让那个Fragment更大更乱一些。

那先看看MVC模式的图吧

e23fe375fe73dc5be6e1d5c8d56c5bcc.png

我并不太愿意去解释这个图,因为这样会让这个文章看起来跟其他的一样 也会很大,我稍微说下自己的看法吧

View -- 他是啥?你可能会说他是Fragment,此时,可以这么理解,但我先说一句,Fragment更像是一个View的容器, 暂且到此为止吧,后面的几篇文章会解释

Controller -- 他是啥?简单说他处理用户在页面交互时的事件比如下拉,点击等

Model -- 这个简单就是UserModel了,里面有picId, name, desc这样的信息

这里面最重要的是Controller层,他做整体的统筹调度

对着代码看吧,我尽量把代码写的简单易懂。

Model::

public class UserModel {

private int picId;

private String userName;

private String userDesc;

public UserModel(int picId, String userName, String userDesc) {

this.picId = picId;

this.userName = userName;

this.userDesc = userDesc;

}

public int getPicId() {

return picId;

}

public void setPicId(int picId) {

this.picId = picId;

}

public String getUserName() {

return userName;

}

public void setUserName(String userName) {

this.userName = userName;

}

public String getUserDesc() {

return userDesc;

}

public void setUserDesc(String userDesc) {

this.userDesc = userDesc;

}

}

View::

public class MVCUserListFragment extends Fragment implements IMainPresenter.IFabListener {

private ScrollChildSwipeRefreshLayout mRefreshLayout;

private ListView mUserListView;

private UserListAdapter mUserListAdapter;

private UserListController mUserListController;

public MVCUserListFragment() {

// Required empty public constructor

}

public static MVCUserListFragment newInstance() {

return new MVCUserListFragment();

}

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

}

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// Inflate the layout for this fragment

View rootView = inflater.inflate(R.layout.fragment_list, container, false);

init(rootView);

return rootView;

}

private void init(View rootView) {

mRefreshLayout = rootView.findViewById(R.id.refresh_layout);

mUserListView = rootView.findViewById(R.id.user_list_view);

mUserListAdapter = new UserListAdapter(null);

mUserListView.setAdapter(mUserListAdapter);

mUserListController = new UserListController(this);

mRefreshLayout.setScrollUpChild(mUserListView);

mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {

@Override

public void onRefresh() {

mUserListController.loadList();

}

});

View emptyView = rootView.findViewById(R.id.empty_list_layout);

TextView emptyTextView = emptyView.findViewById(R.id.empty_list_text_view);

String emptyListViewTextString = mUserListController.getEmptyListText();

String emptyText = getResources().getString(R.string.empty_text, emptyListViewTextString);

emptyTextView.setText(emptyText);

mUserListView.setEmptyView(emptyView);

}

@Override

public void onStart() {

super.onStart();

}

public void updateListView(List userModels) {

mUserListAdapter.setUserModelList(userModels);

mUserListAdapter.notifyDataSetChanged();

Toast.makeText(getActivity(), "Update UI from MVC " + userModels.size(), Toast.LENGTH_LONG).show();

}

public void showLoading() {

mRefreshLayout.setRefreshing(true);

}

public void hideLoading() {

mRefreshLayout.setRefreshing(false);

}

@Override

public void onFabClicked() {

mUserListController.addUser();

}

}

Controller::

public class UserListController {

private MVCUserListFragment mMVCUserListFragment;

private List mUserModels;

public UserListController(MVCUserListFragment MVCUserListFragment) {

mMVCUserListFragment = MVCUserListFragment;

}

public String getEmptyListText() {

return "MVC模式,默认没有数据,尝试下拉刷新吧!";

}

public void loadList() {

mMVCUserListFragment.showLoading();

BackgroundThreadPoster.post(new Runnable() {

@Override

public void run() {

mUserModels = UserManager.callAPIToGetUserList();

updateUI(mUserModels);

}

});

}

public void addUser() {

mMVCUserListFragment.showLoading();

BackgroundThreadPoster.post(new Runnable() {

@Override

public void run() {

UserModel userModel = UserManager.addUser();

mUserModels.add(userModel);

updateUI(mUserModels);

}

});

}

private void updateUI(final List userModels) {

MainThreadPoster.post(new Runnable() {

@Override

public void run() {

mMVCUserListFragment.hideLoading();

mMVCUserListFragment.updateListView(userModels);

}

});

}

}

跟上一篇一样,关注点如下:

View从哪初始化的?

ListView的下拉监听在哪做的?

ListView的下拉回调是在哪里执行的?

ListView下拉回调(模拟)调用API数据加载完毕之后,更新UI是在哪里执行的?

Add User是怎么执行的?

ListView为空时EmptyView是怎么初始化的?

看下改成MVC的逻辑:

View是从Fragment初始化的 - 跟NoMVX一样

ListView的下拉监听也是在Fragment做的 - 跟NoMVX一样

ListView的下拉回调是在哪里执行的 - 这个是在Fragment里面执行的,但他跟NoMVX有本质的区别,那就是这里的回调是调用了Controller的方法,而不是调用Fragment类里面的方法

private void init(View rootView) {

mRefreshLayout = rootView.findViewById(R.id.refresh_layout);

...

mRefreshLayout.setScrollUpChild(mUserListView);

mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {

@Override

public void onRefresh() {

mUserListController.loadList(); // 重要:Fragment (View) 持有Controller的引用

}

});

...

}

ListView下拉回调(模拟)调用API数据加载完毕之后,更新UI是在哪里执行的 - 这个是在Controller里面执行的:

public void loadList() {

mMVCUserListFragment.showLoading(); // 重要:Controller持有Fragment (View) 的引用

BackgroundThreadPoster.post(new Runnable() {

@Override

public void run() {

mUserModels = UserManager.callAPIToGetUserList();

updateUI(mUserModels);

}

});

}

Add User是怎么执行的 - 这个跟第4点类似,Fragment(View)调用Controller, Controller调用API,完成后再调用Fragment (View) 的方法来更新UI -- 这个是MVC的主要执行过程

ListView为空时EmptyView是怎么初始化的 - 在Fragment初始化过程中调用了Controller的方法来得到要显示的内容:

private void init(View rootView) {

...

TextView emptyTextView = emptyView.findViewById(R.id.empty_list_text_view);

String emptyListViewTextString = mUserListController.getEmptyListText(); // 此处调用了Controller的方法

String emptyText = getResources().getString(R.string.empty_text, emptyListViewTextString);

emptyTextView.setText(emptyText);

mUserListView.setEmptyView(emptyView);

}

我们来分析下把 Fragment 拆成 Fragment + Controller 有什么好处, 难道我们就是为了耍下花样吗?

Fragment现在类变小了。他基本上就是从layout里面加载一个布局,然后加上监听,不过监听回调都委派给了Controller,他也不用关心什么时候要更新UI了,这些都放到Controller中来处理了

Controller他的职责也很明确,那就是处理Fragment拆分出来的事情,主要可以概括为两点:

调用API或数据库执行数据交互,并在完毕之后更新UI

Fragment中有一些不确定的因素,比如上例中,TextView显示的文字,可能也是需要根据运行时情况不同而返回不同的文字(比如,返回当前登录的用户名,当前页面名称等等),这样一来,Fragment是不需要关心这些与数据相关的细节的,都由Controller来负责了

你可能还会再问,现在Fragment是小了可他把所有的工作都交给Controller去做了,这就好像是把AB分成了A+B,有什么好处,你告诉我。

我得说如果你这么想了 那这是一个好问题

这么说吧,如果需求改变了

比如改变了UI,对于之前NoMVX来说, 是需要修改Fragment类的,而这个有可能会影响到数据访问方面的功能,当然你可以很小心的去做来保证他不受影响,但这个可能性还是存在且有点大的。如果对于MVC来说,你改Fragment不影响Controller,只要数据访问那边的需求不变,Controller几乎是不用动的

再比如改变了数据来源,原来是从数据库中取,现在改变成从服务器取,那NoMVX也是需要改Fragment的,而这个可能影响UI部分。对于MVC来说,只需要在Controller里面改就行了,而这个不影响Fragment的UI显示。

可以看出来MVC的职责更纯粹一些,Fragment在MVC模式中只负责View, Controller只负责数据交互然后更新UI。

看起来不错对吧?可为什么还要提到MVP呢?一定是MVC还有什么不足之处。

是的。

这里我只提一点,那就是Controller引用了Fragment的实例,而这个对单元测试是比较困难的,因为在Android环境之外的单元测试环境中难以模拟Fragment这个类。

(其他MVC的不足还请自行查看其他长篇的文字。。)

那MVP比MVC又好到哪了呢?

MVP

简单示例演示如何从自由写法改进到MVC再改进到MVP - 3 - MVP

改成MVP是啥样?

先看一个图吧。

e1b6059b0b9978b612dd1982e755766a.png

从这个图中我们可以看出来,

View 和 Presenter各有一个接口, IView 和 IPresenter

View持有Presenter的引用,同时Presenter也持有View的引用

在上代码之前先说下,这个MVP的设计跟MVC类似,当用户操作View的时候,在View里面调用IPresenter的方法(此处为Presenter接口),而Presenter的实现类去处理数据相关的操作并更新UI。

其实这个地方,我感觉跟MVC非常相似,唯一的区别在于,MVP引入了接口,而MVC里面直接引用的是相关的类。

引入接口的好处就是,Presenter类里面不直接引用Fragment(或者其他的View \ Activity等Android里面特有的类),而是引用了IView, 这样以来就很容易在单元测试的时候写任何一个类来实现IView接口,这样使得单元测试更加容易进行。

上代码:

IView::

public interface IUserListView {

void updateList(List userModels);

void showLoading();

void hideLoading();

}

View::

public class MVPUserListFragment extends Fragment implements IUserListView, IMainPresenter.IFabListener {

private ScrollChildSwipeRefreshLayout mRefreshLayout;

private IUserListPresenter mListPresenter;

private ListView mUserListView;

private UserListAdapter mUserListAdapter;

public MVPUserListFragment() {

super();

}

public static MVPUserListFragment newInstance() {

return new MVPUserListFragment();

}

@Nullable

@Override

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {

View rootView = getFragmentView(inflater, container);

init(rootView);

return rootView;

}

private void init(View rootView) {

mListPresenter = new UserUserListPresenter(this);

mRefreshLayout = rootView.findViewById(R.id.refresh_layout);

mUserListView = rootView.findViewById(R.id.user_list_view);

mUserListAdapter = new UserListAdapter(null);

mUserListView.setAdapter(mUserListAdapter);

mRefreshLayout.setScrollUpChild(mUserListView);

mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {

@Override

public void onRefresh() {

mListPresenter.loadList();

}

});

View emptyView = rootView.findViewById(R.id.empty_list_layout);

TextView emptyTextView = emptyView.findViewById(R.id.empty_list_text_view);

String emptyListViewTextString = mListPresenter.getEmptyListText();

String emptyText = getResources().getString(R.string.empty_text, emptyListViewTextString);

emptyTextView.setText(emptyText);

mUserListView.setEmptyView(emptyView);

}

private View getFragmentView(LayoutInflater inflater, @Nullable ViewGroup container) {

return inflater.inflate(R.layout.fragment_list, container, false);

}

@Override

public void updateList(List userModels) {

hideLoading();

mUserListAdapter.setUserModelList(userModels);

mUserListAdapter.notifyDataSetChanged();

Toast.makeText(getActivity(), "Update UI from MVP", Toast.LENGTH_LONG).show();

}

@Override

public void showLoading() {

mRefreshLayout.setRefreshing(true);

}

@Override

public void hideLoading() {

mRefreshLayout.setRefreshing(false);

}

@Override

public void onFabClicked() {

mListPresenter.addUser();

}

}

IPresenter:

public interface IUserListPresenter {

String getEmptyListText();

void loadList();

void addUser();

}

Presenter:

public class UserUserListPresenter implements IUserListPresenter {

private IUserListView mListView;

private List mUserModels;

public UserUserListPresenter(IUserListView iUserListView) {

mListView = iUserListView;

}

@Override

public String getEmptyListText() {

return "MVP - NO DATA FOR NOW! ";

}

@Override

public void loadList() {

mListView.showLoading();

BackgroundThreadPoster.post(new Runnable() {

@Override

public void run() {

mUserModels = UserManager.callAPIToGetUserList();

updateUI(mUserModels);

}

});

}

@Override

public void addUser() {

mListView.showLoading();

BackgroundThreadPoster.post(new Runnable() {

@Override

public void run() {

UserModel userModel = UserManager.addUser();

mUserModels.add(userModel);

updateUI(mUserModels);

}

});

}

private void updateUI(final List userModels) {

MainThreadPoster.post(new Runnable() {

@Override

public void run() {

mListView.updateList(userModels);

}

});

}

}

这个MVP的另一个好处是,通过定义IView和IPresenter接口,很容易知道实现类有什么功能。

但这个MVP实现中,还是直接用Fragment实现了IView接口,而我比较同意这个观点:

https://www.techyourchance.com/activities-android/

也就是说,Activity/Fragment 更应该被当做Controller / Presenter的角色来看待。

这样就有了MVP2的做法。

MVP2

简单示例演示如何从自由写法改进到MVC再改进到MVP - 4 - MVP2

另一种MVP2是啥样?

这个模式我还真没找到相关的图,那就描述一下吧!

上一篇中MVP的设计图是这样的:

e1b6059b0b9978b612dd1982e755766a.png

这个MVP2跟他类似,但有两个区别:

一个区别是在于上一篇中提到的:

这(上)个MVP实现中,还是直接用Fragment实现了IView接口

另外一个区别是:

View不直接引用Presenter,而是通过接口

这个设计中Fragment作为中介,实现了IView和IPresenter中的回调接口。

先看Fragment和IView的分离:

简单的说就是在Fragment的onCreateView方法中返回一个View,而这个View实现了IView接口。

我们先看下IView::

public interface IBaseView {

View getRootView();

Context getContext();

}

IBaseView是一个基础接口,具体的页面应该有自己的一个IView,比如:

public interface IUserListView extends IBaseView {

interface IListViewHandler {

void onRefreshList(); // 重点:注意这个是连接View 和 Presenter的接口方法

}

void updateList(List userModels);

void showLoading();

void hideLoading();

void setViewHandler(IListViewHandler handler);

void setEmptyListText(String emptyListViewTextString);

}

而这个IUserListView的实现类是这样的:

public class UserListView implements IUserListView {

private ScrollChildSwipeRefreshLayout mRefreshLayout;

private ListView mUserListView;

private UserListAdapter mUserListAdapter;

private IListViewHandler mListViewHandler; // 重点:没有引用 Presenter,而是实现了一个回调接口

private View mEmptyView;

private TextView mEmptyTextView;

private View mRootView;

private Context mContext;

public UserListView(LayoutInflater layoutInflater, ViewGroup container) {

mRootView = layoutInflater.inflate(getLayoutId(), container, false);

mContext = mRootView.getContext();

init(mRootView);

}

private int getLayoutId() {

return R.layout.fragment_list;

}

protected void init(View rootView) {

mRefreshLayout = rootView.findViewById(R.id.refresh_layout);

mUserListView = rootView.findViewById(R.id.user_list_view);

mUserListAdapter = new UserListAdapter(null);

mUserListView.setAdapter(mUserListAdapter);

mRefreshLayout.setScrollUpChild(mUserListView);

mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {

@Override

public void onRefresh() {

if (mListViewHandler != null) {

mListViewHandler.onRefreshList();

}

}

});

mEmptyView = rootView.findViewById(R.id.empty_list_layout);

mEmptyTextView = mEmptyView.findViewById(R.id.empty_list_text_view);

}

@Override

public void updateList(List userModels) {

hideLoading();

mUserListAdapter.setUserModelList(userModels);

mUserListAdapter.notifyDataSetChanged();

Toast.makeText(getContext(), "Update UI", Toast.LENGTH_LONG).show();

}

@Override

public void showLoading() {

mRefreshLayout.setRefreshing(true);

}

@Override

public void hideLoading() {

mRefreshLayout.setRefreshing(false);

}

@Override

public void setViewHandler(IListViewHandler handler) {

mListViewHandler = handler;

}

@Override

public void setEmptyListText(String emptyListViewTextString) {

String emptyText = mContext.getResources().getString(R.string.empty_text, emptyListViewTextString);

mEmptyTextView.setText(emptyText);

mUserListView.setEmptyView(mEmptyView);

}

@Override

public View getRootView() {

return mRootView;

}

@Override

public Context getContext() {

if (mRootView == null) {

return null;

}

return mRootView.getContext();

}

}

再看Fragment的实现,一切都就清楚了。

Fragment中实现了IListViewHandler 和 ListUpdaterListener, 他们分别是IView 和 IPresenter 的回调。

当用户操作(比如下拉)时调用IListViewHandler的回调方法,此时执行到Fragment里面的代码,

在Fragment里面调用Presenter的方法执行数据交互

然后Presenter调用ListUpdaterListener的方法触发到Fragment里面的回调

Fragment再调用IView的方法来完成UI更新

这几步完成了IView和IPresenter的解耦。

public class MVP2UserListFragment extends Fragment implements IUserListView.IListViewHandler,

UserListPresenter.ListUpdaterListener, IMainPresenter.IFabListener {

private IUserListView mListView;

IUserListPresenter mPresenter;

public MVP2UserListFragment() {

super();

}

public static MVP2UserListFragment newInstance() {

return new MVP2UserListFragment();

}

@Override

public void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

mPresenter = new UserListPresenter();

}

@Nullable

@Override

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

mListView = new UserListView(inflater, container);

mListView.setViewHandler(this);

mListView.setEmptyListText(mPresenter.getEmptyListText());

return mListView.getRootView();

}

@Override

public void onResume() {

super.onResume();

mPresenter.registerListener(this);

}

@Override

public void onPause() {

super.onPause();

mPresenter.unregisterListener(this);

}

@Override

public void onRefreshList() {

mListView.showLoading();

mPresenter.loadList();

}

@Override

public void updateList(List userModels) {

mListView.hideLoading();

mListView.updateList(userModels);

}

@Override

public void onFabClicked() {

mListView.showLoading();

mPresenter.addUser();

}

}

贴下IPresenter:

public interface IUserListPresenter extends IBasePresenter {

void loadList();

void addUser();

String getEmptyListText();

}

和Presenter

public class UserListPresenter extends BasePresenter implements IUserListPresenter {

private List mUserModels;

public UserListPresenter() {

}

public void loadList() {

BackgroundThreadPoster.post(new Runnable() {

@Override

public void run() {

mUserModels = UserManager.callAPIToGetUserList();

updateUI(mUserModels);

}

});

}

public void addUser() {

BackgroundThreadPoster.post(new Runnable() {

@Override

public void run() {

UserModel userModel = UserManager.addUser();

mUserModels.add(userModel);

updateUI(mUserModels);

}

});

}

@Override

public String getEmptyListText() {

return "MVP2 :: ";

}

private void updateUI(final List userModels) {

MainThreadPoster.post(new Runnable() {

@Override

public void run() {

for (ListUpdaterListener listener : getListeners()) {

listener.updateList(userModels);

}

}

});

}

public interface ListUpdaterListener {

void updateList(List userModels);

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值