使用MVP模式实现onItemClickListener

时间:2017-08-31 12:36:58

标签: android onclicklistener mvp

我正在学习MVP,感到困惑 应该如何实现onClickListener而不破坏mvp概念。

按照本指南: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中使用它,但是在这个实现中,我覆盖了我的演示者中的所有适配器函数,并且传递onClickListener(与android相关的东西)会与mvp模式相矛盾。在这种情况下该怎么做。也许有人可以写一个解决方案 - 真的很困惑。

我的主要目标是点击recyclerview项目并获取项目名称(通过吐司)

2 个答案:

答案 0 :(得分:11)

OnClickListener是Android SDK的界面。您的演示者不应该对Andriod SDK有任何了解。它应该是纯Java,因此只需在JVM上使用单元测试即可对其进行测试。它不应该对视图,RecyclerView,Adapter和ViewHolder了解任何信息。

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

您应该在适配器/视图中实现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());
        }
    }
}

答案 1 :(得分:1)

与其在适配器内部调用演示者,不如在视图中创建单击界面以从视图中调用它,因为您将在视图中实例化此适配器,所以将MVP模式与单击保持在一起是一件好事。视图内部而不是适配器本身中的元素。

此示例位于Kotlin中,但我相信您会理解的。

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

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 ...

并实现click方法

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

这样做,请确保单击列表中的元素是从视图本身而不是从适配器中进行的,在这里,您可以像往常一样与演示者进行交互,并且还可以做更多的事情您的项目干净。