如何在尊重 MVVM 架构的同时,在回收视图中的项目内的按钮上提交点击事件?

How to submit a click event on a button inside an item in a recylcerview while respecting MVVM architecture?

我有一个问题,它是技术和概念上的,我正在尝试使用 MVVM 架构,我不想离开它,所以我有一个项目列表,每个项目都有一个删除按钮,我使用 recylcerview 显示列表,所以我使用适配器,现在当我在每一行调用一个事件时,我这样做:

这是我的Adapter,去ViewHolder里面的deleteBtn。

    public class ContractListAdapter extends RecyclerView.Adapter<ContractListAdapter.ContractViewHolder> {

    List<ContractModel> contracts;
    Context context;
    public ContractListAdapter(Context context, List<ContractModel> contracts){
        this.context = context;
        this.contracts = contracts;
    }

    @NonNull
    @Override
    public ContractViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ContratBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.contrat, parent, false); // ContratBinding >> as your list item layout named "contrat"
        return new ContractViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(@NonNull ContractViewHolder holder, int position) {
        holder.binding.setContract(contracts.get(position));
    }

    @Override
    public int getItemCount() {
        return this.contracts.size();
    }

    public class ContractViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

        ContractsListViewModel listViewModel;

        @BindView(R.id.courtier)
        TextView courtier;

        @BindView(R.id.delete)
        Button deleteButton;

        ContratBinding binding;

        @BindView(R.id.contratImage)
        ImageView contractImage;

        public ContractViewHolder(@NonNull ContratBinding binding){
            super(binding.getRoot());
            this.binding = binding;
            ButterKnife.bind(this, itemView);

            itemView.setOnClickListener(this);

            courtier.setOnClickListener(this);
            deleteButton.setOnClickListener(this);
            contractImage.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {

            int position = getAdapterPosition();
            int idOfContract = contracts.get(position).getId();

            if(deleteButton.getId() == v.getId()){
                // action here
            }
            else {
                Intent myIntent = new Intent(context, ContractActivity.class);
                myIntent.putExtra("key", idOfContract + ""); //Optional parameters
                context.startActivity(myIntent);
            }
        }
    }
}

这是我的 MainActivity:

public class MainActivity extends AppCompatActivity {

    ContractsListViewModel contractsListViewModel;

    @BindView(R.id.contractList)
    RecyclerView contractList;

    @BindView(R.id.newContractBtn)
    Button newContractBtn;

    @BindView(R.id.listLoading)
    ProgressBar listLoading;

    @BindView(R.id.listError)
    TextView listError;


    RecyclerView.Adapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        ActivityMainBinding main = DataBindingUtil.setContentView(this, R.layout.activity_main);

        ButterKnife.bind(this);

        main.contractList.setLayoutManager(new LinearLayoutManager(this));

        contractsListViewModel = ViewModelProviders.of(this).get(ContractsListViewModel.class);
        contractsListViewModel.call();

        contractsListViewModel.contractList.observe(this,contractModels -> {
            if(contractModels != null){

                contractList.setVisibility((View.VISIBLE));
                adapter = new ContractListAdapter(MainActivity.this, contractModels);
                main.contractList.setAdapter(adapter);
            }
        });

        contractsListViewModel.isLoading.observe(this,isLoading -> {
            if(isLoading != null){
                listLoading.setVisibility(isLoading ? View.VISIBLE : View.GONE );
                if(isLoading){
                    listError.setVisibility(View.GONE);
                    contractList.setVisibility((View.GONE));
                }
            }
        });

        contractsListViewModel.error.observe(this,Error -> {
            if(Error != null){
                listError.setVisibility(Error ? View.VISIBLE : View.GONE );
            }
        });

        newContractBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent newContractIntent = new Intent(getApplicationContext(), NewContractActivity.class);
                MainActivity.this.startActivity(newContractIntent);
            }
        });

    }

    @Override
    public void onResume()
    {
        super.onResume();
        contractsListViewModel = ViewModelProviders.of(this).get(ContractsListViewModel.class);
        contractsListViewModel.call();
    }
}

因此,当我调用一个动作并观察它时,我通常会在我的 MainActivity 中执行,因为我在 MainActivity 中实例化了我的 ListViewModel,并调用了我的 ViewModel 方法,但现在我觉得我必须在此实例化相同的 ViewModel ViewHolder.

首先我不知道怎么做,因为当我在 MainActivity 中调用 'this' 时它指的是 MainActvity 但如果我在 ViewHolder 中调用它(而不是 activity ),它不会'行不通,那么我将如何在 ViewHolder 中调用我的 ViewModel。

contractsListViewModel = ViewModelProviders.of(this).get(ContractsListViewModel.class);

其次,更重要的是,如果我这样做,我是否尊重 MVVM 架构?

因为我是如何将它概念化的:

    @Override
    public void onClick(View v) {

        int position = getAdapterPosition();
        int idOfContract = contracts.get(position).getId();

        // Delete Button clicked
        if(deleteButton.getId() == v.getId()){

           // I call the ViewModel

           // call the action I want ( delete request )

           // observe it in my MainActivity

        }
    }

任何帮助将不胜感激!

谢谢。

您必须使用绑定适配器在 MVVM 架构中使用数据进行按钮点击 binding.All 您的按钮点击将在同一 class 中进行。

<android.support.design.widget.Button
            ...
            android:onClick="@{handlers::ButtonClicked}" />

public class MyClickHandlers {

        public void onButtonClicked(View view) {
            Toast.makeText(getApplicationContext(), "button clicked!", Toast.LENGTH_SHORT).show();
        }
}

我会在 Activity 中创建一个回调函数并让 Activity 实现它:

interface OnDeleteClicklistener {
     void onDelete(int id) 
}

然后 activity 将在此方法中调用其 ViewModel:

void onDelete(int id) {
     viewModel.deleteItem(id)
}

然后我不会将 ViewModel 传递给适配器,而是将 activity 作为接口传递:

recyclerView.setAdapter(MyAdapter(this, listOfItems)

适配器构造函数:

MyAdapter(OnDeleteClicklistener listener, List<Item> list) 

这是您更新后的 Activity。

public class MainActivity extends AppCompatActivity {

    ContractsListViewModel contractsListViewModel;

    @BindView(R.id.contractList)
    RecyclerView contractList;

    @BindView(R.id.newContractBtn)
    Button newContractBtn;

    @BindView(R.id.listLoading)
    ProgressBar listLoading;

    @BindView(R.id.listError)
    TextView listError;


    RecyclerView.Adapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        ActivityMainBinding main = DataBindingUtil.setContentView(this, R.layout.activity_main);

        ButterKnife.bind(this);

        main.contractList.setLayoutManager(new LinearLayoutManager(this));

        contractsListViewModel = ViewModelProviders.of(this).get(ContractsListViewModel.class);
        contractsListViewModel.call();

        contractsListViewModel.contractList.observe(this,contractModels -> {
            if(contractModels != null){
                contracts.addAll(contractModels);

                contractList.setVisibility((View.VISIBLE));
                adapter = new ContractListAdapter(MainActivity.this, contractModels, new ContractListAdapter.OnButtonPressed() {
                    @Override
                    public void onClicked(int position) {
                        contractsListViewModel.callDelete(position);
                    }
                });
                main.contractList.setAdapter(adapter);
            }
        });

        contractsListViewModel.isLoading.observe(this,isLoading -> {
            if(isLoading != null){
                listLoading.setVisibility(isLoading ? View.VISIBLE : View.GONE );
                if(isLoading){
                    listError.setVisibility(View.GONE);
                    contractList.setVisibility((View.GONE));
                }
            }
        });

        contractsListViewModel.error.observe(this,Error -> {
            if(Error != null){
                listError.setVisibility(Error ? View.VISIBLE : View.GONE );
            }
        });

        newContractBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent newContractIntent = new Intent(getApplicationContext(), NewContractActivity.class);
                MainActivity.this.startActivity(newContractIntent);
            }
        });

    }

    @Override
    public void onResume()
    {
        super.onResume();
        contractsListViewModel = ViewModelProviders.of(this).get(ContractsListViewModel.class);
        contractsListViewModel.call();
    }
}

这是你更新后的ContractListAdapter

public class ContractListAdapter extends RecyclerView.Adapter<ContractListAdapter.ContractViewHolder> {

    List<ContractModel> contracts;
    Context context;
    OnButtonPressed onButtonPressed;

    public ContractListAdapter(Context context, List<ContractModel> contracts,OnButtonPressed onButtonPressed) {
        this.context = context;
        this.contracts = contracts;
        this.onButtonPressed = onButtonPressed;
    }

    @NonNull
    @Override
    public ContractViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ContratBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.contrat, parent, false); // ContratBinding >> as your list item layout named "contrat"
        return new ContractViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(@NonNull ContractViewHolder holder, int position) {
        holder.binding.setContract(contracts.get(position));
    }

    @Override
    public int getItemCount() {
        return this.contracts.size();
    }

    public class ContractViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        ContractsListViewModel listViewModel;

        @BindView(R.id.courtier)
        TextView courtier;

        @BindView(R.id.delete)
        Button deleteButton;

        ContratBinding binding;

        @BindView(R.id.contratImage)
        ImageView contractImage;

        public ContractViewHolder(@NonNull ContratBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
            ButterKnife.bind(this, itemView);

            itemView.setOnClickListener(this);

            courtier.setOnClickListener(this);
            deleteButton.setOnClickListener(this);
            contractImage.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {

            int position = getAdapterPosition();
            int idOfContract = contracts.get(position).getId();

            if (deleteButton.getId() == v.getId()) {
                // action here
                onButtonPressed.onClicked(position);
            } else {
                Intent myIntent = new Intent(context, ContractActivity.class);
                myIntent.putExtra("key", idOfContract + ""); //Optional parameters
                context.startActivity(myIntent);
            }
        }
    }

    interface OnButtonPressed {
        void onClicked(int position);
    }
}