问题
I have a problem with my recyclerView.
I'm developing a "ToDo" App where the user can add some tasks to do and these tasks are displayed with a recyclerView. I'm also using Room, so I have a ViewModel, a Repository and a DAO.
Inside the method onOptionsItemSelected
(MainActivity) I have some filter like "Pending Tasks", Private Tasks, ...
I would like to use an ItemTouchHelper
to delete (swipe Left) and update a task from "pending" to "completed" (swipe Right).
PROBLEM
When I start the app I can add new tasks, delete it with the swipe left and "complete" it with the swipe right BUT when I filter my recyclerView and I obtain, for example, the completed tasks and delete one of these completed tasks with the swipe, I don't delete the task swiped but the app delete the task in the same position BUT in the recyclerView without filter, so in the recyclerView loaded when the app started.
MY CODE
MainActivity
public class MainActivity extends AppCompatActivity{
//abbiamo bisogno di due valori differenti percè dobbiamo distinguere due situazioni differenti: EDIT e ADD
public static final int ADD_TASK_REQUEST = 1;
public static final int EDIT_TASK_REQUEST = 2;
private TaskDatabase db;
private TaskDao taskDao;
public static final String EXTRAS=
"com.example.todoapp.EXTRAS";
/*recupera la viewmodel e assegna l'observer al LiveData*/
private TaskViewModel taskViewModel;
private String classe = "*";
private String stato = "pending";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
final TaskAdapter adapter = new TaskAdapter();
recyclerView.setAdapter(adapter);
/*Dobbiamo chidere al sistema android per la viewmodel perchè il sistema sa quando viene create
* una nuova istanza*/
taskViewModel = ViewModelProviders.of(this).get(TaskViewModel.class);
taskViewModel.getAllTasks().observe(this, new Observer<List<Task>>() {
//chiamato ogni volta che i dati dentro alla LiveData cambiano
@Override
public void onChanged(List<Task> tasks) {
/*Ogni volta che onChange è triggerato, (dovrebbe essere ogni volta che i dati nella tabella Task corrispondente cambiano)
* "apdater" dovrebbe essere aggiornato con la lista di task che passiamo come argomento e refressare il dataset*/
adapter.setTasks(tasks);
}
});
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(metodo(adapter));
itemTouchHelper.attachToRecyclerView(recyclerView);
//metodo per la modica dei tasks, una volta cliccato il task si possono modificare i suoi valori
adapter.setOnItemClickListener(new TaskAdapter.OnItemClickListener() {
@Override
public void onItemClick(Task task) {
Intent intent = new Intent(MainActivity.this, AddEditTasks.class);
//oltre a far partire l'activity addedittask vogliamo anche mandare i dati relativi al task selezionato all'activity
intent.putExtra(AddEditTasks.EXTRA_TITLE, task.getTitle());
intent.putExtra(AddEditTasks.EXTRA_DATE, task.getDate());
intent.putExtra(AddEditTasks.EXTRA_TIME, task.getTime());
intent.putExtra(AddEditTasks.EXTRA_TYPE, task.getType());
intent.putExtra(AddEditTasks.EXTRA_PRIORITY , task.getPriority());
intent.putExtra(AddEditTasks.EXTRA_NOTE, task.getNote());
intent.putExtra(AddEditTasks.EXTRA_STATO, task.getState());
intent.putExtra(AddEditTasks.DATA_NOTIFICA, task.getDataNotifica());
intent.putExtra(AddEditTasks.ORA_NOTIFICA, task.getOraNotifica());
//a questo punto dobbiamo recuperare l'id del task cliccato per recuperare correttamente i dati dell'oggetto selezionato
intent.putExtra(AddEditTasks.EXTRA_ID, task.getId());
startActivityForResult(intent, EDIT_TASK_REQUEST);
}
});
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
//creo il metodo per aprire la view appropriata (addTasks) con il click del pulsante +
public void onClick(View view) {
openAddTasks();
}
});
}
public ItemTouchHelper.SimpleCallback metodo(final TaskAdapter adapter) {
ItemTouchHelper.SimpleCallback simpleCallback = 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) {
switch (direction) {
//da destra verso sinistra -> cancello
case ItemTouchHelper.LEFT:
//in questo modo otteniamo l'oggetto Task alla posizione richiesta e lo passiamo al nostro metodo delete che si occupa del resto
String titolo = adapter.getTaskAt(viewHolder.getAdapterPosition()).getTitle();
TaskDeleted taskDeleted = new TaskDeleted(titolo);
taskViewModel.delete(adapter.getTaskAt(viewHolder.getAdapterPosition()));
taskViewModel.insertDeleted(taskDeleted);
Toast.makeText(MainActivity.this, "Task Cancellato", Toast.LENGTH_SHORT).show();
break;
//da sinistra verso destra -> completato
case ItemTouchHelper.RIGHT:
Task task = adapter.getTaskAt(viewHolder.getAdapterPosition());
String title = task.getTitle();
String data_calendario = task.getDate();
String time = task.getTime();
String priority = task.getPriority();
String type = task.getType();
String note = task.getNote();
String stato = "completed";
long datalong = task.getDataNotifica();
long oralong = task.getOraNotifica();
int id = task.getId();
Task completeTask = new Task(title, data_calendario, time, priority, type, note, stato, datalong, oralong);
completeTask.setId(id);
taskViewModel.update(completeTask);
Toast.makeText(MainActivity.this, "Task Completato", Toast.LENGTH_SHORT).show();
break;
}
}
};
return simpleCallback;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
RecyclerView recyclerView = findViewById(R.id.recycler_view);
final TaskAdapter adapter;
switch (item.getItemId()){
//filtri parte relativa ai Programmati, In corso e completati
case R.id.select_pending:
adapter = new TaskAdapter();
recyclerView.setAdapter(adapter);
stato = "pending";
switch (classe){
case "*":
taskViewModel.getAllTasks().observe(this, new Observer<List<Task>>() {
//chiamato ogni volta che i dati dentro alla LiveData cambiano
@Override
public void onChanged(List<Task> tasks) {
/*Ogni volta che onChange è triggerato, (dovrebbe essere ogni volta che i dati nella tabella Task corrispondente cambiano)
* "apdater" dovrebbe essere aggiornato con la lista di task che passiamo come argomento e refressare il dataset*/
adapter.setTasks(tasks);
}
});
modificaTask(adapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(metodo(adapter));
itemTouchHelper.attachToRecyclerView(recyclerView);
Toast.makeText(this, "Tutti i Tasks Programmati", Toast.LENGTH_SHORT).show();
return true;
case "Private":
taskViewModel.getPrivatePending().observe(this, new Observer<List<Task>>() {
//chiamato ogni volta che i dati dentro alla LiveData cambiano
@Override
public void onChanged(List<Task> tasks) {
/*Ogni volta che onChange è triggerato, (dovrebbe essere ogni volta che i dati nella tabella Task corrispondente cambiano)
* "apdater" dovrebbe essere aggiornato con la lista di task che passiamo come argomento e refressare il dataset*/
adapter.setTasks(tasks);
}
});
modificaTask(adapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(metodo(adapter));
itemTouchHelper.attachToRecyclerView(recyclerView);
Toast.makeText(this, "Tasks Privati Programmati", Toast.LENGTH_SHORT).show();
return true;
.
.
.
case R.id.select_completed:
adapter = new TaskAdapter();
recyclerView.setAdapter(adapter);
stato = "completed";
switch (classe){
case "*":
taskViewModel.getAllTasksCompleted().observe(this, new Observer<List<Task>>() {
//chiamato ogni volta che i dati dentro alla LiveData cambiano
@Override
public void onChanged(List<Task> tasks) {
/*Ogni volta che onChange è triggerato, (dovrebbe essere ogni volta che i dati nella tabella Task corrispondente cambiano)
* "apdater" dovrebbe essere aggiornato con la lista di task che passiamo come argomento e refressare il dataset*/
adapter.setTasks(tasks);
}
});
modificaTask(adapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(metodo(adapter));
itemTouchHelper.attachToRecyclerView(recyclerView);
Toast.makeText(this, "Tutti i Tasks Completati", Toast.LENGTH_SHORT).show();
return true;
AND THIS IS MY ADAPTER CLASS
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.TaskHolder> {
//variabile per la nostra lista dei task
private List<Task> tasks = new ArrayList<>();
private OnItemClickListener listener;
@NonNull
@Override
public TaskHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//qui è dove creiamo e ritorniamo il nostro TaskHolder perchè questo è il Layout che useremo per il singolo Item nella recycler view
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) {
//qui è dove ci occupiamo di prendere i dati dal singlenote java-object nella views del nostro TaskHolder
//(perciò vogliamo il Titolo nella text_view_title, ecc ecc)
Task currentTask = tasks.get(position);
holder.textViewTitle.setText(currentTask.getTitle()); //si fa ciò perchè ciò che recuperiamo è il testo che vogliamo sia messo dentro text_view_title
holder.textViewDate.setText(currentTask.getDate());
holder.textViewTime.setText(currentTask.getTime());
holder.textViewType.setText(currentTask.getType());
holder.textViewNote.setText(currentTask.getNote());
holder.textViewPriority.setText(currentTask.getPriority()); //questo potrebbe essere sbagliato perchè passiamo un intero anche se lo abbiamo definito dentro uno string-array (vedi String.valueOf(currentTask...))
}
@Override
public int getItemCount() {
return tasks.size();
}
public void setTasks(List<Task> tasks) {
//metodo che permettere di prendere la lista dei Tasks nella Recycler view
this.tasks = tasks;
notifyDataSetChanged();
}
//metodo public che richiamiamo dalla nostra mainActivity per sapere in quale posizione del nostro Adapter si trova il task che swippiamo (che vogliamo cancellare)
//Siccome nel DAO non passiamo la posizione, nel metodo Delete, bensì un Task, dobbiamo creare un metodo che ci restituisca la posizione
public Task getTaskAt(int position) {
//ritorniamo l'oggetto task alla posizione passata
return tasks.get(position);
}
//classe che mantiene le views nella nostra single recycler_view Item
class TaskHolder extends RecyclerView.ViewHolder {
//creiamo le variabili per le nostre views all'interno della task_item
private TextView textViewTitle;
private TextView textViewDate;
private TextView textViewTime;
private TextView textViewType;
private TextView textViewNote;
private TextView textViewPriority;
/*Assegniamo le nostre textviews nel costruttore TaskHolder
questo è il posto in cui chiamare il nostro OnItemClick method sul nostro listener
listener prenderà il click quando clicckeremo su un oggetto della cardview
siccome l'itemview è la nostra Cardview per intero vogliamo settare l'onClickListener alla nostra itemview*/
public TaskHolder(@NonNull View itemView) {
super(itemView);
textViewTitle = itemView.findViewById(R.id.text_view_title);
textViewDate = itemView.findViewById(R.id.text_view_date);
textViewTime = itemView.findViewById(R.id.text_view_time);
textViewType = itemView.findViewById(R.id.text_view_type);
textViewNote = itemView.findViewById(R.id.text_view_note);
textViewPriority = itemView.findViewById(R.id.text_view_priority);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//qui è dove passiamo il task
int position = getAdapterPosition();
//listener != null per evitare crash mentre il secondo serve per controllare che non
// si clicchi un item in una posizione non valida (potrebbe essere il caso in cui
// clicchiamo un item che è nella fase di animazione perchè lo si sta trascinando
// per cancellarlo)
if (listener != null && position != RecyclerView.NO_POSITION) {
listener.onItemClick(tasks.get(position));
}
}
});
}
}
//creiamo un'interfaccia per gestire il click sul task della recyclerview
public interface OnItemClickListener {
//nell'interfaccia non forniamo un metodo per l'implementazione ma lo dichiariamo e basta
//passiamo solo task perchè è l'unico oggetto che vogliamo aggiornare
void onItemClick(Task task);
}
public void setOnItemClickListener(OnItemClickListener listener) {
//usiamo la variabile listener per chiamare il nostro metodo onItemClick
//variabile che getisce il click del task nella recycler view
this.listener = listener;
}
}
Someone knows why I have this problem? What am I missing? Thank you in advance for helping me
来源:https://stackoverflow.com/questions/59221428/wrong-object-selected-with-itemtouchhelper