8

Ho un ListView con un CursorLoader. L'utente può aprire gli articoli ListView (aprire un altro Fragment) o eliminare elementi. Tutte le azioni del database si verificano in modo asincrono e in genere richiedono una frazione di secondo. Ma tecnicamente l'utente può cancellare un oggetto e quindi aprire l'oggetto prima della cancellazione callback e causa ed errore. Qual è il modo migliore per gestire questo? Ecco le opzioni che vedo.Procedura consigliata per eliminare in modo asincrono un oggetto ListView/RecyclerView

  1. dare per scontato che il AsyncTask sarà sempre accadere abbastanza velocemente per evitare un problema
  2. fare l'operazione database sul thread UI
  3. invalidare la ListView prima della AsyncTask (ma questo potrebbe causare un fuoco di interfaccia utente)
  4. input dell'utente
  5. Blocca in qualche modo durante la AsyncTask

Edit: Ho finito per utilizzare RecyclerView ma non posso chiamare adapter.notifyItemRemoved(itemPos) fino a dopo aver eliminato l'elemento dal database.

risposta

3
@Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     final View itemView = convertView != null ? convertView : /* inflate view */; 
     itemView.findViewById(R.id.delete_button).setOnClickListener(
       new View.OnClickListener() { 
        @Override 
        public void onClick(View view) { 
         itemView.setEnabled(false); 
         // OR view.setClickable(false); 
        } 
       }); 
     return itemView; 
    } 

Oppure utilizzare RecyclerView con animazione di rimozione.

build.gradle

compile 'com.android.support:recyclerview-v7:23.2.1' 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 

    <android.support.v7.widget.RecyclerView 
     android:id="@+id/my_recycler_view" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     app:layoutManager="android.support.v7.widget.LinearLayoutManager" /> 
</RelativeLayout> 

my_item.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:orientation="horizontal"> 

    <TextView 
     android:id="@+id/value_text" 
     android:layout_width="0dp" 
     android:layout_height="wrap_content" 
     android:layout_weight="1"/> 

    <Button 
     android:id="@+id/delete_button" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:text="Delete"/> 

</LinearLayout> 

MainActivity.java

import android.os.Bundle; 
import android.support.v7.app.AppCompatActivity; 
import android.support.v7.widget.RecyclerView; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.Button; 
import android.widget.TextView; 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 

public class MainActivity extends AppCompatActivity { 

    private List<String> items = new ArrayList<>(
      Arrays.asList("first", "second", "third")); 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     RecyclerView myRecyclerView = (RecyclerView) 
       findViewById(R.id.my_recycler_view); 
     myRecyclerView.setAdapter(new MyAdapter()); 
    } 

    private class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { 

     @Override 
     public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
      return new MyViewHolder(LayoutInflater.from(MainActivity.this) 
        .inflate(R.layout.my_item, parent, false)); 
     } 

     @Override 
     public void onBindViewHolder(MyViewHolder holder, int position) { 
      holder.bind(position); 
     } 

     @Override 
     public int getItemCount() { 
      return items.size(); 
     } 

     public class MyViewHolder extends RecyclerView.ViewHolder { 

      private TextView valueTextView; 
      private Button deleteButton; 

      public MyViewHolder(View itemView) { 
       super(itemView); 
       valueTextView = (TextView) itemView.findViewById(R.id.value_text); 
       deleteButton = (Button) itemView.findViewById(R.id.delete_button); 
       deleteButton.setOnClickListener(new View.OnClickListener() { 
        @Override 
        public void onClick(View v) { 
         int position = (int) v.getTag(); 
         items.remove(position); 
         notifyItemRemoved(position); 
         notifyItemRangeChanged(position, items.size()); 
        } 
       }); 
      } 

      public void bind(int position) { 
       valueTextView.setText(items.get(position)); 
       deleteButton.setTag(position); 
      } 
     } 
    } 
} 
+0

La disattivazione della vista presuppone che l'elemento non sia e non sia associato a un'altra vista, giusto? Potresti indicarmi i metodi chiave che dovrei esaminare in RecyclerView?Sembra un'opzione interessante se non si complica eccessivamente. – cambunctious

+0

In realtà è super facile :) Ho appena aggiunto il codice alla mia risposta. – Andrew

6

opzione migliore nel tuo caso verrà utilizzato RecyclerView. Perché quando hai cliccato su cancella puoi chiamare adapter.notifyItemRemoved(itemPos). Quindi l'elemento della lista verrà rimosso con l'animazione dal tuo RecyclerView e non ti preoccuperai del risultato dell'operazione di cancellazione.

Vedere la Developer esercitazione Android su Creating Lists and Cards:

utilizzare il widget RecyclerView quando si dispone di raccolte di dati la cui elementi cambiare in fase di esecuzione in base agli eventi di azione dell'utente o di rete.

Ecco un esempio:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener, View.OnLongClickListener { 

private ArrayList<String> mDataset; 
private static Context sContext; 

public MyAdapter(Context context, ArrayList<String> myDataset) { 
    mDataset = myDataset; 
    sContext = context; 
} 

@Override 
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) { 
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_text_view, parent, false); 

    ViewHolder holder = new ViewHolder(v); 
    holder.mNameTextView.setOnClickListener(MyAdapter.this); 

    holder.mNameTextView.setTag(holder); 

    return holder; 
} 

@Override 
public void onBindViewHolder(ViewHolder holder, int position) { 

    holder.mNameTextView.setText(mDataset.get(position)); 

} 

@Override 
public int getItemCount() { 
    return mDataset.size(); 
} 


@Override 
public void onClick(View view) { 
    ViewHolder holder = (ViewHolder) view.getTag(); 
    if (view.getId() == holder.mNameTextView.getId()) { 
     **//Important !!!** 
     notifyItemRemoved(getPosition()); 
     // Make your database operation also here 
    } 
} 



public static class ViewHolder extends RecyclerView.ViewHolder { 
    public TextView mNumberRowTextView; 
    public TextView mNameTextView; 


    public ViewHolder(View v) { 
     super(v); 

     mNameTextView = (TextView) v.findViewById(R.id.nameTextView); 
    } 
} 
+0

Non dimenticare di mDataset.remove (posizione) e notifyItemRangeChanged (posizione, mDataset.size()). Senza quest'ultimo, si arresta in modo anomalo quando si elimina il primo oggetto finché l'elenco non è vuoto. – Andrew

+0

Cosa succede se utilizzo un cursore? Non riesco a rimuovere l'elemento dal set di dati finché non ottengo un nuovo cursore. – cambunctious

+0

Non è necessario attendere il nuovo cusosr. Basta eliminare prima l'elemento dal tuo recyclerView. NotifyItemRemoved accetta la posizione come parametro. Quindi basta passare la posizione di dove hai cliccato il pulsante Elimina. Voglio dire che non hai bisogno di mettere il tuo notifyItemRemoved dopo aver ottenuto il nuovo cursore. Modifica la priorità e chiama notifyItemRemoved innanzitutto quando fai clic sul pulsante Elimina. NotifyItemRemived non guarda i tuoi dati, rimuove solo la tua vista di riciclaggio dall'elenco. Quindi puoi anche rimuovere i tuoi dati dal tuo db @cambunctious –

0

se la query di database non restituisce molti dati quindi non v'è alcun problema di fare operazioni di database sul thread dell'interfaccia utente.

+0

questo è probabilmente * verosimile vero * nel mio caso, ma sono riluttante ad adottarlo. La quantità di dati dipende dal numero di elementi creati dall'utente. Anche la quantità di dati può facilmente aumentare con l'evolversi dell'app. – cambunctious

Problemi correlati