嵌套的 RecyclerViews MVP 实现

Nested RecyclerViews MVP implementation

我想制作一个具有垂直 RecyclerView 和嵌套水平 RecyclerView 的应用。我不明白在这种情况下如何正确使用 MVP 模式。 MVP "rule" 说一个屏幕应该只有一个视图。

我的View界面:

public interface ViewLayer {
    void showProductsInCategory(int categoryId, List<ProductModel> productList, PresenterLayer presenter);

    void showCategories(List<CategoryModel> categoryItemList, PresenterLayer presenter);
}

主持人:

public interface PresenterLayer {
    void onViewReady();
}

型号:

public interface InteractorLayer {
    void getProducts(int categoryId);

    void getCategories();
}

模型侦听器接口:

public interface InteractorListener {
    void onProductsLoaded(int id, List<ProductModel> products);

    void onCategoriesLoaded(List<CategoryModel> categories);
}

类别模型:

public class CategoryModel {
    private String categoryName;
    private List<ProductModel> productList;

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }

    public List<ProductModel> getProductList() {
        return productList;
    }

    public void setProductList(List<ProductModel> productList) {
        this.productList = productList;
    }
}

所以我必须 select 每个嵌套 RecyclerViewcategoryId 将数据添加到它们的适配器。我可以为每个水平 RecyclerView 创建单独的 Model-View-Presenter 界面吗?

UPD:

循序渐进

1) MainActivity.onCreate 呼叫 presenter.onViewReady()

2) 演示者调用 interactorLayer.getCategories()

3) 模型调用 InteractorListener.onCategoriesLoaded(List<CategoryModel> categories)

4) 演示者调用 ViewLayer(MainActivity) showCategories(List<CategoryModel> categoryItemList, PresenterLayer presenter)

5) MainActivitycategoryItemList 设置为外部 RecyclerView 的适配器。现在每个 categoryItem 都有 null productList

6) 在方法onCategoriesLoaded(...)之后ViewLayer.showCategories(...)Presenter在每个Category

的循环中调用Model的InteractorLayer.getProducts(i)

7) 在任何 productList 加载后 Presenter 调用 ViewLayershowProductsInCategory(...)

8) MainActivity 获取主要 RecyclerViewAdapter,获取一个类别项并为其设置 productList

9) MainActivity 调用 AdapternotifyDataSetChanged()

10) 当onBinding调用

时,内部RecyclerView设置新的productList

我觉得很复杂。我能用它做什么?

2017 年 3 月 24 日更新

源代码:https://github.com/Lex74/ProductsShop

首先,我想声明,我不认为自己是 MVP 大师,而是努力理解模式的人,

我最喜欢的 MVP 参考资料:The Clean Architecture 来自 Uncle Bob 的博客

根据这个博客 post,有一个叫做 The Dependency Rule 的东西:

...source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle...

例如,Presenter class 不需要知道任何关于 RecyclerViewRecyclerView.Adapter 的信息。它需要一些 interface 来向外层传递信息。

interface 的方法取决于用例:对于 List,人们希望能够

  • 传递对整个数据的引用List (showCategories())
  • 刷新单个列表项(showProductsInCategory()

    所以我认为依赖规则说,ViewLayer 接口必须提供满足 [Model 层和] Presenter层。作为 Presenter,我根本不在乎 View 是否有 ListView 或者可能没有 View 完全没有,而是声音和振动信号的某种组合。

    另一方面,View class 知道它的 Presenter class 的名称(和方法)似乎完全没问题,所以也许 PresenterLayer 接口不是必须的。

    这完全取决于 View 如何向用户提供数据。嵌套的 View 结构仍然只是一个复杂的 View。所以我认为不需要提供嵌套的 interfaces.

    在某些嵌套 List 的情况下,Presenter 可能需要一种方法来更新内部 List 的项目,例如 showSingleProductInCategory(ProductModel product, int categoryPosition, int productPosition).

    另一个有趣的问题:谁保存(并可能修改)数据?在我看来,Presenter 负责数据,它应该只将对数据的引用传递到 View 层或通知它变化。 Adapter 不应该有权修改原始数据列表,Presenter 永远不必询问 Adapter "how many items are there?" 而且我真的不喜欢两个单独的数据列表的想法。各种 notify... 方法的名称似乎表明我在正确的轨道上。

    这意味着 Presenter 将始终保留原始数据 List。如果数据发生变化,Presenter 将更新其数据(可能是 clear() 和 "copy the new items",也可能更细粒度,具体取决于 ProductLoader 提供的内容)之后, Presenter 将通过 ViewLayer 接口通知 Adapter

    Link 到 zip file with the modified Java classes

    编辑

    不知何故,我怀疑 "one View for one screen" 是否适用于 Android。想象一下典型的 Master-Detail 情况。如果屏幕很大,您会希望使用 space 并同时显示两个 Fragment

    因此,如果每个 Fragment 有一个 View(和一个 Presenter),一切都适用于所有类型的屏幕。 Activity 根据屏幕大小管理 Fragment

    我已经解释过,我喜欢让某些 ListViewRecyclerViewAdapter 实现 interface,它需要作为 Presenter。 (作为回调的所有 Fragment 可以做的就是将信息传递给 Adapter

    另一方面,一个Fragment可能包含多组数据。其中一些可能以某种方式相关(例如某个特定艺术家的所有歌曲),其他一些(所有这些广告......)而不是相关。 Presenter 需要一些方法来告诉 View 向用户显示什么:一种用于艺术家,一种用于广告等

    因此,如果我有一个带有少量 Fragment 的应用程序,interface 将包含

    之类的方法
    void showAdvertisement(AdObject ad);
    void showArtistInfo(Artist artist);
    

    ...并且 Presenter 会期望一些 class 在其 Constructor 中实现这个特定的 interface。 (加上歌曲的 Adapter),我会让 Fragment 为所有非收集数据实施 interface

    在包含多个应用程序的项目中,可以考虑使用通用 interfaces (一个用于任何类型的详细信息,一个用于集合)。然后会有一个方法 showData(T data),上面示例中的 Presenter 会期望一个回调用于广告,一个回调用于艺术家信息:

    MyPlaylistPresenter (DetailInterface<AdObject> adCallback, DetailInterface<Artist> artistCallback, CollectionInterface<Song> songsCallback){...}
    

    然后在 Fragment 中写:

    MyPlaylistPresenter presenter = new MyPlaylistPresenter(this, this, adapter);
    

    有点像乐高 :),但总的来说少 interface classes。做基本相同事情的方法在整个项目中具有相同的名称,因此我认为这有助于提高可维护性。

    现在关于你的另一个问题:

    如果您的应用在客户端有一个 模型,那么我认为您是对的。 另一方面,有些项目 Model 是后端的一部分。那么 Presenter 将是合乎逻辑的选择。