android studio中的recyclerview项目事故(不良行为)
Posted
技术标签:
【中文标题】android studio中的recyclerview项目事故(不良行为)【英文标题】:recyclerview item mishap(undesirable behaviour) in android studio 【发布时间】:2021-12-09 22:07:06 【问题描述】:我有一个包含项目列表的回收站视图,每个项目都附有复选框。选中复选框时,它会正常运行,并且检查不需要的项目没有问题。但是当一个选中的项目被删除时,不需要的项目也会被选中。
我的适配器类:
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);
编辑:我想问题出在与保存复选框状态相关的代码上,因为选中了特定项目位置的复选框,因此当删除该项目时,下面的项目将取代它,因此它被选中。假设第二个位置的项目被选中,我删除了那个项目,然后第三个位置的项目取而代之,这样就被选中了。我需要知道如何解决这个问题。我知道我应该做些什么改变来纠正这个问题吗? 谢谢你
【问题讨论】:
从列表中删除项目时是否通知更改? 我提供 notifyItemChanged(position)。我现在已经发布了我的删除代码 在 notifyItemChanged(position) 之后使用 adapter.notifyDataSetChanged()。 @Danish 还是不行 @Danish 不,它仍然无法正常工作 【参考方案1】:回到那天,没有删除直觉,现在因为您使用其位置存储是否在 SharedPreferences 中检查项目的状态,如果您有 3 个项目,其中位置 =2 被选中,并且您删除这个在 pos=2 时选中的项目,然后第三个项目将在 pos=2 处作为新项目代替它,这就是为什么当你删除一个时,所有选中的状态都会转移。
我想这里没有其他选择,只能在您的 Task 类中使用某个标识符,其中一个 Task 使用此数字/字符串唯一标识,您使用该唯一标识符将项目状态存储在 SharedPreferences 中作为键。
一种快速廉价的方法是让 Task 类的行为类似于 Room 数据库识别其自动唯一 int/long 标识符的方式。 方法是通过
在您的 Task 类中定义一个静态 int/long 计数器字段,该字段标识您到目前为止使用了多少个 id,以便不重复之前获取的任何 id(即使它已被删除且现在未使用)
在 Task 类中定义一个私有 int/long id 字段,而在 Task 类构造函数中,当您初始化一个新任务时,您将增加静态计数器字段并将这个新值用作您的私有 id 的值您新创建的任务。 注意:如果您正在为其创建对象的任务是您从 SharedPreferences/Database 检索到的已具有旧 ID 的旧任务,则不应增加静态值并为每个创建的任务分配新 ID,在这种情况下您可能有两个构造函数,一个接受旧 id 作为参数,一个增加 taskCounter 并获取新 id,您可能还有两个构造函数,一个调用另一个,以防您有一些其他参数传递给任务对象在创建时,您希望避免在两个构造函数中重复代码。
这样,当您使用 SharedPreferences 检查某个任务的状态时,您将使用 Task 的私有 id 值而不是其位置。
您的 Task 类可能如下所示(如果有两个构造函数,而构造函数中没有任何附加代码):
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 ;
您的 Task 类可能如下所示(如果有两个构造函数并且您希望避免代码重复):
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 中
当你创建一个新任务时,它的值会增加,所以你应该存储新值 或 就在通过覆盖活动或片段中的onDestroy()
方法而破坏活动或片段之前,您应该存储它的最后一个值
当您开始您的活动或片段时,您需要从 sharedpreferences 中获取它并将其分配给Task.tasksCounter
。
注意:如果你不知道,你可以通过调用它自己的类来获取静态字段的值,你不需要创建这个类的新对象来获取它的值,调用下一个代码是足以获取和编辑它的值:
Task.tasksCounter
最后, 由于您现在拥有复杂的数据(任务类),我强烈建议您停止使用 SharedPreferences 来存储所有内容,然后开始阅读并切换到 Room Database
Room Database 为您提供数据库所需的存储能力,包括:
拥有一个自动增量标识符,而不必担心它们的值 只需一行代码和一个简单的查询即可存储和获取数据,而不是使用键调用 100 次。将您的 Task 类更改为具有自动增量字段的实体后,您就可以开始使用 Room 来存储您的任务了。
【讨论】:
非常感谢,我的Task 类中已经有了task.id,但没想到这样使用它。这帮助了很多!【参考方案2】:我对 lambda 函数不太熟悉,所以我将分享我将如何完成相同的任务。我为自己的应用程序尝试了这段代码,它运行得非常好。 检查以下代码:
@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);
【讨论】:
它仍然没有解决我的复选框问题。我想问题出在与保存复选框状态相关的代码上,因为选中了特定项目位置的复选框,因此当删除该项目时,下面的项目将取而代之,因此它会被选中。假设第二个位置的项目被选中,我删除了那个项目,然后第三个位置的项目取而代之,这样就被选中了。我需要知道如何解决这个问题,但感谢您的帮助 是的,这就是如果您查看复选框代码的原因,您将在共享首选项中保存复选框的状态,当您删除回收站视图项目时,您的复选框再次设置为选中状态。 尝试将您的复选框设为公开静态,然后在您的 onSwipe 中访问它并设置为 true。像这样:checkbox.setChecked(false) onSwiped() 在 HomeFragment.java 中,而复选框在 TaskAdapter.java 中,它们在两个不同的活动中,那么我该如何访问它呢? 公开静态。以上是关于android studio中的recyclerview项目事故(不良行为)的主要内容,如果未能解决你的问题,请参考以下文章
无法按照 Google“入门”页面中的说明在 Android Studio 中添加地图; Android Studio 报错
android studio application怎么创建