简单示例演示如何从自由写法改进到MVC再改进到MVP - NoMVX
本来不懂标题说的这些东西(以及MVVM),看了这些文章之后
https://www.techyourchance.com/mvp-mvc-android-1/
https://academy.realm.io/posts/eric-maxwell-mvc-mvp-and-mvvm-on-android/
https://github.com/florina-muntenescu/MVPvsMVVM
http://tangpj.com/2016/12/04/mvvm-structure-introduction/#comments
可能还有一些其他资料没能一一列出,然后感觉大部分说的写的都是很深奥很神秘。然后我自己有点想法,不过:
以下内容纯属个人观点,不喜请喷,谢谢各位大神,走过路过加个评论
先看下这个示例是啥样的。
然后我再说下这个示例都包括了什么。
主页面就是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<UserModel> 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<UserModel> userModels) {
MainThreadPoster.post(new Runnable() {
@Override
public void run() {
hideLoading();
updateListView(userModels);
}
});
}
public void updateListView(List<UserModel> 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<UserModel> 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,看看他能不能改善。
网友评论