Kerstin
Kerstin

Reputation: 19

RecyclerView with FirebaseRecyclerAdapter remains empty

I'm currently trying to build an app using Firebase Realtime Database, I already have some data there, which are structured like this (excerpt of the exported JSON)

{
"userdata" : {
    "593QfAvxxxxxxxxxxxHqCMA3" : {
      "calcentries" : {
        "-LJy_hvvm3ySUqM6sQcf" : {
          "candyCount" : 0,
          "evolutionLine" : {
            "candyNeeded" : 25,
            "evolvedSpecies" : 2,
            "precondition" : {
              "buddyKm" : 0,
              "preconditionType" : "NONE"
            }
          },
          "evolvedSpeciesTransferCount" : 0,
          "newPokedexEntry" : false,
          "pokemonCount" : 0,
          "possibleEvoCountWithPreconditionsMet" : 0,
          "possibleEvolutionCount" : 0,
          "preconditionsMet" : false,
          "sourceSpecies" : 1,
          "sourceSpeciesCountAfterEvolutions" : 0,
          "sourceSpeciesTransferCount" : 0
        },
        "-LJy_hwMVTM20f0c5JOJ" : { ...

}

I try to display them in a RecyclerView by using a FirebaseRecyclerAdapter (Firebase UI 4.1.0). This is how I initialize the RecyclerView and the adapter in the onCreate of my activity

mRecyclerView = findViewById(R.id.rv_calculation_input);

RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this,1, LinearLayoutManager.VERTICAL,false);

mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);

FirebaseRecyclerOptions<CalculationInputEntry> opt
     = new FirebaseRecyclerOptions.Builder<CalculationInputEntry>()
         .setLifecycleOwner(this)
         .setQuery(AppUtil.getCurrentUserRef().child("calcentries"), CalculationInputEntry.class)
         .build();

mAdapter = new FirebaseRecyclerAdapter<CalculationInputEntry, CalculationInputEntryViewHolder>(opt) {        
     @NonNull
     @Override
     public CalculationInputEntryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
          View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_main, parent, false);
          return new CalculationInputEntryViewHolder(v);
     }

     @Override
     protected void onBindViewHolder(@NonNull CalculationInputEntryViewHolder holder, int position, @NonNull CalculationInputEntry model) {
           holder.bindEntry(model);
     }
 };

mRecyclerView.setAdapter(mAdapter);

The view remains empty... By setting the lifecycle owner, manually calling startListening and stopListening on the adapter should not be necessary, right?

I set the Firebase log level to debug and found quite some entries that "CHILD_ADDED" events are raised, even showing the expected data...

D/EventRaiser: Raising /userdata/593QxxxxxxxxxCMA3/calcentries: CHILD_ADDED: { -LJy_tNakAl19tH5H_tb: {preconditionsMet=false, sourceSpeciesCountAfterEvolutions=0, newPokedexEntry=false, possibleEvoCountWithPreconditionsMet=0, possibleEvolutionCount=0, candyCount=0, sourceSpeciesTransferCount=0, evolutionLine={candyNeeded=25, precondition={buddyKm=0, preconditionType=NONE}, evolvedSpecies=375}, pokemonCount=0, sourceSpecies=374, evolvedSpeciesTransferCount=0} }

but they seem to never get through to the UI.

Here are the involved model classes, I'm using Lombok to generate getters, setters and the needed constructors (at least I hope I included everything needed). I already changed some fields from Enums to Strings, just in case this could be a problem... Caution: lots of code ahead...

CalculationInputEntry

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class CalculationInputEntry implements Comparable, Parcelable {

    //Pokédex number for less data redundancy when serializing
    private int sourceSpecies;
    private SpeciesEvolution evolutionLine;

    //input variables
    private int candyCount;
    private int pokemonCount;
    private boolean newPokedexEntry;
    private boolean preconditionsMet;

    //calculation and result variables
    private int possibleEvolutionCount; // 0 when preconditionsMet false
    private int sourceSpeciesTransferCount;
    private int evolvedSpeciesTransferCount;
    private int sourceSpeciesCountAfterEvolutions;
    private int possibleEvoCountWithPreconditionsMet;

    public CalculationInputEntry(int species, SpeciesEvolution evolution, int candyCount, int pokemonCount) {
        this.sourceSpecies = species;
        this.evolutionLine = evolution;
        this.candyCount = candyCount;
        this.pokemonCount = pokemonCount;
        this.preconditionsMet = false;
        this.newPokedexEntry = false;
    }

   //some other methods, including compareTo and the Parcelable stuff
}

SpeciesEvolution

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class SpeciesEvolution implements Parcelable {
    private int candyNeeded;
    private EvolutionPrecondition precondition;
    private int evolvedSpecies;

    //Parcelable stuff here
}

EvolutionPrecondition

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class EvolutionPrecondition implements Parcelable {

    private String preconditionType;
    private int buddyKm;
    private String evoItem;

   //Parcelable stuff
}

Is there anything wrong with my models or am I just too blind to see the real problem? Any hint would be highly appreciated...

Upvotes: 0

Views: 616

Answers (2)

Kerstin
Kerstin

Reputation: 19

I solved it. Neither the start/stopListening is needed, nor are my getters/setters or the DatabaseReference wrong...

It's this line

mRecyclerView.setHasFixedSize(true);

after I had removed it, I could revert everything to the state shown in the question. I got the idea from this GitHub issue https://github.com/firebase/FirebaseUI-Android/issues/204 Thanks to Alex Mamo anyways :)

Upvotes: 1

Alex Mamo
Alex Mamo

Reputation: 138824

By setting the lifecycle owner, manually calling startListening and stopListening on the adapter should not be necessary, right?

No, both are necessary. In order to be able to display data from the Firebase database you need to start listening for changes and for that you should add the following line of code in the onStart() method:

@Override
protected void onStart() {
    super.onStart();
    mAdapter.startListening();
}

To stop listening foir changes you need add the following line of code in the onStop() method like this:

@Override
protected void onStop() {
    super.onStop();
    if(mAdapter != null) {
        mAdapter.stopListening();
    }
}

Please see my answer from this post where I have explained why you should remove the listener.

Upvotes: 1

Related Questions