2017-08-08 1 views
0

Ich habe eine normalisierte Datenbank, die wie folgt vereinfacht werden kann:Wie kann man wissen, dass zwei Nested FirebaseIndexRecyclerAdapter Daten geladen haben?

|__ menu 
|  |___ menuKey1 
|    |___ subMenus (key list of subMenus belongs to menu) 
|    |___ other fields... 
| 
|__ subMenu 
|  |___ subMenuKey1 
|    |__ menuItems (key list of menuItems belongs to subMenu) 
|    |__ other fields... 
| 
|__ menuItem 
     |_____ ... 

Was ich in der Benutzeroberfläche angezeigt werden soll ist die Liste der Untermenüs (Außen RecyclerView), die jeweils ihre eigene Liste von Menüpunkten mit (einer Innen RecyclerView für jede Reihe von RecyclerView). Ich erreichte dies mit folgendem Code in onCreate() mein Fragment:

rootRef = FirebaseDatabase.getInstance().getReference(); 
DatabaseReference subMenuKeyRef = rootRef.child("menu").child(menuKey).child("subMenus"); 
DatabaseReference subMenuRef = rootRef.child("subMenu"); 
subMenuAdapter = new FirebaseIndexRecyclerAdapter<MySubMenu, SubMenuHolder>(
     MySubMenu.class, 
     R.layout.row_submenu, 
     SubMenuHolder.class, 
     subMenuKeyRef, 
     subMenuRef) { 
    @Override 
    protected void populateViewHolder(SubMenuHolder viewHolder, MySubMenu model, int position) { 
     String subMenuKey = getRef(position).getKey(); 
     DatabaseReference menuItemKeyRef = rootRef.child("subMenu").child(subMenuKey).child("menuItems"); 
     DatabaseReference menuItemRef = rootRef.child("menuItem"); 
     FirebaseIndexRecyclerAdapter menuItemAdapter = new FirebaseIndexRecyclerAdapter<MyMenuItem, MenuItemHolder>(
       MyMenuItem.class, 
       R.layout.row_menu_item, 
       MenuItemHolder.class, 
       menuItemKeyRef, 
       menuItemRef) { 
      @Override 
      protected void populateViewHolder(MenuItemHolder viewHolderx, MyMenuItem modelx, int positionx) { 
       viewHolderx.setName(modelx.getName()); 
       viewHolderx.setPrice(modelx.getPrice()); 
      } 
     }; 
     viewHolder.setName(model.getName()); 
     viewHolder.setMenuItemList(menuItemAdapter); 
    } 
}; 
subMenuList.setAdapter(subMenuAdapter); 

Hier ist row_submenu.xml, die Reihe der äußeren RecyclerView ist:

<?xml version="1.0" encoding="utf-8"?> 
<android.support.constraint.ConstraintLayout 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="wrap_content" 
    android:visibility="gone"> 

    <TextView 
     android:id="@+id/submenu_name" 
     android:layout_width="0dp" 
     android:layout_height="wrap_content" 
     android:textAllCaps="true" 
     android:textStyle="bold" 
     app:layout_constraintLeft_toLeftOf="parent" 
     app:layout_constraintRight_toRightOf="parent" 
     app:layout_constraintTop_toTopOf="parent" /> 

    <android.support.v7.widget.RecyclerView 
     android:id="@+id/submenu_menu_items" 
     android:layout_width="0dp" 
     android:layout_height="wrap_content" 
     android:paddingBottom="8dp" 
     app:layoutManager="android.support.v7.widget.LinearLayoutManager" 
     app:layout_constraintBottom_toBottomOf="parent" 
     app:layout_constraintLeft_toLeftOf="parent" 
     app:layout_constraintRight_toRightOf="parent" 
     app:layout_constraintTop_toBottomOf="@id/submenu_name" /> 

</android.support.constraint.ConstraintLayout> 

row_menu_item.xml nur zeigt menuItem Namen und Preis in Textviews so I don‘ t es hier einschließen.

Zusammenfassend verursacht dieses Design seltsame Animationen, Sprünge usw. während der Initialisierung von RecyclerViews. Ich habe zwei Fragen:

  • Gibt es eine bessere Alternative in Bezug auf das Design? (Etwas gleichbedeutend mit ExpandableListView)

  • Wenn nicht, wie kann ich erkennen, dass die anfängliche Datenladung abgeschlossen ist (so dass ich alles ausblenden kann, bis das letzte menuItem abgerufen wird)? Ich denke, addListenerForSingleValueEvent auf menuItemRef Anruf wird hier nicht funktionieren.

Antwort

0

Ich habe zwei Zähler verwendet, um das Ende von asynchronen Abfragen erkennen zu können. Schlüsselzahlen werden kumulativ in totalItemCount hinzugefügt, so dass die Gesamtzahl der Elemente in den inneren Adaptern gespeichert wird. Wenn innere Adapter einen ViewHolder füllen, wird populatedItemCount um eins erhöht und der aktuelle Wert wird mit totalItemCount verglichen. Wenn Werte gleich sind, bedeutet dies, dass alle Daten jetzt abgerufen werden und die übergeordneten RecyclerView- und RecyclerViews in ihren Zeilen sichtbar gemacht werden können.

Hier ist die letzte Version des verschachtelten Adapters I in der Frage zu finden:

rootRef = FirebaseDatabase.getInstance().getReference(); 
DatabaseReference subMenuKeyRef = rootRef.child("menu").child(menuKey).child("subMenus"); 
DatabaseReference subMenuRef = rootRef.child("subMenu"); 
subMenuAdapter = new FirebaseIndexRecyclerAdapter<MySubMenu, SubMenuHolder>(
     MySubMenu.class, 
     R.layout.row_submenu, 
     SubMenuHolder.class, 
     subMenuKeyRef, 
     subMenuRef) { 
    int totalItemCount = 0; 
    int populatedItemCount = 0; 

    @Override 
    protected void populateViewHolder(SubMenuHolder viewHolder, MySubMenu model, int position) { 
     totalItemCount += model.getMenuItems().size(); 
     String subMenuKey = getRef(position).getKey(); 
     DatabaseReference menuItemKeyRef = rootRef.child("subMenu").child(subMenuKey).child("menuItems"); 
     DatabaseReference menuItemRef = rootRef.child("menuItem"); 
     FirebaseIndexRecyclerAdapter menuItemAdapter = new FirebaseIndexRecyclerAdapter<MyMenuItem, MenuItemHolder>(
       MyMenuItem.class, 
       R.layout.row_menu_item, 
       MenuItemHolder.class, 
       menuItemKeyRef, 
       menuItemRef) { 
      @Override 
      protected void populateViewHolder(MenuItemHolder viewHolderx, MyMenuItem modelx, int positionx) { 
       populatedMenuItemCount++; 
       viewHolderx.setName(modelx.getName()); 
       viewHolderx.setPrice(modelx.getPrice()); 

       // TODO - Find a resolution if this if never gets true 
       if (populatedMenuItemCount == totalMenuItemCount) { 
        (new Handler()).postDelayed(new Runnable() { 
         @Override 
         public void run() { 
          showSubMenuList(); 
         } 
        }, 500); 
       } 
      } 
     }; 
     viewHolder.setName(model.getName()); 
     viewHolder.setMenuItemList(menuItemAdapter); 
    } 
}; 
subMenuList.setAdapter(subMenuAdapter); 

Ich bin immer noch nicht sicher über die if-Anweisung, die zwei Zähler vergleicht.Würde es einen Fall geben, bei dem die Datenwiederherstellung irgendwie stoppt und somit der Zustand der if-Anweisung niemals wahr wird und die App hängt?

0

FirebaseUI Die Adapter haben ein Verfahren genannt onDataChanged() zu erkennen, wenn eine Datenänderung vollständig geladen wurde.

Siehe source code on github. Von dort:

Diese Methode wird jedes Mal ausgelöst, wenn Updates von der Datenbank vollständig verarbeitet wurden. Beim ersten Aufruf dieser Methode wurden also die ursprünglichen Daten geladen - einschließlich des Falls, wenn überhaupt keine Daten verfügbar sind. Jedes Mal, wenn die Methode das nächste Mal aufgerufen wird, ist eine vollständige Aktualisierung (möglicherweise bestehend aus Aktualisierungen für mehrere untergeordnete Elemente) abgeschlossen.

Sie würden diese Methode normalerweise überschreiben, um einen Ladeindikator auszublenden (nach dem erstmaligen Laden) oder um eine Stapelaktualisierung auf ein UI-Element abzuschließen.

Siehe auch: How to dismiss a progress bar even if there is no view to populate in the FirebaseListAdapter?, die ich gerade mit dieser Information (und einem Beispiel) aktualisiert habe.

+0

Ich weiß, wenn die Daten für den äußeren Adapter abgeschlossen sind. Aber jeder Eintrag erzeugt hier einen weiteren inneren Adapter. Ich möchte wissen, wann die letzte innere Datenladevervollständigung stattfindet, aber ich kann nicht, da alle von ihnen asynchron sind. – Mehmed

Verwandte Themen