2014-07-14 20 views
38

Ricevo i dati dal server per poi analizzarlo e archiviarlo in un elenco. Sto usando questo elenco per l'adattatore di RecyclerView. Sto usando Frammenti.notifyDataSetChanged non funziona su RecyclerView

Sto utilizzando un Nexus 5 con KitKat. Sto usando la libreria di supporto per questo. Questo farà la differenza?

Ecco il mio codice: (Usando dati fittizi per la domanda)

variabili membro:

List<Business> mBusinesses = new ArrayList<Business>(); 

RecyclerView recyclerView; 
RecyclerView.LayoutManager mLayoutManager; 
BusinessAdapter mBusinessAdapter; 

mio onCreateView():

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
     Bundle savedInstanceState) { 

    // Getting data from server 
    getBusinessesDataFromServer(); 

    View view = inflater.inflate(R.layout.fragment_business_list, 
      container, false); 
    recyclerView = (RecyclerView) view 
      .findViewById(R.id.business_recycler_view); 
    recyclerView.setHasFixedSize(true); 

    mLayoutManager = new LinearLayoutManager(getActivity()); 
    recyclerView.setLayoutManager(mLayoutManager); 

    mBusinessAdapter = new BusinessAdapter(mBusinesses); 
    recyclerView.setAdapter(mBusinessAdapter); 

    return view; 
} 

Dopo aver ottenuto i dati dal server, parseResponse() SI CHIAMA .

protected void parseResponse(JSONArray response, String url) { 
    // insert dummy data for demo 

    mBusinesses.clear(); 

    Business business; 

    business = new Business(); 
    business.setName("Google"); 
    business.setDescription("Google HeadQuaters"); 
    mBusinesses.add(business); 

    business = new Business(); 
    business.setName("Yahoo"); 
    business.setDescription("Yahoo HeadQuaters"); 
    mBusinesses.add(business); 

    business = new Business(); 
    business.setName("Microsoft"); 
    business.setDescription("Microsoft HeadQuaters"); 
    mBusinesses.add(business); 

    Log.d(Const.DEBUG, "Dummy Data Inserted\nBusinesses Length: " 
      + mBusinesses.size()); 

    mBusinessAdapter = new BusinessAdapter(mBusinesses); 
    mBusinessAdapter.notifyDataSetChanged(); 
} 

mio BusinessAdapter:

public class BusinessAdapter extends 
    RecyclerView.Adapter<BusinessAdapter.ViewHolder> { 

    private List<Business> mBusinesses = new ArrayList<Business>(); 

    // Provide a reference to the type of views that you are using 
    // (custom viewholder) 
    public static class ViewHolder extends RecyclerView.ViewHolder { 
     public TextView mTextViewName; 
     public TextView mTextViewDescription; 
     public ImageView mImageViewLogo; 

     public ViewHolder(View v) { 
      super(v); 
      mTextViewName = (TextView) v 
        .findViewById(R.id.textView_company_name); 
      mTextViewDescription = (TextView) v 
        .findViewById(R.id.textView_company_description); 
      mImageViewLogo = (ImageView) v 
        .findViewById(R.id.imageView_company_logo); 
     } 
    } 

    // Provide a suitable constructor (depends on the kind of dataset) 
    public BusinessAdapter(List<Business> myBusinesses) { 

     Log.d(Const.DEBUG, "BusinessAdapter -> constructor"); 

     mBusinesses = myBusinesses; 
    } 

    // Create new views (invoked by the layout manager) 
    @Override 
    public BusinessAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, 
      int viewType) { 

     Log.d(Const.DEBUG, "BusinessAdapter -> onCreateViewHolder()"); 

     // create a new view 
     View v = LayoutInflater.from(parent.getContext()).inflate(
       R.layout.item_business_list, parent, false); 

     ViewHolder vh = new ViewHolder(v); 
     return vh; 
    } 

    // Replace the contents of a view (invoked by the layout manager) 
    @Override 
    public void onBindViewHolder(ViewHolder holder, int position) { 
     // - get element from your dataset at this position 
     // - replace the contents of the view with that element 

     Log.d(Const.DEBUG, "BusinessAdapter -> onBindViewHolder()"); 

     Business item = mBusinesses.get(position); 
     holder.mTextViewName.setText(item.getName()); 
     holder.mTextViewDescription.setText(item.getDescription()); 
     holder.mImageViewLogo.setImageResource(R.drawable.ic_launcher); 

    } 

    // Return the size of your dataset (invoked by the layout manager) 
    @Override 
    public int getItemCount() { 

     Log.d(Const.DEBUG, "BusinessAdapter -> getItemCount()"); 

     if (mBusinesses != null) { 
      Log.d(Const.DEBUG, "mBusinesses Count: " + mBusinesses.size()); 
      return mBusinesses.size(); 
     } 
     return 0; 
    } 
} 

Ma non ottengo i dati visualizzati nella vista. Che cosa sto facendo di sbagliato?

Ecco il mio registro,

07-14 21:15:35.669: D/xxx(2259): Dummy Data Inserted 
07-14 21:15:35.669: D/xxx(2259): Businesses Length: 3 
07-14 21:26:26.969: D/xxx(2732): BusinessAdapter -> constructor 

non ho ricevuto alcun registro dopo questo. Non dovrebbe getItemCount() in adattatore dovrebbe essere chiamato di nuovo?

risposta

46

Nel parseResponse() si sta creando una nuova istanza della classe BusinessAdapter, ma non la si sta effettivamente utilizzando da nessuna parte, quindi il proprio RecyclerView non sa che la nuova istanza esiste.

vi sia bisogno di:

  • chiamata recyclerView.setAdapter(mBusinessAdapter) per aggiornare riferimento all'adattatore del RecyclerView per puntare a quello nuovo
  • O semplicemente rimuovere mBusinessAdapter = new BusinessAdapter(mBusinesses); di continuare ad utilizzare l'adattatore esistente. Poiché non è stato modificato il riferimento mBusinesses, l'adattatore continuerà a utilizzare tale elenco di array e dovrebbe aggiornarsi correttamente quando si chiama notifyDataSetChanged().
+0

Grazie per il vostro aiuto !!! Capito. Ho appena rimosso la linea che crea di nuovo una nuova istanza. Accetterà la tua risposta in 8 minuti. –

+3

Questo non ha funzionato per me, ho dovuto riattivare l'adattatore e reimpostarlo al riclassifico – jonney

+0

Sto seguendo il tuo secondo punto creando di nuovo l'adattatore con il nuovo BusinessAdapter (mBusiness). Devo rilasciare il vecchio adattatore in alcun modo? Ciò causerà una perdita di memoria se continuo a creare un nuovo adattatore ogni volta? – wayway

17

provare questo metodo:

List<Business> mBusinesses2 = mBusinesses; 
mBusinesses.clear(); 
mBusinesses.addAll(mBusinesses2); 
//and do the notification 

un po 'di tempo, ma dovrebbe funzionare.

+1

La risposta di Tanis.7x ha aiutato il mio problema .. –

+2

Questa dichiarazione: Lista mBusinesses2 = mBusiness; ** fa ** non fa una copia profonda della lista. Quindi non stai clonando la lista - questa è una cosa, la seconda cosa è che non vedo come questo potrebbe risolvere il problema originale .... –

+1

In realtà ho avuto un problema simile e l'ho risolto in questo modo: [soluzione] (http://stackoverflow.com/questions/24495542/notifydatasetchange-not-working-on-recyclerview/29032141#29032141) –

4

Ho avuto lo stesso problema. L'ho appena risolto dichiarando adapter pubblico prima del onCreate della classe.

PostAdapter postAdapter; 

dopo che

postAdapter = new PostAdapter(getActivity(), posts); 
recList.setAdapter(postAdapter); 

alla fine mi hanno chiamato:

@Override 
protected void onPostExecute(Void aVoid) { 
    super.onPostExecute(aVoid); 
    // Display the size of your ArrayList 
    Log.i("TAG", "Size : " + posts.size()); 
    progressBar.setVisibility(View.GONE); 
    postAdapter.notifyDataSetChanged(); 
} 

maggio di volontà ti aiuta.

0

Solo per integrare le altre risposte come io non credo che nessuno ha menzionato questo qui: notifyDataSetChanged()deve essere eseguito sul thread principale (altri notify<Something> metodi di RecyclerView.Adapter così, ovviamente)

Da quello che ho raccogliere, dal momento che hai le procedure di analisi e la chiamata a notifyDataSetChanged() nello stesso blocco, o lo chiami da un thread di lavoro, o stai facendo l'analisi di JSON sul thread principale (che è anche un no-no come io ' sono sicuro che lo sai). Quindi il modo corretto sarebbe:

protected void parseResponse(JSONArray response, String url) { 
    // insert dummy data for demo 
    // <yadda yadda yadda> 
    mBusinessAdapter = new BusinessAdapter(mBusinesses); 
    // or just use recyclerView.post() or [Fragment]getView().post() 
    // instead, but make sure views haven't been destroyed while you were 
    // parsing 
    new Handler(Looper.getMainLooper()).post(new Runnable() { 
     public void run() { 
      mBusinessAdapter.notifyDataSetChanged(); 
     } 
    }); 

}

PS cosa strana è, non credo che si ottiene alcuna indicazione circa la cosa principale filo da entrambi IDE o tronchi di run-time. Questo è solo dalle mie osservazioni personali: se faccio chiamare il notifyDataSetChanged() da un thread di lavoro, non ottengo l'obbligatorio Solo il thread originale che ha creato una gerarchia di visualizzazione può toccare il suo messaggio o qualcosa del genere - semplicemente fallisce silenziosamente (e nel mio caso una chiamata off-main può anche impedire che le chiamate main-thread funzionino correttamente, probabilmente a causa di qualche tipo di condizione di gara)

Inoltre, né lo né il relativo ufficiale dev guide menzionano esplicitamente il richiesta di thread principale al momento (il momento è il 2017) e nessuna delle regole di ispezione dei pelucchi di Android Studio sembra riguardare questo problema.

Ma, here is an explanation di questo stesso autore

Problemi correlati