2017-08-08 1 views
0

J'ai une base de données dénormalisé qui peut être simplifiée comme suit:Comment savoir que deux FirebaseIndexRecyclerAdapter imbriqués ont fini de charger des données?

|__ 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 
     |_____ ... 

Ce que je veux montrer dans l'interface utilisateur est la liste des sous-menus (de RecyclerView externe), chacun ayant leur propre liste d'éléments de menu (une intérieure RecyclerView pour chaque rangée de RecyclerView externe). J'ai réalisé cela avec code suivant dans onCreate() de mon 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); 

est ici row_submenu.xml, qui est rangée de RecyclerView extérieure:

<?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 montre juste le nom de ligneMenu et prix dans TextViews donc je n » t l'inclure ici. En résumé, cette conception provoque des animations bizarres, des sauts, etc. lors de l'initialisation de RecyclerViews. J'ai deux questions:

  • Existe-t-il une meilleure alternative en termes de conception? (Quelque chose d'équivalent à ExpandableListView)

  • Sinon, comment puis-je détecter que le chargement initial des données est terminé (pour que je puisse tout masquer jusqu'à ce que le dernier élément de menu soit récupéré)? Je pense que l'appel addListenerForSingleValueEvent sur menuItemRef ne fonctionnera pas ici.

Répondre

0

J'ai utilisé deux compteurs pour pouvoir détecter la fin des extractions asynchrones. Les nombres de clés sont cumulativement ajoutés dans totalItemCount afin qu'il contienne le nombre total d'éléments à charger dans les adaptateurs internes. Lorsque les adaptateurs internes remplissent un ViewHolder, populatedItemCount est incrémenté de un et sa valeur actuelle est comparée à totalItemCount. Si les valeurs sont égales, cela signifie que toutes les données sont maintenant récupérées et que les classes RecyclerView et RecyclerViews à l'intérieur de ses lignes peuvent être rendues visibles.

Voici la dernière version des cartes imbriquées je fourni dans la question:

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

Je ne suis toujours pas sûr de l'instruction if qui compare deux compteurs.Serait-il un cas où la récupération de données s'arrête d'une manière ou d'une autre et donc la condition de l'instruction if ne devient jamais vraie et l'application se bloque?

0

Les adaptateurs FirebaseUI ont une méthode appelée onDataChanged() pour détecter quand un changement de données est complètement installé. Voir le source code on github. De là:

Cette méthode sera déclenchée chaque fois que les mises à jour de la base de données auront été complètement traitées. Ainsi, la première fois que cette méthode est appelée, les données initiales ont été chargées, y compris dans le cas où aucune donnée n'est disponible. Chaque fois que la méthode est appelée, une mise à jour complète (potentiellement constituée de mises à jour de plusieurs éléments enfants) a été effectuée.

Vous devez généralement remplacer cette méthode pour masquer un indicateur de chargement (après le chargement initial) ou pour effectuer une mise à jour par lots d'un élément d'interface utilisateur.

Voir également: How to dismiss a progress bar even if there is no view to populate in the FirebaseListAdapter?, que je viens de mettre à jour avec cette information (et un échantillon).

+0

Je sais quand la charge de données est terminée pour l'adaptateur externe. Mais chaque entrée crée un autre adaptateur interne ici. Je veux savoir quand le dernier chargement interne de données se termine mais je ne peux pas car ils sont tous asynchrones. – Mehmed