1

I'm running into some trouble figuring out how to get the number of items/children in my Firebase real-time database for my app to use as an int value.

Check the image below:

enter image description here

So as you can see above, I have 2 children of images_list.

What I want to do is: get the number of items/children of images_list returned to my app, which would obviously equal 2 initially, and have this number expand whenever I add more items/children.

So my question is, what code do I implement to grab the number of children in this database? And how could I then convert this number into an int value? I've already experimented with differing methods, but I haven't found a solution as of yet.

I hope that made sense. Thanks so much for any of your assistance!


Code solution down here; I had to move most of this code from my RecyclerView Adapter into my MainActivity for it to work

Interface

public interface FirebaseCallback {
    void onCallback(List<String> list);
}

readData method

private void readData(final FirebaseCallback firebaseCallback) {
    mDatabaseImagesRef = FirebaseDatabase.getInstance().getReference("images_list");
    ValueEventListener valueEventListener = new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            for (DataSnapshot ds : dataSnapshot.getChildren()) {
                String imageItems = ds.getValue(String.class);
                imageList.add(imageItems);
            }

            firebaseCallback.onCallback(imageList);
            Log.i("imageList.size() is: ", String.valueOf(imageList.size()));
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
            Log.i(LOG_TAG, databaseError.getMessage());
        }
    };
    mDatabaseImagesRef.addListenerForSingleValueEvent(valueEventListener);
}

called in onCreate()

readData(new FirebaseCallback() {
        @Override
        public void onCallback(List<String> list) {
            mImageAdapter.notifyDataSetChanged();
        }
    });

getItemCount() in RecyclerViewAdapter

@Override
public int getItemCount() {
    return imagesList.size();
}
Community
  • 1
  • 1

4 Answers4

1

To count the all the children beneath images_list node, please use the following lines of code:

DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference imagesListRef = rootRef.child("images_list");
ValueEventListener valueEventListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        int count = (int) dataSnapshot.getChildrenCount(); //Cast long to int
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {
        Log.d(TAG, databaseError.getMessage());
    }
};
imagesListRef.addListenerForSingleValueEvent(valueEventListener);

how could I then convert this number into an int value?

According to the official documentation, getChildrenCount() method returns a long and not an int. So you need to cast that primitive long to an int.

how to extract count from this block of code so that it can be referenced and used outside this code as well?

You cannot simply create the count variable as a global variable and use it outside the onDataChange() method because it will always be 0 due the asynchronous behaviour of this method. This means that if try to use its result outside this method, the data hasn't finished loading yet from the database and that's why is not accessible. With other words, your count will always be 0.

A quick solve for this problem would be to use the value of your count variable only inside the onDataChange() method, otherwise I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • Thanks for your comment, Alex! So I've tried implementing your method and I think I need a bit of assistance. I decided to follow your video and make an ArrayList called `imagesList`, iterating through the for loop to add items to my ArrayList as they are got from `dataSnapshot`. I then tried to return `imagesList.size()` for `getItemCount()`, but it's not returning any images, as if the number is 0? I followed your method on creating the FirebaseCallback interface and everything, but there is no images populating my list. Is there something I might be doing wrong here? – ArclightOne Aug 30 '18 at 19:10
  • I cannot be much of a help without seeing your code. So please edit your question by adding the code that you have tried so far. – Alex Mamo Aug 30 '18 at 19:12
  • I actually found a way to fix this issue last night, though it may not be very elegant. I created a SharedPreference, putting the `childrenCount` as the value in `putInt` and returned the value in `getItemCount()` and this works! The only caveat is that on a fresh install of the app, I need to call `onFinish()` and refresh the app once the Database info has been got and the SharedPreference has been set. I set a ProgressDialog to show on the first launch telling the user that the app is fetching images, right before `onFinish()` is called. Every launch after that works normally. – ArclightOne Aug 30 '18 at 19:16
  • `SharedPreferences` doesn't persist across app uninstalls. So it's not the best idea. Seeing your code, I can say, it's almost correct, but you should pass to your `onCallback` method the `count`, if this is what you need. If you need the entire list, then everything looks fine to me. So if you are using `Log.i("READ", list.size());` (**size() not toString()**) I'm sure it prints the correct size, right? – Alex Mamo Aug 30 '18 at 19:31
  • If I change `toString()` to `size()` in the `Log.i` function, it gives me the error `Wrong 2nd argument type. Found: 'int', required: 'java.lang.String'`. Also, for some reason I am not seeing any of the `Log.i` methods being called within Logcat. It makes me think something's wrong with the write-up of my code? Or maybe I'm calling them in the wrong places? I'm pretty confused. My ArrayList is instantiated and assigned as `List imagesList = new ArrayList<>()` nothing wrong there, right? So I'm not sure where I'm going wrong. – ArclightOne Aug 30 '18 at 19:42
  • Oh, I'm sorry, you're right, second parameter should be a String and not an int. You should use instead `Log.i("READ", String.valueOf(list.size()));` Does it return now the right size? – Alex Mamo Aug 30 '18 at 19:48
  • It doesn't. There must be something along the way that is causing the app to not fully initialize with this code. For instance, when I change `getItemCount()` back to returning `listOfItems()`, which is my method that uses the `SharedPreferences` setup, all the `Log.i` methods are called and show up in Logcat. But, when I change `getItemCount()` to calling `imagesList.size()` again, none of the `Log.i` messages show from within the Adapter. I tested putting `Log.i` inside my MainActivity, and that is called, but not in the adapter when `imagesList.size()` is returned. So strange. – ArclightOne Aug 30 '18 at 20:04
  • Is not strange, this is how asynchronous API's work. You need to wait for the data in order to make use of it. So when you call `readData` and try to print `Log.i("READ", String.valueOf(list.size()));` does it work? – Alex Mamo Aug 30 '18 at 20:10
  • It looks like there's something wrong with how the code is iterating through `dataSnapShot.getChildren()`, because now I am seeing the `Log.i` message from the FirebaseCallback, but with the first image showing, I am getting a number of 37, which is the actual number of items I have in my real-time database, but the next 5 images on the screen keep adding 37 to that number, so with the second image, now the number says 74, then 111, and so on. So I'm clearly not adding the items correctly to my `imagesList`, am i? – ArclightOne Aug 30 '18 at 20:11
  • To be clear, what I'm trying to do, is add each individual item from my database to the ArrayList - so item 0, like in the image in my OP, would be added as the first element, and item 1 as the second, as so on. How is this done from within `for (DataSnapshot ds : dataSnapshot.getChildren()) { String imageItems = ds.getValue(String.class); imagesList.add(imageItems); }`? – ArclightOne Aug 30 '18 at 20:12
  • Oh, I just saw that you are iterating using `getChildren()` method, directly on the `images_list` node and that is not correct you only should only use `dataSnapshot.getChildrenCount()` and put the count in `onCallback` method. You cannot `getChildren()` method on properties, only on objects. So sorry for my comments above, `Log.i("READ", String.valueOf(list.size()));` cannot print the list size. – Alex Mamo Aug 30 '18 at 20:19
  • Oh I see. So how would this be set up? Should I completely remove the for loop and only have `dataSnapshot.getChildrenCount()` as the method? How would I then set this to add the number to my imagesList ArrayList? If I change `dataSnapshot.getChildren()` in the loop to `getChildrenCount()` it gives me the error `foreach not applicable to type 'long'` – ArclightOne Aug 30 '18 at 20:29
  • `Should I completely remove the for loop and only have dataSnapshot.getChildrenCount()`? **Yes**. If you need to use the `counter` outside the callback then change the implementation, instead of passing a list, pass a long/int and when you call `readData`, print the count inside that method, right? – Alex Mamo Aug 30 '18 at 20:36
  • Ok, I'm following you so far - so now that that's done, I'm trying to figure out what I am putting as the return value in `getItemCount()`. Any advice? Obviously I can't return `readData()` because its void and `getItemCount()` requires an int. I apologize for taking so long to understand this. – ArclightOne Aug 30 '18 at 20:50
  • 1
    `getItemCount()` is an overriding method from inside your adapter class and has nothing to do with the async call. Are two different things. **[This](https://stackoverflow.com/questions/48622480/showing-firebase-data-in-listview)** is how you can get data from a Firebase real-time database and display it in a `ListView` using an `ArrayAdapter`. – Alex Mamo Aug 30 '18 at 20:55
  • I understand, but I just want to either: get the childrenCount and set that number to be returned by `getItemCount()` **or** populate an ArrayList with the individual children and return the arraylist's `.size()` to `getItemCount()`. This is all I want so that my gallery can populate itself with images based on one of these two methods. Do you know what I can input for this to work? I hope I'm making sense. – ArclightOne Aug 30 '18 at 21:12
  • Okay, I figured it out! Moving a lot of this code from within my RecyclerAdapter into the MainActivity is what made it finally work. Everything functions as it should, finally. Thanks a lot for the time you invested in helping me resolve this problem, Alex. – ArclightOne Aug 30 '18 at 22:10
0

try this

userReference = FirebaseDatabase.getInstance().getReference("images_list");
userReference.addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    num = dataSnapshot.getChildrenCount();
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            });
Momen Zaqout
  • 1,508
  • 1
  • 16
  • 17
  • This seems to be on the right track! I've been fiddling with this exact code, but I can't find a way to extract the given number from the `onDataChange()` block. Basically, I'm trying to use the number given, in this case 2, as the number returned for my Adapter in `getItemCount()`. Any ideas? – ArclightOne Aug 29 '18 at 23:36
  • Do you want to use the number "num" as a number of your Adapter? so, if the num=2, you want the list to display 2 items, right? – Momen Zaqout Aug 30 '18 at 17:35
0
mRef= FirebaseDatabase.getInstance().getReference("images_list");

on your "onDataChange" method inside the listener , use getChildrenCount() to get the number of images, you can iterate those children using i loop , Exmeple :

mRef.addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {

                    int count = dataSnapshot.getChildrenCount();
                   // here you get your images number

                  for(DataSnapshot ds : dataSnapshot.getChildren()) {
                      String key = ds.getKey()
                      // and here you get the key
                   }
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {

                }
            });
ikerfah
  • 2,612
  • 1
  • 11
  • 18
  • Thanks for your comment, ikerfah! Your solution is very similar to Momen's and just as useful! Maybe you can also help me with how to extract `count` from this block of code so that it can be referenced and used outside this code as well? I'm looking at trying to use the number from `getChildrenCount` as the number used in my Adapter's `getItemCount()` method. Does that make sense? – ArclightOne Aug 29 '18 at 23:42
  • In best practice you must return inside getItemCount the size of the datahandler ( An arrayList for example ), So you need to define a public method named for exemple setData(ArrayList items) inside your adapter and pass the new data to it ( dont forget to call notifydatasetchanged() ) , or Simply you can define a public method inside the adapter named setItemCount and pass the count variable from onDataChange to it – ikerfah Aug 30 '18 at 15:07
0

Try this code:

public void onDataChange(@NonNull DataSnapshot snap) {
        int count = (int) snap.getChildrenCount();
        //shows the number of items
}
AJ Seraspi
  • 113
  • 1
  • 14