使用 MVP 模式实现 onItemClickListener

Implement onItemClickListener using MVP pattern

我正在学习 MVP 并感到困惑 wherehow 我应该在不破坏 mvp 概念的情况下实现 onClickListener。

已遵循本指南:https://android.jlelse.eu/recyclerview-in-mvp-passive-views-approach-8dd74633158

我的实现。

适配器:

public class RepositoriesRecyclerAdapter extends RecyclerView.Adapter<RepositoriesRecyclerAdapter.RepoViewHolder> {


private final RepositoriesListPresenter presenter;

public RepositoriesRecyclerAdapter(RepositoriesListPresenter repositoriesPresenter) {
    this.presenter = repositoriesPresenter;
}

@Override
public RepositoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    return new RepositoryViewHolder(LayoutInflater.from(parent.getContext())
                                            .inflate(R.layout.cell_repo_view, parent, false));
}

@Override
public void onBindViewHolder(RepositoryViewHolder holder, int position) {
    presenter.onBindRepositoryRowViewAtPosition(position, holder);

}

@Override
public int getItemCount() {
    return presenter.getRepositoriesRowsCount();
}

}

RepositoryViewHolder 的

public class RepositoryViewHolder extends RecyclerView.ViewHolder implements RepositoryRowView {

    TextView titleTextView;
    TextView starsCountTextView;

    public RepositoryViewHolder(View itemView) {
        super(itemView);
        titleTextView = itemView.findViewById(R.id.repoTitleText);
        starsCountTextView = itemView.findViewById(R.id.repoStarsCountText);
    }

    @Override
    public void setTitle(String title) {
        titleTextView.setText(title);
    }

    @Override
    public void setStarCount(int starCount) {
        starsCountTextView.setText(String.format("%s ★", starCount));
    }
}

RepositoryRowView

interface RepositoryRowView {

    void setTitle(String title);

    void setStarCount(int starCount);
}

我看到的所有指南都是关于在 Adapter 中创建 onClickListener 对象,然后在 ViewHolder 中使用它,但是在这个实现中,我重写了我的 Presenter 中的所有适配器功能并传递 onClickListener(android 相关的东西)会矛盾mvp模式。在这种情况下该怎么办。也许有人可以写一个解决方案 - 真的很困惑。

我的主要目标是单击 recyclerview 项目并获取项目名称(通过 toast)

OnClickListener 是来自 Android SDK 的接口。您的演示者应该对 Andriod SDK 一无所知。它应该是纯 Java 以便仅通过在 JVM 上使用单元测试即可对其进行测试。它应该对视图、RecyclerView、Adapter 或 ViewHolder 一无所知。

你的onBindViewHolder没有违反这个原则,因为它被一个抽象接口——RepositoryRowView隔开。

您应该在 adapter/viewholder 中实现 OnClickListener 并从那里呼叫您的演示者。

public class RepositoryViewHolder extends RecyclerView.ViewHolder implements RepositoryRowView, View.OnClickListener {

    TextView titleTextView;
    TextView starsCountTextView;
    RepositoriesListPresenter presenter;

    public RepositoryViewHolder(View itemView, RepositoriesListPresenter presetner) {
        super(itemView);
        titleTextView = itemView.findViewById(R.id.repoTitleText);
        starsCountTextView = itemView.findViewById(R.id.repoStarsCountText);
        this.presenter = presenter;
        itemView.setOnClickListener(this);
    }

    @Override
    public void setTitle(String title) {
        titleTextView.setText(title);
    }

    @Override
    public void setStarCount(int starCount) {
        starsCountTextView.setText(String.format("%s ★", starCount));
    }

    @Override
    public void onClick(View view) {
        if (presenter != null) {
            presenter.onItemInteraction(getAdapterPosition());
        }
    }
}

与其在你的适配器中调用演示者,我宁愿制作一个点击界面从视图中调用它,因为你将在你的视图中实例化这个适配器,保持 MVP 模式是一件好事单击视图中的元素而不是适配器本身。

这个例子是用 Kotlin 写的,但我相信你会理解的。

首先,只需创建一个简单的界面即可在用户单击列表中的任何项目时调用您的单击事件。

class EquipmentAdapter(private val context: Context,private var equipmentList:ArrayList<Equipment>,itemListener:RecyclerViewClickListener): RecyclerView.Adapter<EquipmentAdapter.EquipmentViewHolder>() {

    interface RecyclerViewClickListener {
        fun recyclerViewListClicked(v: View?, position: Int)
    }

    companion object{
        var itemClickListener: RecyclerViewClickListener? = null
        var equipmentSearchList:ArrayList<Equipment>? = null
    }

    init {
        equipmentSearchList = equipmentList
        itemClickListener = itemListener
    }

然后,在您的 ViewHolder 中,您应该调用此接口来处理点击

inner class EquipmentViewHolder(itemView: View): RecyclerView.ViewHolder(itemView), View.OnClickListener {
    val equipmentName:TextView = itemView.txt_equipmentname

    init {
        itemView.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        itemClickListener?.recyclerViewListClicked(v, adapterPosition)
    }
}

最后,只需在您调用适配器的视图中实现点击接口,然后只需在适配器内部管理演示者交互即可

class EquipmentActivity : BaseActivity(), EquipmentContract.EquipmentView, EquipmentAdapter.RecyclerViewClickListener ...

并实现点击方法

override fun recyclerViewListClicked(v: View?, position: Int) {
    presenter.onItemInteraction(position)
}

这样做,你确保列表中的元素的点击是从视图本身而不是从适配器进行的,在这里,你可以像往常一样与演示者交互,也可以做更多的事情将使您的项目保持清洁。