android - 数据绑定 Recyclerview 和 onClick
问题描述
好的,我再试一次。上次我询问在 recyclerview 和 item 之间传递数据,一个人通过点击帮助我打开了项目,但我仍然不知道如何在新活动中显示点击项目的数据。我想单击一个项目,然后在新活动中显示该项目的数据。在此活动中,我想编辑数据。有谁知道该怎么做?我需要任何想法。
带有 OnItemClickListener 接口的 RecyclerView 适配器:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.TaskViewHolder> {
private List<MainViewModel> mTasks;
private List<Task> tasks = new ArrayList<>();
private Context context;
private EditTaskViewModel editTaskViewModel;
public RecyclerViewAdapter(List<MainViewModel> tasks, Context context, EditTaskViewModel editTaskViewModel) {
this.mTasks = tasks;
this.context = context;
this.editTaskViewModel = editTaskViewModel;
}
@NonNull
@Override
public RecyclerViewAdapter.TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final RecyclerViewItemBinding binding = DataBindingUtil.inflate(inflater, R.layout.recycler_view_item, parent, false);
binding.setItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(View view) {
Intent intent = new Intent(view.getContext(), EditTaskActivity.class);
intent.putExtra("id", binding.getPosition());
view.getContext().startActivity(intent);
Toast.makeText(view.getContext(), "ID " + binding.getPosition(), Toast.LENGTH_SHORT).show();
}
});
return new TaskViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final RecyclerViewAdapter.TaskViewHolder holder, final int position) {
Task currentTask = tasks.get(position);
holder.mBinding.descriptionItem.setText(currentTask.getDescription());
holder.mBinding.dateItem.setText(currentTask.getDate());
holder.mBinding.timeItem.setText(currentTask.getTime());
holder.mBinding.setPosition(position);
}
@Override
public int getItemCount() {
return tasks.size();
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
notifyDataSetChanged();
}
public Task getTaskPosition(int position) {
return tasks.get(position);
}
public class TaskViewHolder extends RecyclerView.ViewHolder {
private final RecyclerViewItemBinding mBinding;
public TaskViewHolder(RecyclerViewItemBinding binding) {
super(binding.getRoot());
this.mBinding = binding;
}
public void bind (MainViewModel mainViewModel){
mBinding.setItemView(mainViewModel);
mBinding.executePendingBindings();
}
}
public interface OnItemClickListener {
void onItemClick(View view);
}
项目 XML 文件:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="itemView"
type="com.example.daniellachacz.taskmvvm.viewmodel.MainViewModel">
</variable>
<variable
name="itemClickListener"
type="com.example.daniellachacz.taskmvvm.adapter.RecyclerViewAdapter.OnItemClickListener">
</variable>
<variable
name="task"
type="com.example.daniellachacz.taskmvvm.model.Task">
</variable>
<variable
name="position"
type="int">
</variable>
</data>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="120dp"
android:shadowColor="@color/colorPrimary"
android:backgroundTint="@color/cardview_shadow_end_color">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="110dp"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
android:onClick="@{(view)-> itemClickListener.onItemClick(view)}">
<TextView
android:id="@+id/description_item"
android:layout_width="250dp"
android:layout_height="96dp"
android:layout_marginStart="5dp"
android:layout_marginTop="9dp"
android:layout_marginBottom="5dp"
android:text="@{itemView.description}"
android:textSize="18sp"
android:textColor="#020202"
android:focusable="true" />
<TextView
android:id="@+id/date_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="9dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="@{itemView.date}"
android:textColor="#020202"
android:textSize="16sp" />
<TextView
android:id="@+id/time_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_alignStart="@+id/date_item"
android:layout_marginBottom="10dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="@{itemView.time}"
android:textColor="#020202"
android:textSize="16sp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</layout>
创建:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
floatingActionButton = findViewById(R.id.floating_action_button);
List<Task> tasks = new ArrayList<>();
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
final RecyclerViewAdapter recyclerViewAdapter = new RecyclerViewAdapter(context, tasks);
recyclerView.setAdapter(recyclerViewAdapter);
mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
mainViewModel.getAllTasks().observe(this, recyclerViewAdapter::setTasks);
解决方案
以下是一些您可能会觉得有用的建议:
不要依赖ViewModel
适配器中的 s 。ViewModel
s 旨在处理来自视图(片段或活动)的事件,并通过一些可观察的机制(最常见的LiveData
实例)将更新广播回视图。直接在适配器内引用你ViewModel
的 s 是不好的,因为它将它们耦合在一起。这意味着如果需要,您将很难使用不同的适配器重用您的适配器ViewModel
。我知道目前似乎不太可能,但请相信我。应用更改后,您的适配器应如下所示:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.TaskViewHolder> {
private LayoutInflater mLayoutInflater;
private List<Task> mTasks;
private OnItemClickListener mOnItemClickListener;
public RecyclerViewAdapter(@NonNull Context context, @NonNull List<Task> tasks) {
mLayoutInflater = LayoutInflater.fromContext(context);
mTasks = tasks;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
@NonNull
@Override
public RecyclerViewAdapter.TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final RecyclerViewItemBinding binding = DataBindingUtil.inflate(mLayoutInflater, R.layout.recycler_view_item, parent, false);
return new TaskViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final RecyclerViewAdapter.TaskViewHolder holder, final int position) {
Task currentTask = tasks.get(position);
holder.bind(currentTask, mOnItemClickListener);
}
@Override
public int getItemCount() {
return tasks.size();
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
notifyDataSetChanged();
}
public Task getTaskPosition(int position) {
return tasks.get(position);
}
public class TaskViewHolder extends RecyclerView.ViewHolder {
private final RecyclerViewItemBinding mBinding;
public TaskViewHolder(RecyclerViewItemBinding binding) {
super(binding.getRoot());
this.mBinding = binding;
}
public void bind (Task item, OnItemClickListener onItemClickListener) {
mBinding.setItem(item);
mBinding.executePendingBindings();
itemView.setOnClickListener(view -> {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(view, item);
}
}
}
}
public interface OnItemClickListener {
void onItemClick(View view, Task item);
}
}
该OnItemClickListener.onItemClick()
方法现在将视图和项目本身作为参数传递。这是向任何可能感兴趣的人公开单击的项目的最简单方法。点击监听器现在设置在适配器级别,使用setOnItemClickListener()
.
项目视图的设置OnClickListener
现在在 的bind()
方法中完成TaskViewHolder
。绑定时,我们知道要填充视图的确切项目,因此我们可以将其返回给OnItemClickListener
.
您还必须简化布局,因为有很多东西并不是真正需要的。它可能看起来像这样:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="task"
type="com.example.daniellachacz.taskmvvm.model.Task">
</variable>
</data>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="120dp"
android:shadowColor="@color/colorPrimary"
android:backgroundTint="@color/cardview_shadow_end_color">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="110dp"
android:layout_marginBottom="6dp"
android:layout_marginTop="6dp"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp">
<TextView
android:id="@+id/description_item"
android:layout_width="250dp"
android:layout_height="96dp"
android:layout_marginStart="5dp"
android:layout_marginTop="9dp"
android:layout_marginBottom="5dp"
android:text="@{item.description}"
android:textSize="18sp"
android:textColor="#020202"
android:focusable="true" />
<TextView
android:id="@+id/date_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="9dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="@{item.date}"
android:textColor="#020202"
android:textSize="16sp" />
<TextView
android:id="@+id/time_item"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_alignStart="@+id/date_item"
android:layout_marginBottom="10dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="@{item.time}"
android:textColor="#020202"
android:textSize="16sp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</layout>
唯一的变量是 the item
,我们将它的属性绑定到TextView
s。
我想这应该足以让你继续前进。
只是与问题没有直接关系但很重要的其他几件事。
setTask()
空安全 -在适配器中调用时,您永远不会检查输入。客户端可能会通过null
并导致到处崩溃。您应该尝试防止这种情况发生。- 使用时调用
notifyDataSetChanged()
不是一个好习惯,RecyclerView.Adapter
因为这将取消RecyclerView
. 最好使用其他notify...
方法。您可能想DiffUtil
在某个时候进行检查。
推荐阅读
- c++ - 收到此错误“预期在'之前的主要表达式'。' 令牌”是 C++ 的新手
- c# - 使用其他表单中的复选框/按钮
- r - r:如何根据每个列名的单独后缀同时更改多个列名
- rpmbuild - 安装 RPM 后如何将消息写入屏幕?
- html - 如何在 BS 中将文本环绕在图像周围,将文本置于移动设备上的图像下方
- spring-mvc - 如何在页面重新加载时在 JSP 页面中保留类型文件的输入
- python - Pandas Plotting 在 x 轴上显示所有日期值(matplolib 仅显示少数值),格式为 MMM-YYYY
- javascript - 使用 React Redux 时出现以下错误 - TypeError: Cannot read property 'id' of undefined
- swiftui - SwiftUI HealthKit 氧饱和度
- react-native - REACT NATIVE - Expo AV didJustFinish