美文网首页
从没有MVX到MVC到MVP - (1 - NoMVX)

从没有MVX到MVC到MVP - (1 - NoMVX)

作者: __无语__ | 来源:发表于2018-09-19 06:48 被阅读0次

    简单示例演示如何从自由写法改进到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。

    1. MainFragment - 就是看到的那个Hello MVX
    2. NoMVXFragment - 不使用任何的MVX模式,所有东西直接在Fragment(或Activity,示例中是Fragment) 写代码
    3. MVCFragment - 同样功能改成MVC模式看看怎么写
    4. MVPFragment - 再改成MVP呢?看看啥区别
    5. MVP2Fragment - 换一种MVP实现,对比下哪个好

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

    1. User List - 用户列表展示的功能
    2. Add User - 添加用户的功能
    3. 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);
                }
            });
        }
    }
    

    关注点:

    1. View从哪初始化的?
    2. ListView的下拉监听在哪做的?
    3. ListView的下拉回调是在哪里执行的?
    4. ListView下拉回调(模拟)调用API数据加载完毕之后,更新UI是在哪里执行的?
    5. Add User是怎么执行的?
    6. ListView为空时EmptyView是怎么初始化的?

    挨个看看啊:

    1. 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);
        }
    
    1. ListView的下拉监听在哪做的? - onCreateView > init方法中
    mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    loadList(); // 下拉回调
                }
            });
    
    1. ListView的下拉回调是在哪里执行的?- onCreateView > init方法中直接调用了 loadList,而这个方法就是实际开发中调用API或查询数据库的地方,此时也写在了Fragment中
        private void loadList() {
            showLoading();
            BackgroundThreadPoster.post(new Runnable() { // 不用关注 BackgroundThreadPoster, 就是在子线程中执行这段代码
                @Override
                public void run() {
                    mUserModels = UserManager.callAPIToGetUserList();
                    updateUI(mUserModels);
                }
            });
        }
    
    1. ListView下拉回调(模拟)调用API数据加载完毕之后,更新UI是在哪里执行的?- updateUI() 方法中
        private void updateUI(final List<UserModel> userModels) {
            MainThreadPoster.post(new Runnable() {  // 不用关注 MainThreadPoster, 就是在主线程中执行这段代码
                @Override
                public void run() {
                    hideLoading();
                    updateListView(userModels);
                }
            });
        }
    
    1. 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);
                }
            });
        }
    
    1. 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,看看他能不能改善。

    具体代码详见:
    https://github.com/chinalwb/mvc_mvp_mvvm

    相关文章

      网友评论

          本文标题:从没有MVX到MVC到MVP - (1 - NoMVX)

          本文链接:https://www.haomeiwen.com/subject/nxazqftx.html