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

2018-09-19  本文已影响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

上一篇下一篇

猜你喜欢

热点阅读