2014-07-14 22 views
38

Ich erhalte Daten vom Server und analysiere sie dann und speichere sie in einer Liste. Ich verwende diese Liste für den RecyclerView-Adapter. Ich benutze Fragmente.notifyDataSetChanged funktioniert nicht auf RecyclerView

Ich verwende ein Nexus 5 mit KitKat. Ich verwende hierfür die Support-Bibliothek. Wird das einen Unterschied machen?

Hier ist mein Code: (Dummy-Daten für die Frage verwenden)

Membervariablen:

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

RecyclerView recyclerView; 
RecyclerView.LayoutManager mLayoutManager; 
BusinessAdapter mBusinessAdapter; 

Mein 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; 
} 

Nachdem die Daten vom Server erhalten, parseResponse() genannt wird .

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(); 
} 

Mein 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; 
    } 
} 

Aber ich nicht bekommen, die in der Ansicht angezeigten Daten. Was mache ich falsch?

Hier ist mein log,

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 

Ich erhalte keine Protokolle nach diesem. Sollte nicht im Adapter getItemCount() erneut aufgerufen werden?

Antwort

46

In Ihrer parseResponse() erstellen Sie eine neue Instanz der BusinessAdapter Klasse, aber Sie verwenden es tatsächlich nirgends, so dass Ihre RecyclerView nicht weiß, dass die neue Instanz existiert.

entweder Sie müssen:

  • Anruf recyclerView.setAdapter(mBusinessAdapter) wieder die RecyclerView des Adapters Bezug auf aktualisieren, um Ihren neuen
  • Oder einfach entfernen mBusinessAdapter = new BusinessAdapter(mBusinesses); weiterhin mit dem vorhandenen Adapter zeigen. Da Sie die Referenz mBusinesses nicht geändert haben, verwendet der Adapter weiterhin diese Array-Liste und sollte korrekt aktualisiert werden, wenn Sie notifyDataSetChanged() aufrufen.
+0

Danke für Ihre Hilfe !!! Habe es .. habe gerade die Zeile entfernt, die erneut eine neue Instanz erstellt. Wird Ihre Antwort in 8 Minuten akzeptieren. –

+3

Dies funktionierte nicht für mich, ich musste den Adapter retrate und setzen Sie es erneut auf die recylerview – jonney

+0

Ich folgen Ihrem zweiten Punkt, indem Sie den Adapter erneut mit neuen BusinessAdapter (mBusinesses) erstellen. Muss ich den alten Adapter irgendwie freigeben? Wird dies zu einem Speicherverlust führen, wenn ich jedes Mal einen neuen Adapter erstelle? – wayway

17

versuchen, diese Methode:

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

ein wenig zeitaufwendig, aber es sollte funktionieren.

+1

Tanis.7x's Antwort half meinem Problem .. –

+2

Diese Aussage: Liste mBusinesses2 = mBusinesses; macht ** nicht ** eine tiefe Kopie der Liste. Du klonst also die Liste nicht - das ist eine Sache, die zweite Sache ist, dass ich nicht sehe, wie dies das ursprüngliche Problem lösen könnte ... –

+1

Eigentlich hatte ich ein ähnliches Problem und löste es so: [Lösung] (http://stackoverflow.com/questions/24495542/notifydatasetchange-not-working-on-recyclerview/29032141#29032141) –

4

Ich hatte das gleiche Problem. Ich habe es gerade gelöst mit adapter öffentlichen vor onCreate der Klasse zu deklarieren.

PostAdapter postAdapter; 

nach diesem

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

Endlich habe ich genannt:

@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(); 
} 

Mai dieses Wille hilft Ihnen.

0

einfach die anderen Antworten zu ergänzen, wie ich jemand glaube nicht, dass dies hier erwähnt: notifyDataSetChanged()sollte auf dem Hauptthread (andere notify<Something> Methoden der RecyclerView.Adapter auch, natürlich) ausgeführt werden

Von dem, was ich Sammeln, da Sie die Parsing-Prozeduren und den Aufruf an notifyDataSetChanged() im selben Block haben, entweder Sie rufen es aus einem Worker-Thread, oder Sie tun JSON Parsing auf Haupt-Thread (das ist auch ein No-No wie ich ' Ich bin sicher, du weißt es). Also der richtige Weg wäre:

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 Seltsame ist, ich glaube nicht, dass Sie keine Hinweise erhalten über das Haupt-Thread, was entweder IDE oder Laufzeitprotokolle. Dies ist nur von meinen persönlichen Beobachtungen: Wenn ich notifyDataSetChanged() von einem Worker-Thread aufrufen, bekomme ich nicht die obligatorische Nur der ursprüngliche Thread, der eine Ansicht Hierarchie erstellt kann seine Ansichten Nachricht oder ähnliches berühren - es schlägt einfach im Hintergrund (und in meinem Fall ein off-main-Thread Anruf sogar verhindern kann Hauptthread Anrufe Erfolg von richtig funktioniert, wahrscheinlich wegen irgendeiner Art von race-Bedingung)

Darüber hinaus weder die RecyclerView.Adapter api reference noch die einschlägigen amtlichen dev guide ausdrücklich erwähnen die Haupt-Thread-Anforderung im Moment (der Moment ist 2017) und keine der Android Studio Flusen Inspektionsregeln scheinen dieses Problem auch zu betreffen.

Aber here is an explanation dies vom Autor selbst

Verwandte Themen