如何使用MVP模式控制Android中的ListView

17
我目前正在使用MVP模式开发Android应用程序。 当我尝试开发一个Activity时,我应该使用ListView。因此,我正在使用ListView的Adapter。但是我听说Adapter类似于MVP模式中的Presenter。
我认为如果Adapter类似于Presenter,那么我应该创建Presenter来更新ListView,而不是使用Adapter。
在这种情况下,如何开发ListView?只需使用Adapter并继续使用MVP模式吗?
感谢您的阅读。

1
在Android世界中,关于MVP似乎存在很多混淆。Activity(或Fragment等)是MVP中的“V”,而不是“P”。请参见:http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/和http://saulmm.github.io/2015/02/02/A%20useful%20stack%20on%20android%20%231,%20architecture/。 - G. Lombard
@G.Lombard,现在它是http://saulmm.github.io/2015/02/02/A-useful-stack-on-android-1,-architecture/。 - CoolMind
3个回答

6

适配器是视图的一部分。事实上,所有Android依赖项都应该是视图的一部分。 将适配器与您的模型和Presenter隔离开来曾经是一项艰巨的任务。 我发布了一个名为PaperKnife的库,用于此目的。

您可以使用PaperKnife将适配器与模型和Presenter层解耦。按照以下步骤操作:

  1. Abstract the model layer using CellElement interface. Your view layer does't need to know your model.

  2. Create a class to provide the information for your row view. You can use your presenter. Implements the class CellDataProvider and create methods to provide all the information. Annotate your provider methods with @DataSource("DataId") to perform the mapping. Your data methods receive the instance of your model class. For example:

    public class SamplePresenterImpl implements SamplePresenter, CellDataProvider {
        @DataSource("Title")
        public String getTitle(Item item) {
            return item.getTitle();
        }
        // etc.
    }
    
  3. Create a ViewHolder in your adapter and implements the CellViewHolder interface. Create methods to manage the views and use DataTarget("DataId")

    static class ViewHolder extends CellViewHolder {
        @DataTarget("Title")
        public String setTitle(String title) {
            mTextViewTitle.setText(title);
        }
    }
    
  4. Execute the mapping in your adapter getView method:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // etc.
        PaperKnife.map(mList.get(position))
                        .dataProvider(mCellDataProvider)
                        .into(viewHolder);
        return convertView;
    }
    

这样,你的视图层只需要知道CellElement接口,而你的Presenter则负责为适配器提供数据。


2
是的,适配器应该是MVP模式中的P组件。实际上,ListViews几乎都是以MVP方式编写的——getView()函数每次被调用时都需要设置视图的所有值,这几乎就是一个Presenter必须做的定义。虽然也可以以MVC方式使用它——只需让getView调用View上的函数,将模型传递给View,并在View中完成这些工作。所以任何一种方式都可以,只要选择自己喜欢的即可。
如果您确实使用了具有复杂列表行的MVP模型,我建议将行作为自定义复合视图,并在其上放置更多描述性函数名称。因此,与其使用listRow.findViewById(R.id.textView).setText(filename),我会使用listRow.setFilename(filename)并让视图知道如何处理它。这有点模糊了MVP和MVC的边界,但我认为这是在适配器可读性和避免一些纯MVC带来的尴尬之间取得平衡的好方法。

6
不是这样想的。MVP、MVC 是架构模式,而适配器则是设计模式。它们不应该是相同的。请参阅讨论 https://dev59.com/VG855IYBdhLWcg3wnFtO#4243407 - Cheng
16年来,这两个词一直被视为同义词。抱歉,你在这里是错误的。 - Gabe Sechan
1
适配器是MVP中的V部分。首先,它是Android库的一部分,在P中,您只想使用Android库类作为可选参数接收它们,这样您以后可以轻松地对它们进行单元测试。其次,适配器具有用于创建和显示布局项的方法,在P中,您不应该考虑显示数据,而应该考虑控制数据。应接受@albertovecina提供的答案。 - Darek Deoniziak
@Gabe Sechan 这取决于对实现的决策,有三个选项,可以在 P 中创建 V,也可以在 V 中创建 P(然后将 V 传递给 P),或者会有第三个对象来管理所有。我所说的显示是指适配器可以控制视图,在我思考另一个问题时写错了,我的错。经过再次思考,这也可以是 P 的工作。P 在这里用于分离逻辑,但是当您希望将 P 与 Android 分离时,不能使用适配器作为 P,否则无法在 junit 中测试 P。如果适配器不应该能够操作数据,那么这就是另一个 P 的工作。 - Darek Deoniziak
1
@DarekDeoniziak,你对MVP的理解过于具体了。你所描述的是MVP的一种具体实现方式。通常来说,MVP需要一个或多个数据对象、独立的视图对象以及一个操作视图以显示数据的Presenter对象。这就是全部所需。它与单元测试、JUnit或其他无关。 - Gabe Sechan
显示剩余8条评论

1
如果活动中只有一个列表视图,则无需编写单独的Presenter,因为适配器实际上是ListView的Presenter。但是,如果您有其他需要更新的UI组件而不是ListView,则必须为这些UI组件编写单独的Presenter。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接