从github上的优秀实例看MVP模式

news/2024/7/5 18:19:03

github上有一个关于MVP模式学习的实例https://github.com/antoniolg/androidmvp,虽然只有简单的几个类,却收获了几千个星。这个例子确实通俗易懂,直观的体现出了MVP模式的特点:


考虑这样一个需求,页面显示一个列表,数据源需要特别获取。

分析一下这个过程,页面首先显示出进度提示框表示正在加载,然后数据源获取数据并传给页面的列表进行渲染。

MVP模式,model ,view,presenter。。视图层只处理UI操作,包括显示dialog,拿到数据后的处理(关闭dialog并渲染列表),这里可以细分为关闭dialog和为列表的适配器设置数据列表。     presenter是视图层和数据层沟通的桥梁,控制着双方的行为。  数据源则负责获取数据并返回。


首先对三个类进行抽象:

1.视图层:

package com.antonioleiva.mvpexample.app.main;

import java.util.List;

public interface MainView {

    void showProgress();

    void hideProgress();

    void setItems(List<String> items);

    void showMessage(String message);
}


2.数据层:

package com.antonioleiva.mvpexample.app.main;

import java.util.List;

public interface FindItemsInteractor {

    interface OnFinishedListener {
        void onFinished(List<String> items);
    }

    void findItems(OnFinishedListener listener);
}

这里一个接口用来返回拿到的数据,接口中的方法将数据传入。在获取数据的方法里将这个接口传入,接口的实例化是在代理中实现的。


3.代理层:

package com.antonioleiva.mvpexample.app.main;

public interface MainPresenter {

    void onResume();

    void onItemClicked(int position);

    void onDestroy();
}
视图层在调用onResume和onDestroy时的逻辑也要通过代理的方法实现。



下面是创建的实例,首先是数据实例:

1.数据层:

public class FindItemsInteractorImpl implements FindItemsInteractor {
    @Override public void findItems(final OnFinishedListener listener) {
        new Handler().postDelayed(new Runnable() {
            @Override public void run() {
                listener.onFinished(createArrayList());
            }
        }, 2000);
    }

    private List<String> createArrayList() {
        return Arrays.asList(
                "Item 1",
                "Item 2",
                "Item 3",
                "Item 4",
                "Item 5",
                "Item 6",
                "Item 7",
                "Item 8",
                "Item 9",
                "Item 10"
        );
    }
}

只有两个方法,也可以看成只有一个方法,就是拿到数据并通过回调接口返回。


2.视图层:
public class MainActivity extends Activity implements MainView, AdapterView.OnItemClickListener {

    private ListView listView;
    private ProgressBar progressBar;
    private MainPresenter presenter;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.list);
        listView.setOnItemClickListener(this);
        progressBar = (ProgressBar) findViewById(R.id.progress);
        presenter = new MainPresenterImpl(this, new FindItemsInteractorImpl());
    }

    @Override protected void onResume() {
        super.onResume();
        presenter.onResume();
    }

    @Override public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override protected void onDestroy() {
        presenter.onDestroy();
        super.onDestroy();
    }

    @Override public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
        listView.setVisibility(View.INVISIBLE);
    }

    @Override public void hideProgress() {
        progressBar.setVisibility(View.INVISIBLE);
        listView.setVisibility(View.VISIBLE);
    }

    @Override public void setItems(List<String> items) {
        listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
    }

    @Override public void showMessage(String message) {
        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
    }

    @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        presenter.onItemClicked(position);
    }
}

视图层拥有一个代理实例,将几个生命阶段的逻辑以及列表项点击全部托管给代理进行处理,自己只负责实现接口中的几个ui操作方法。


3.代理层:

public class MainPresenterImpl implements MainPresenter, FindItemsInteractor.OnFinishedListener {

    private MainView mainView;
    private FindItemsInteractor findItemsInteractor;

    public MainPresenterImpl(MainView mainView, FindItemsInteractor findItemsInteractor) {
        this.mainView = mainView;
        this.findItemsInteractor = findItemsInteractor;
    }

    @Override public void onResume() {
        if (mainView != null) {
            mainView.showProgress();
        }

        findItemsInteractor.findItems(this);
    }

    @Override public void onItemClicked(int position) {
        if (mainView != null) {
            mainView.showMessage(String.format("Position %d clicked", position + 1));
        }
    }

    @Override public void onDestroy() {
        mainView = null;
    }

    @Override public void onFinished(List<String> items) {
        if (mainView != null) {
            mainView.setItems(items);
            mainView.hideProgress();
        }
    }

    public MainView getMainView() {
        return mainView;
    }
}

首先代理的构造器要传入视图和数据两个实例,然后通过调用这两个实例的方法来完成控制。这里代理的几个任务有,在视图销毁时设置解绑,对列表项点击事件的响应,在视图刚开始时控制视图显示等待中并开始控制数据源获取数据,在数据获取完成后通过实现的回调接口将数据传给视图层。

需要注意的是代理实现了数据层的接口,以此来接收并处理数据。



http://www.niftyadmin.cn/n/4646364.html

相关文章

显著减少项目gradle编译时间

原文来自https://zeroturnaround.com/rebellabs/making-gradle-builds-faster/ 1.对build过程进行配置实现编译优化&#xff1a; &#xff08;1&#xff09;首先了解如何用命令行进行编译&#xff1a; 使用git命令行进入项目根目录&#xff0c;然后执行 ./gradlew :app:asse…

CVE-2018-12613(远程文件包含)

问题在index.php的55~63行// If we have a valid target, lets load that script instead if (! empty($_REQUEST[target])&& is_string($_REQUEST[target])&& ! preg_match(/^index/, $_REQUEST[target])&& ! in_array($_REQUEST[target], $target_bl…

Support Design库中的BottomSheetDialog组件使用

首先来看这个组件使用的经典案例&#xff0c;微信公众号文章操作功能显示&#xff1a; 首先要知道BottomSheetDialog有两种&#xff0c;第一种弹出后不影响主界面交互&#xff0c;第二种弹出后主界面变暗不能交互&#xff0c;这里分别进行使用。 1.首先是普通的BottomSheetDia…

Netty内存池ByteBuf 内存回收

内存池ByteBuf 内存回收: 在前面的章节中我们有提到, 堆外内存是不受JVM 垃圾回收机制控制的, 所以我们分配一块堆外内存进行ByteBuf 操作时, 使用完毕要对对象进行回收, 本节就以PooledUnsafeDirectByteBuf 为例讲解有关内存分配的相关逻辑。PooledUnsafeDirectByteBuf 中内存…

Android 导入库文件在主工程使用不了库文件的代码

主工程不识别这个文件 但是我明明加入这个lib库了为什么使用不了呢&#xff1f; 原因在这里 将库文件里面的build.gradle中的依赖implementation转换成api 然后sync一下 酱酱酱~~~不报错啦 是不是超级简单&#xff1f;

SocketChannel 读取ByteBuf 的过程

SocketChannel 读取ByteBuf 的过程&#xff1a; 我们首先看NioEventLoop 的processSelectedKey 方法&#xff1a; private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {//获取到channel 中的unsafefinal AbstractNioChannel.NioUnsafe unsafe ch.unsafe…

ReactNative学习实例(八) 第三方组件TabNavigator底部导航栏

TabNavigator是github上开源的一个rn组件&#xff0c;是适用于ios和安卓两个平台的底部导航栏。 组件地址&#xff1a;https://github.com/expo/react-native-tab-navigator 1.首先通过命令行将组件引入项目&#xff1a; 进入项目根目录并执行 npm install react-native-tab-…

switch-case内不能定义变量?

1. 报错 switch&#xff08;something&#xff09; { case a: int a 0; break; default: break;   } 结果报错&#xff1a; error: cannot jump from switch statement to this case label…… 2. 错误原因 究其根本原因&#xff0c;是C的一条规则&#xff1a;在任何作用域内…