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:
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();
}
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.
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) {
}
});
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) {
}
});
Try this code:
public void onDataChange(#NonNull DataSnapshot snap) {
int count = (int) snap.getChildrenCount();
//shows the number of items
}
Related
I am trying to detect value change from my Firebase Database. Here is my code for initializing the ValueEventListener:
valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
try {
String customerLocation = String.valueOf(dataSnapshot.getValue());
Point customerPoint = locationFounder.getPoint(customerLocation);
if (customerPoint != null) {
databaseReference.child("isActive").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
boolean isActive = Boolean.valueOf(String.valueOf(dataSnapshot.getValue()));
displayPopUp(isActive, customerPoint, customerLocation);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
} catch (Exception e) {
Log.d("Listener",e.toString());
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
};
destinationReference.addValueEventListener(valueEventListener);
Problem occurs when I want to call this listener in my activity. I've been trying with this:
destinationListener.getValueEventListener().onDataChange(REQUIRED_SNAPSHOT);
I do not know how can I get datasnapshot which is required for onDataChange. I would like to get this work with ValueEventListener, not ChildEventListener, if possible. However, I am not pretty sure that this is the right way of trying to detect value change. If there is any other way that will work properly, I'd like to know about it.
There is nothing built to the Firebase Realtime Database to tell you what specific data under the snapshot has changed in the onDataChange method.
If you want to know what specific property has change, you'll need to:
Keep the snapshot that you get in onDataChange in a field.
When onDataChange gets called, compare the data in the new snapshot with the data in the field.
Say that you have a reference on a node in your JSON, and under that node is a status property, and you want to both listen to the entire node, and detect if the status has changed.
You'd do that with something like:
// Add a field to your class to keep the latest snapshot
DataSnapshot previousSnapshot;
// Then add your listener
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
bool wasActive = false;
if (previousSnapshot != null && previousSnapshot.child("status").exists()) {
wasActive = dataSnapshot.child("status").getValue(Boolean.class);
}
boolean isActive = dataSnapshot.child("status").getValue(Boolean.class);
if (isActive <> wasActive) {
... the user's status changed
}
previousSnapshot = dataSnapshot;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
});
I have a block of code that gets called before another block of code. I want the first block of code to execute entirely before the other code gets called as it is null otherwise. The code is shown below
public void displayGraph1() {
xnumsList = new ArrayList<>();
namesList = new ArrayList<>();
xxx = new ArrayList<>();
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("child");
ref.addValueEventListener(new ValueEventListener() {
#Override
public synchronized void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
key = child.getKey();
DatabaseReference checkingWhoisInWhatChild = FirebaseDatabase.getInstance().getReference().child("child").child(key);
checkingWhoisInWhatClass.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String module = dataSnapshot.child("a").getValue().toString();
String xnum = dataSnapshot.child("b").getValue().toString();
for (int i = 0; i < arrayOfModules.size(); i++) {
String lecturerModule = arrayOfModules.get(i);
if (module.equals(lecturerModule)) {
xxx.add(xnum);
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
//null array
for (int i = 0; i < xxx.size(); i++) {
Log.d("dddd", "arraycontents" + xxx.get(i));
}
Obviously this is not the whole method but my point is that the loop at the bottom gets called before the preceding code due to asynchronous calls. I want a way to make sure that the code above is executed first so that the size of the array in the loop is not null. I have debugged the code and the array is not empty / null normally so I'm 100% sure that this is an asynchronous call that is causing the issue of the empty array in the loop. I'm looking for a way to synchronize the code, I have tried using the keyword "synchronized" on the functions and I have even tried to make a single thread for the entire function but this hasn't proven to work for me. Any help is appreciated.
Thank you
I have a list in java android:
List<FirebaseData> GetListMap(String data){
final List<FirebaseData> Elements = new ArrayList<>();
FirebaseDatabase database = FirebaseDatabase.getInstance();
mShiftReference = database.getReference("Map/Data/").child(data);
mListener = mShiftReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot child: dataSnapshot.getChildren()) {
Elements.add(child.getValue(FirebaseData.class));
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
// Failed to read value
Log.w("data", "Failed to read value.", error.toException());
}
});
return Elements;
}
When i try to exctract the size of the array it returns 0, for example:
int size = GetListMap("2019-06-24").size();
Or i can't exctract any property.
Any idea what i'm doing wrong.
First, I assume you mean int size = GetListMap("2019-06-24").size(); (size at the end).
Second, you are assigning a list to the returned value, it's an empty list at this moment. Then you attach a listener that waits for an event (data change), if that happens, then and only then it will add an element to the list.
Basically, your list is always gonna be empty, unless the data change events occur. You need to trigger an event and then check the list, it should change it's size.
You're returning your Elements collection before it actually gets data from your Firebase Database due to the asynchronous nature of your listener.
You'd have to define a callback if you want to get an update after your async code is done
List<FirebaseData> GetListMap(String data){
final List<FirebaseData> Elements = new ArrayList<>();
FirebaseDatabase database = FirebaseDatabase.getInstance();
mShiftReference = database.getReference("Map/Data/").child(data);
mListener = mShiftReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
System.out.println("About to fill Elements");
for (DataSnapshot child: dataSnapshot.getChildren()) {
Elements.add(child.getValue(FirebaseData.class));
}
System.out.println("Elements is filled");
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
// Failed to read value
Log.w("data", "Failed to read value.", error.toException());
}
});
System.out.println("Returning Elements with size: " + Elements.size());
return Elements;
}
int x = GetListMap("...").size();
System.out.println("x: +" x);
``
That code would result in following output:
Returning Elements with size 0
x: 0
About to fill Elements
Elements is filled
Btw please use camelCase for your methods and variables whenever you are developing in Java
You are returning the list before firebase has finished adding everything. You want to create an interface method for the function.
in the class create an interface:
public interface Complete{
void taskCompleted(List<FirebaseData> list);
}
then assign it to your method (now returns void as the interface will be producing the result):
public void GetListMap(String data, Complete completeListener){
. . . .
mListener = mShiftReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot child: dataSnapshot.getChildren()) {
Elements.add(child.getValue(FirebaseData.class));
}
completeListener.taskComplete(Elements);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
completeListener.taskComplete(null);
}
});
}
To use method:
GetListMap("data", new Complete() {
#Override
public void taskCompleted(List<FirebaseData> list) {
//do what you want with the list object
}
});
First of all your GetListMap method contains firebase database call which of-course asynchronous call. Means you are getting data on separate thread. So the line below this calls execute before firebase process query response. So it is not possible to get returned value from method which contains asynchronous functions.
So to get exact what you want, you will have to wait for query response.
Refer below code to achieve what you want.
// Avoiding return type with this method.
void GetListMap(String data){
final List<FirebaseData> Elements = new ArrayList<>();
FirebaseDatabase database = FirebaseDatabase.getInstance();
mShiftReference = database.getReference("Map/Data/").child(data);
mListener = mShiftReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot child: dataSnapshot.getChildren()) {
Elements.add(child.getValue(FirebaseData.class));
}
// Use below method to update data inside your views.
// This method will only called once firebase query return response.
updateUI(Elements);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
// Failed to read value
Log.w("data", "Failed to read value.", error.toException());
}
});
// Now this method is void so need to return any value.
//return Elements;
}
Example of updateUI(...) method.
void updateUI(List<FirebaseData> elements) {
int size = elements.size();
}
I am building an app that needs to retrieve all child from a certain parent node and would like to perform a method call on each child retrieved.
Here is my current database:
Click Me
I would like to call all child of conradjr and on retrieval of a child for example "ranniecardino15", it should store it to a variable then perform a method call. How can I do this?
I have made a code like this but the method call "getCurrentLocation" is only performed on the last child retrieved.
public void getCurrentChildUser() {
DatabaseReference getuser = FirebaseDatabase.getInstance().getReference().child("Children");
getuser.child(user).orderByChild("CurrentLocation").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot childSnapshot : dataSnapshot.getChildren()) {
childuser = childSnapshot.getKey();
if (childuser != null){
getCurrentLocation();
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
public void getCurrentLocation(){
DatabaseReference getuser = FirebaseDatabase.getInstance().getReference().child("Children");
getuser.child(user).child(childuser).child("CurrentLocation").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot != null){
currentloc = dataSnapshot.getValue(String.class);
System.out.println("Current Location: "+currentloc);
String split[] = currentloc.split(",");
lat1 = Double.parseDouble(split[0]);
lng1 = Double.parseDouble(split[1]);
System.out.println("Current Latitude: "+lat1+" and Current Longitude: "+lng1);
getSavedLocation();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
Please help me :) Thanks in advance :)
To solve this, you need to change the declaration of your getCurrentLocation() method from:
public void getCurrentLocation(){}
to
public void getCurrentLocation(String childuser){}
Now you need to call this method from inside the onDataChange() method like this:
getCurrentLocation(childuser);
See, I have passed the childuser as an argument to the getCurrentLocation() so it can be used in a proper way.
I am trying to load data from firebase database and then I want to add some more data to it and in the end I want it to upload back. But the problem is when the data is being uploaded back it is not stopping after updating the data. It looks like it is under loop and doing the task again and again.
mTempDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.hasChild(mAuth.getCurrentUser().getUid())){
final DatabaseReference newTemp = mTempDatabase.child(mAuth.getCurrentUser().getUid());
final long qntCount = (long) dataSnapshot.child(mAuth.getCurrentUser().getUid()).child("QuantityCount").getValue();
final long foodamount = (long) dataSnapshot.child(mAuth.getCurrentUser().getUid()).child("QuantityCount").getValue();
//final long countfood = (long) dataSnapshot.child(mAuth.getCurrentUser().getUid()).child("QuantityCount").getValue();
mDatabaseBestseller.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String foodname = (String) dataSnapshot.child(food_key).child("foodname").getValue();
long foodprice = (long) dataSnapshot.child(food_key).child("foodprice").getValue();
long mfoodprice = foodprice + foodamount;
long mqntCOunt = qntCount +1;
newTemp.child("QuantityCount").setValue(mqntCOunt);
newTemp.child("FoodPrice1").setValue(mfoodprice);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
mTempDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.hasChild(mAuth.getCurrentUser().getUid())) {
final DatabaseReference newTemp = mTempDatabase.child(mAuth.getCurrentUser().getUid());
mDatabaseBestseller.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String foodname = (String) dataSnapshot.child(food_key).child("foodname").getValue();
long foodprice = (long) dataSnapshot.child(food_key).child("foodprice").getValue();
newTemp.child("QuantityCount").setValue(1);
newTemp.child("CountFood1").setValue(1);
newTemp.child("Food1").setValue(foodname);
newTemp.child("FoodPrice1").setValue(foodprice);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
viewHolder.mFoodAddLayout.setVisibility(View.INVISIBLE);
viewHolder.mIncrLayout.setVisibility(View.VISIBLE);
}
});
According to my understanding I have did wrong math while retrieving the data and adding the data back.
addValueEventListener() will always listening to database reference value changes.if you want to stop listening you have to use removeEventListener(valueListener),
But addListenerForSingleValueEvent() executes onDataChange method immediately and after executing that method once, it stops listening to value changes.
You need to use the .addListenerForSingleValueEvent() instead of .addValueEventListener(). The difference is, the second one keep updating every time a value is changed in the database, which keep happening as you update your value inside the function. But, the first method that I am suggesting, just read the values once, which is what you need here.