Wrong object selected with ItemTouchHelper

て烟熏妆下的殇ゞ 提交于 2019-12-24 19:38:58

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!