android studio 中的 recyclerview 项目事故(不良行为)
recyclerview item mishap(undesirable behaviour) in android studio
我有一个带有项目列表的回收站视图,其中每个项目都附有复选框。当一个复选框被选中时,它的行为是正确的,不需要的项目被选中也没有问题。但是当删除选中的项目时,不需要的项目也会被选中。
我的适配器Class:
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.TaskHolder> {
private static List<Task> tasks = new ArrayList<>();
private static OnItemClickListener listener;
private static TaskAdapter adapter = new TaskAdapter();
@NonNull
@Override
public TaskHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.task_item, parent, false);
return new TaskHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull TaskHolder holder, int position) {
Task currentTask = tasks.get(position);
holder.a_tname.setText(currentTask.getTname());
holder.a_tdate.setText(currentTask.getTDate());
holder.a_ttime.setText(currentTask.getTTime());
holder.a_tprior.setText(currentTask.getTprior());
holder.bind(tasks.get(position));
holder.bind2(tasks.get(position));
}
@Override
public int getItemCount() {
return tasks.size();
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
Collections.sort( tasks, Task.comparepriority);
notifyDataSetChanged();
}
public Task getTaskAt(int position){
return tasks.get(position);
}
class TaskHolder extends RecyclerView.ViewHolder {
private final TextView a_tname;
private final TextView a_tdate;
private final TextView a_ttime;
private final TextView a_tprior;
ImageView priorityIndicator;
CheckBox checkbox;
public TaskHolder(View itemView) {
super(itemView);
a_tname = itemView.findViewById(R.id.a_tname);
a_tdate=itemView.findViewById(R.id.a_tdate);
a_ttime = itemView.findViewById(R.id.a_ttime);
a_tprior = itemView.findViewById(R.id.a_tprior);
priorityIndicator = itemView.findViewById(R.id.priorityIndicator);
checkbox = itemView.findViewById(R.id.checkbox);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = getAdapterPosition();
if(listener!=null&&position!=RecyclerView.NO_POSITION){
listener.onItemClick(tasks.get(position));
}
}
});
}
private void bind(Task task){
int drawableId;int red = R.color.red;int yellow = R.color.yellow;int green = R.color.green;
int color1 = ContextCompat.getColor(a_tprior.getContext(), red);
int color2 = ContextCompat.getColor(a_tprior.getContext(),yellow);
int color3 = ContextCompat.getColor(a_tprior.getContext(),green);
switch(task.t_prior){
case "1": drawableId = R.drawable.ic_baseline_priority_high_24;
a_tprior.setTextColor(color1);
break;
case "2": drawableId = R.drawable.ic_baseline_priority_middle_24;
a_tprior.setTextColor(color2);
break;
case "3" : drawableId = R.drawable.ic_baseline_low_priority_24;
a_tprior.setTextColor(color3);
break;
default: drawableId = R.drawable.ic_baseline_crop_square_24;
}
priorityIndicator.setImageDrawable(ContextCompat.getDrawable(priorityIndicator.getContext(),drawableId));
}
public void bind2(Task task){
final boolean[] checked = {true};
checkbox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(checkbox.isChecked()) {
String pos = Integer.valueOf(getAdapterPosition()).toString();
PreferenceManager.getDefaultSharedPreferences(checkbox.getContext()).edit().
putBoolean("checkbox" + pos , checked[0]).apply();
Toast.makeText(checkbox.getContext(), "Way to go! Now swipe to delete", Toast.LENGTH_LONG).show();
}
else {
checked[0] =false;
String pos = Integer.valueOf(getAdapterPosition()).toString();
PreferenceManager.getDefaultSharedPreferences(checkbox.getContext()).edit().
putBoolean("checkbox" + pos, checked[0]).apply();
}
}
}); String pos = Integer.valueOf(getAdapterPosition()).toString();
boolean cb = PreferenceManager.getDefaultSharedPreferences(checkbox.getContext()).getBoolean
("checkbox" + pos, false);
checkbox.setChecked(cb);
}
}
public interface OnItemClickListener {
void onItemClick(Task ta);
}
public void setOnItemClickListener(OnItemClickListener listener){
this.listener = listener;
}
}
我要删除的代码 HomeFragment.java -
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull
RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
new AlertDialog.Builder(getActivity())
.setMessage("Do you want to delete this task?")
.setPositiveButton("Delete", ((dialog, which) ->
taskViewmodel.delete(adapter.getTaskAt(viewHolder.getAdapterPosition()))))
.setNegativeButton("Cancel", ((dialog, which) -> adapter.notifyItemChanged(position)))
.setOnCancelListener(dialog -> adapter.notifyItemChanged(position))
.create().show();
}
}).attachToRecyclerView(recyclerView);
编辑:我猜问题出在与保存复选框状态相关的代码上,因为特定项目位置的复选框被选中,所以当项目被删除时,下面的项目会取代它,所以它得到检查。假设第 2 个位置的项目被选中,我删除了那个项目,然后第 3 个位置的项目取而代之,这样就被选中了。我需要知道如何解决 this.May 我知道我应该做哪些改变来纠正这个问题?
谢谢
我不太熟悉 lambda 函数,所以我分享了我如何做同样的事情 task.I 为我自己的应用程序尝试了这段代码,它运行得非常好。
检查以下代码:
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAbsoluteAdapterPosition();//getAdapterPosition is depreciated, use getAbsoluteAdapterPosition
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setMessage("Do you want to delete this task?");
builder.setPositiveButton("Delete",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
taskViewmodel.delete(adapter.getTaskAt(viewHolder.getAdapterPosition()))
adapter.notifyItemRemoved(position);
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
adapter.notifyItemChanged(position);
}
}).show();
}
}).attachToRecyclerView(recyclerView);
在那天,没有删除的直觉,现在因为你使用它的位置在 SharedPreferences 中存储一个项目是否被选中的状态,如果你有 3 个项目,位置 =2 被选中,并且你在 pos=2 处删除这个选中的项目,然后第三个项目将取代它在 pos=2 处表现为新项目,这就是为什么当你删除一个时,所有选中的状态都会发生变化。
我想这里别无选择,只能在您的任务 class 中使用一些标识符,其中一个任务使用此 number/string 进行唯一标识,您使用该唯一标识符将项目状态存储在您的 SharedPreferences 作为键。
一种快速廉价的方法是使任务 class 的行为类似于 Room 数据库识别其自动唯一 int/long 标识符的方式。
方法是
在您的任务 class 中定义一个静态 int/long 计数器字段,用于标识您到目前为止使用了多少个 ID,以便不重复之前使用的任何 ID(即使它已被删除,现在不再使用)
在任务 class 中定义一个私有 int/long id 字段,而在任务 class 构造函数中,当您初始化一个新任务时,您将递增静态计数器字段并将此新值用作新创建任务的私有 ID 的值。
N.B :如果您为其创建对象的任务是您从 SharedPreferences/Database 中检索到的旧任务,那么您不应该增加静态值并为每个创建的任务分配一个新 ID旧 id,对于那种情况,您可能有两个构造函数,一个接受旧 id 作为参数,另一个构造函数递增 taskCounter 并获得一个新 id,您可能还有两个构造函数,一个调用另一个构造函数,以防您有其他参数在创建时重新传递给任务对象,并且您想避免在两个构造函数中重复代码。
这样当您使用 SharedPreferences 检查某些任务的状态时,您将使用任务的私有 ID 值而不是其位置。
你的任务 class 可能看起来像这样(如果有两个构造函数而构造函数中没有任何附加代码):
public class Task {
public static int tasksCounter =0;
public int taskId ;
...
//constructor for a new Task
public Task(){
this.taskId= ++tasksCounter ;
}
//constructor for an old Task
public Task(int oldId){
this.taskId= oldId ;
}
你的任务 class 可能看起来像这样(如果有两个构造函数并且你想避免代码重复):
public class Task {
public static int tasksCounter =0;
public int taskId ;
//you'd call that one if you're creating a completely new Task and it will call
the other constructor for you
public Task(){
Task(++tasksCounter)
}
//you'd call that one if you're creating a Task that you already have an id for
public Task(int id){
this.taskId= id ;
//some other code here
}
在你的适配器中,当你询问它是否被选中时,它会是这样的:
PreferenceManager.getDefaultSharedPreferences(checkbox.getContext()).getBoolean
("checkbox" + task.taskId, false);
并且当您更改复选框状态时,您将同样使用 task.taskId
而不是位置来更改其在首选项中的值。
这当然会引发另一个问题,即每次启动应用程序时,静态字段都会再次重置为 0。
所以你也应该将它的值存储在 sharedpreferences 中
- 当你创建一个新任务时,它的值会增加,所以你应该存储新值
或者
- 就在 activity 或片段通过覆盖 activity 或片段中的方法
onDestroy()
被销毁之前,您应该存储它具有的最后一个值
当您启动 activity 或片段时,您需要从 sharedpreferences 中获取它并将其分配给 Task.tasksCounter
.
N.B :以防万一你不知道,你可以通过调用 class 来获取静态字段的值,你不需要创建这个 class 获取它的值,调用下一个代码就足以获取和编辑它的值:
Task.tasksCounter
最后,
由于您现在拥有复杂的数据(任务 class),我强烈建议您停止使用 SharedPreferences 来存储所有内容,然后开始阅读并切换到 Room Database
Room Database 为您提供数据库的必要存储能力,包括:
- 拥有一个自动递增的标识符而不用担心它们的值
- 只需一行代码和一个简单的查询即可存储和获取数据,而不是使用键调用 get 100 次。
将您的任务 class 更改为具有自动增量字段的实体后,您就可以开始使用 Room 来存储您的任务了。
我有一个带有项目列表的回收站视图,其中每个项目都附有复选框。当一个复选框被选中时,它的行为是正确的,不需要的项目被选中也没有问题。但是当删除选中的项目时,不需要的项目也会被选中。
我的适配器Class:
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.TaskHolder> {
private static List<Task> tasks = new ArrayList<>();
private static OnItemClickListener listener;
private static TaskAdapter adapter = new TaskAdapter();
@NonNull
@Override
public TaskHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.task_item, parent, false);
return new TaskHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull TaskHolder holder, int position) {
Task currentTask = tasks.get(position);
holder.a_tname.setText(currentTask.getTname());
holder.a_tdate.setText(currentTask.getTDate());
holder.a_ttime.setText(currentTask.getTTime());
holder.a_tprior.setText(currentTask.getTprior());
holder.bind(tasks.get(position));
holder.bind2(tasks.get(position));
}
@Override
public int getItemCount() {
return tasks.size();
}
public void setTasks(List<Task> tasks) {
this.tasks = tasks;
Collections.sort( tasks, Task.comparepriority);
notifyDataSetChanged();
}
public Task getTaskAt(int position){
return tasks.get(position);
}
class TaskHolder extends RecyclerView.ViewHolder {
private final TextView a_tname;
private final TextView a_tdate;
private final TextView a_ttime;
private final TextView a_tprior;
ImageView priorityIndicator;
CheckBox checkbox;
public TaskHolder(View itemView) {
super(itemView);
a_tname = itemView.findViewById(R.id.a_tname);
a_tdate=itemView.findViewById(R.id.a_tdate);
a_ttime = itemView.findViewById(R.id.a_ttime);
a_tprior = itemView.findViewById(R.id.a_tprior);
priorityIndicator = itemView.findViewById(R.id.priorityIndicator);
checkbox = itemView.findViewById(R.id.checkbox);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = getAdapterPosition();
if(listener!=null&&position!=RecyclerView.NO_POSITION){
listener.onItemClick(tasks.get(position));
}
}
});
}
private void bind(Task task){
int drawableId;int red = R.color.red;int yellow = R.color.yellow;int green = R.color.green;
int color1 = ContextCompat.getColor(a_tprior.getContext(), red);
int color2 = ContextCompat.getColor(a_tprior.getContext(),yellow);
int color3 = ContextCompat.getColor(a_tprior.getContext(),green);
switch(task.t_prior){
case "1": drawableId = R.drawable.ic_baseline_priority_high_24;
a_tprior.setTextColor(color1);
break;
case "2": drawableId = R.drawable.ic_baseline_priority_middle_24;
a_tprior.setTextColor(color2);
break;
case "3" : drawableId = R.drawable.ic_baseline_low_priority_24;
a_tprior.setTextColor(color3);
break;
default: drawableId = R.drawable.ic_baseline_crop_square_24;
}
priorityIndicator.setImageDrawable(ContextCompat.getDrawable(priorityIndicator.getContext(),drawableId));
}
public void bind2(Task task){
final boolean[] checked = {true};
checkbox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(checkbox.isChecked()) {
String pos = Integer.valueOf(getAdapterPosition()).toString();
PreferenceManager.getDefaultSharedPreferences(checkbox.getContext()).edit().
putBoolean("checkbox" + pos , checked[0]).apply();
Toast.makeText(checkbox.getContext(), "Way to go! Now swipe to delete", Toast.LENGTH_LONG).show();
}
else {
checked[0] =false;
String pos = Integer.valueOf(getAdapterPosition()).toString();
PreferenceManager.getDefaultSharedPreferences(checkbox.getContext()).edit().
putBoolean("checkbox" + pos, checked[0]).apply();
}
}
}); String pos = Integer.valueOf(getAdapterPosition()).toString();
boolean cb = PreferenceManager.getDefaultSharedPreferences(checkbox.getContext()).getBoolean
("checkbox" + pos, false);
checkbox.setChecked(cb);
}
}
public interface OnItemClickListener {
void onItemClick(Task ta);
}
public void setOnItemClickListener(OnItemClickListener listener){
this.listener = listener;
}
}
我要删除的代码 HomeFragment.java -
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull
RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
new AlertDialog.Builder(getActivity())
.setMessage("Do you want to delete this task?")
.setPositiveButton("Delete", ((dialog, which) ->
taskViewmodel.delete(adapter.getTaskAt(viewHolder.getAdapterPosition()))))
.setNegativeButton("Cancel", ((dialog, which) -> adapter.notifyItemChanged(position)))
.setOnCancelListener(dialog -> adapter.notifyItemChanged(position))
.create().show();
}
}).attachToRecyclerView(recyclerView);
编辑:我猜问题出在与保存复选框状态相关的代码上,因为特定项目位置的复选框被选中,所以当项目被删除时,下面的项目会取代它,所以它得到检查。假设第 2 个位置的项目被选中,我删除了那个项目,然后第 3 个位置的项目取而代之,这样就被选中了。我需要知道如何解决 this.May 我知道我应该做哪些改变来纠正这个问题? 谢谢
我不太熟悉 lambda 函数,所以我分享了我如何做同样的事情 task.I 为我自己的应用程序尝试了这段代码,它运行得非常好。 检查以下代码:
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAbsoluteAdapterPosition();//getAdapterPosition is depreciated, use getAbsoluteAdapterPosition
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setMessage("Do you want to delete this task?");
builder.setPositiveButton("Delete",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
taskViewmodel.delete(adapter.getTaskAt(viewHolder.getAdapterPosition()))
adapter.notifyItemRemoved(position);
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
adapter.notifyItemChanged(position);
}
}).show();
}
}).attachToRecyclerView(recyclerView);
在那天,没有删除的直觉,现在因为你使用它的位置在 SharedPreferences 中存储一个项目是否被选中的状态,如果你有 3 个项目,位置 =2 被选中,并且你在 pos=2 处删除这个选中的项目,然后第三个项目将取代它在 pos=2 处表现为新项目,这就是为什么当你删除一个时,所有选中的状态都会发生变化。
我想这里别无选择,只能在您的任务 class 中使用一些标识符,其中一个任务使用此 number/string 进行唯一标识,您使用该唯一标识符将项目状态存储在您的 SharedPreferences 作为键。
一种快速廉价的方法是使任务 class 的行为类似于 Room 数据库识别其自动唯一 int/long 标识符的方式。
方法是
在您的任务 class 中定义一个静态 int/long 计数器字段,用于标识您到目前为止使用了多少个 ID,以便不重复之前使用的任何 ID(即使它已被删除,现在不再使用)
在任务 class 中定义一个私有 int/long id 字段,而在任务 class 构造函数中,当您初始化一个新任务时,您将递增静态计数器字段并将此新值用作新创建任务的私有 ID 的值。 N.B :如果您为其创建对象的任务是您从 SharedPreferences/Database 中检索到的旧任务,那么您不应该增加静态值并为每个创建的任务分配一个新 ID旧 id,对于那种情况,您可能有两个构造函数,一个接受旧 id 作为参数,另一个构造函数递增 taskCounter 并获得一个新 id,您可能还有两个构造函数,一个调用另一个构造函数,以防您有其他参数在创建时重新传递给任务对象,并且您想避免在两个构造函数中重复代码。
这样当您使用 SharedPreferences 检查某些任务的状态时,您将使用任务的私有 ID 值而不是其位置。
你的任务 class 可能看起来像这样(如果有两个构造函数而构造函数中没有任何附加代码):
public class Task {
public static int tasksCounter =0;
public int taskId ;
...
//constructor for a new Task
public Task(){
this.taskId= ++tasksCounter ;
}
//constructor for an old Task
public Task(int oldId){
this.taskId= oldId ;
}
你的任务 class 可能看起来像这样(如果有两个构造函数并且你想避免代码重复):
public class Task {
public static int tasksCounter =0;
public int taskId ;
//you'd call that one if you're creating a completely new Task and it will call
the other constructor for you
public Task(){
Task(++tasksCounter)
}
//you'd call that one if you're creating a Task that you already have an id for
public Task(int id){
this.taskId= id ;
//some other code here
}
在你的适配器中,当你询问它是否被选中时,它会是这样的:
PreferenceManager.getDefaultSharedPreferences(checkbox.getContext()).getBoolean
("checkbox" + task.taskId, false);
并且当您更改复选框状态时,您将同样使用 task.taskId
而不是位置来更改其在首选项中的值。
这当然会引发另一个问题,即每次启动应用程序时,静态字段都会再次重置为 0。
所以你也应该将它的值存储在 sharedpreferences 中
- 当你创建一个新任务时,它的值会增加,所以你应该存储新值
或者 - 就在 activity 或片段通过覆盖 activity 或片段中的方法
onDestroy()
被销毁之前,您应该存储它具有的最后一个值 当您启动 activity 或片段时,您需要从 sharedpreferences 中获取它并将其分配给Task.tasksCounter
.
N.B :以防万一你不知道,你可以通过调用 class 来获取静态字段的值,你不需要创建这个 class 获取它的值,调用下一个代码就足以获取和编辑它的值:
Task.tasksCounter
最后,
由于您现在拥有复杂的数据(任务 class),我强烈建议您停止使用 SharedPreferences 来存储所有内容,然后开始阅读并切换到 Room Database
Room Database 为您提供数据库的必要存储能力,包括:
- 拥有一个自动递增的标识符而不用担心它们的值
- 只需一行代码和一个简单的查询即可存储和获取数据,而不是使用键调用 get 100 次。
将您的任务 class 更改为具有自动增量字段的实体后,您就可以开始使用 Room 来存储您的任务了。