Dynamically update getChildCount() from Firebase? - android

I'm trying to update a RecyclerView with information from Firebase. I've successfully been able to update the TextViews in my view from Firebase but, in doing so, my current code won't update the getChildCount() of the RecyclerView with the amount of children from the database without infinitely looping.
My current code: (CardAdapter.java)
public int getChildCount() {
mDatabase = FirebaseDatabase.getInstance();
mReference = mDatabase.getReference();
final String userID = FirebaseAuth.getInstance().getCurrentUser().getUid();
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snap :
dataSnapshot.child("pet").child("users").child(userID).getChildren()) {
mChildCount = (int) snap.getChildrenCount();
}
notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mReference.addListenerForSingleValueEvent(listener);
return mChildCount;
}
This code works, but it infinitely loops due to the notifyDataSetChanged(); method that updates the RecyclerView.
What can I do to make it so the RecyclerView getChildCount() is dynamically updated from Firebase without infinitely looping?

You should not place your code to acquire data inside getChildrenCount(). I myself, usually place that in my Activity or inside constructor of Adapter. Like this:
public class YourAdapter extends RecyclerView.Adapter<YourAdapter.ViewHolder> {
private List<YourObject> yourObjectList;
public YourAdapter() {
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snap : dataSnapshot.child("pet").child("users").child(userID).getChildren()) {
...
// add to list
yourObjectList.add(snap.getValue(YourObject));
...
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mReference.addListenerForSingleValueEvent(listener);
...
}
#Override
public void onBindViewHolder(YourAdapter.ViewHolder holder, int position) {
YourObject yourObject = yourObjectList.get(position);
...
}
#Override
public int getItemCount() {
return yourObjectList.size();
}
}
Hope this help.

Related

Why data from firebase database did not show in Spinner?

As refer in this link. I want to try in my coding apps. But it didnt retrieve any data from my firebase database. I just cant figure out how to fix it. Can someone please help me. Is there any idea, where did I miss?
My firebase database as show image below:-
Spinner coding:-
mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.keepSynced(true);
mDatabase.child("Advertisement").child(mAuth.getUid()).addValueEventListener(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
//final List<String> name = new ArrayList<String>();
ArrayList<String> identity = new ArrayList<>();
identity.add(0, "Choose Tuition Centre");
for (DataSnapshot nameSnapshot: dataSnapshot.getChildren())
{
String tuitionName = nameSnapshot.child("adstuitioname").getValue(String.class);
identity.add(tuitionName);
}
ArrayAdapter<String> nameAdapter = new ArrayAdapter<String>(RatingActivity.this, android.R.layout.simple_spinner_item, identity);
nameAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mName.setAdapter(nameAdapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
mName.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
{
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
if(position==0)
{
//Toast.makeText(getApplicationContext(),"No Item Selected",Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(getApplicationContext(),parent.getItemAtPosition(position) +" Selected",Toast.LENGTH_SHORT).show();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent)
{
// TODO Auto-generated method stub
}
});
Add listener as below :
DatabaseReference mDatabaseRef =
FirebaseDatabase.getInstance().getReference();
ArrayList<String> list=new ArrayList<>();
mDatabaseRef.child("Advertisement")
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot :dataSnapshot.getChildren())
{
Advertisementject advertisement=
snapshot.getValue(Advertisement.class);
//replace Object.class with your class name
//get your key value here from your "Custom class"
// which contains "adstutioname"
// add in list
list.add(advertisement.getAdstutuioname());
}
//set adapter
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
generate class like this
public class Advertisement{
#SerializedName("adstutuioname")
#Expose
private String adstutuioname;
public String getAdstutuioname() {
return adstutuioname;
}
public void setAdstutuioname(String adstutuioname) {
this.adstutuioname = adstutuioname;
}
}
Your class contains all your params and .
Replace Object with this class
I hope the problem is with your getValue() method. Firebase getValue method returns an Object, and you are expecting a String. Please modify this line to convert your Object to String -
String tuitionName = nameSnapshot.child("adstuitioname").getValue(String.class).toString();
It's not clear if you're trying to read once, or watch changes Difference between addValueEventListener() and addListenerForSingleValueEvent() of firebase
First things first, look if you are getting a database error
#Override
public void onCancelled(DatabaseError databaseError) {
// Toast or print the databaseError, don't ignore it
}
Then look where you're adding a listener - mDatabase.child("Advertisement").child(mAuth.getUid())., which from your picture is every element at the level of -LNZ6....
For that level, you have a list of object's, so you can need to use a loop, and as shown from some examples on Firebase site, it's recommended that you make an object, but you still can use child method for a certain field as you're trying to do
But if a field doesn't exist, you get null value, and Adapters cannot hold nulls, so you must ignore or set some default value.
ArrayList<String> identity = new ArrayList<>();
identity.add(0, "Choose Tuition Centre");
for (DataSnapshot nameSnapshot: dataSnapshot.getChildren()) {
String tuitionName = nameSnapshot.child("adstuitioname").getValue(String.class);
Log.d("DEBUG TUITION", String.valueOf(tuitionName); // look for this in your logs
identity.add(tuitionName == null ? "none" : tuitionName);
}
ArrayAdapter<String> nameAdapter =...

RecyclerView notifyDataSetChanged() not working

I'm using firebase for chat app and I'm implementing to fetch messages from firebase database and update recycler view.
When I click the "send" button, it acts like below.
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final ChatModel.Comment comment = new ChatModel.Comment();
comment.uid = uid;
comment.message = editText.getText().toString();
FirebaseDatabase.getInstance().getReference().child("chatrooms")
.child(chatroomUid).push().setValue(comment).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
FirebaseDatabase.getInstance().getReference().child("chatrooms")
.child(chatroomUid).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
comments.clear();
for(DataSnapshot item : dataSnapshot.getChildren()) {
if(item.getKey().compareTo("users")!=0) {
comments.add(item.getValue(ChatModel.Comment.class));
}
}
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
});
}
});
Process is like this.
Store message data into firebase database using "Comment" object form.
If stage 1 is succeeded, fetch that message and call notifyDataSetChanged()
But it is not working, I have global adpater object and above method is called by this variable.
Adapter code is like below.
private class messageRecyclerveiwAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_message, viewGroup, false);
return new MessageViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder viewHolder, int i) {
((MessageViewHolder)viewHolder).textView.setText(comments.get(i).message);
}
#Override
public int getItemCount() {
return comments.size();
}
}
How can I fix it?
You need to create an object of your Adapter in the same class as you have the setOnClickListener:
Something like this:
final MessageRecyclerViewAdapter adapter = new MessageRecyclerViewAdapter(getActivity(), messageArrayList);
Then you need to pass the adapter to the method you're using to get the messages. Right now, you're calling notifyDataSetChanged() in the adapter which is wrong. First you need a specific method to get those messages from Firebase. For example:
public void getLiveChatMessages(final ArrayList<ChatMessageClass> messageArrayList, final MessageRecyclerViewAdapter adapter) {
I'm guessing you have a class that returns different part of the messages.. That's what I've named "ChatMessageClass" here, but you may call it something else. Within this method you get the messages from Firebase, then afterward you call as the last thing in the method:
adapter.notifyDataSetChanged();}
within the getLiveChatMessages method. Here you also pass in the adapter so that you can use it. This is the way to call the notifyDataSetChanged() within the same method as where you get the messages.
Good luck!

Firebase query wait for single value event listener

I'm trying to get data from firebase to update the contents in adapter. I need to wait for it to load the result asynchronously but onDataChange is never called. I'm using the following code:
class AttendanceAdapter extends PagerAdapter implements View.OnTouchListener {
private loginInfo_Collector mUserInfoCollector;
private FirebaseDatabase mDatabase;
private DatabaseReference mUsersInfoRef;
private DatabaseReference mUser_dataAttRef;
AttendanceAdapter(Context context){
this.context = context;
}
#Override
public int getCount() {
if(mRollDisplayList.isEmpty()) {
loadDatabase();
getCount();
}
return mRollDisplayList.size();
}
private void loadDatabase(){
mDatabase = FirebaseDatabase.getInstance();
mUsersInfoRef = mDatabase.getReference()
.child(AttendanceActivity.mFacultyI)
.child(AttendanceActivity.mYearI)
.child("users");
setViewContentsFromDb();
}
private void setViewContentsFromDb(){
mUsersInfoRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (mRollList.isEmpty()) {
for (DataSnapshot users : dataSnapshot.getChildren()) {
uIdList.add(users.getKey());
mUserInfoCollector = users.getValue(loginInfo_Collector.class);
mNameList.add(mUserInfoCollector.getName());
mRollList.add(mUserInfoCollector.getRoll_no());
mPhotoUriList.add(Uri.parse(mUserInfoCollector.getPhoto_url()));
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
I've tried using while with condition that list is not empty else stay in empty loop but list is never updated.Is there any way to wait for query to finish before returning the result?
Database Structure:
1
Edit Solved: Moved the database code to before setting the adapter, and made the variables public and static to access them from adapter class.
To solve this, only the following lines of code are needed:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference usersRef = rootRef.child("BCT/2072/notice_node/users");
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<String> mNameList = new ArrayList<>();
List<String> mRollList = new ArrayList<>();
List<String> mPhotoUriList = new ArrayList<>();
for(DataSnapshot ds : dataSnapshot.getChildren()) {
loginInfo_Collector mUserInfoCollector = users.getValue(loginInfo_Collector.class);
mNameList.add(mUserInfoCollector.getName());
mRollList.add(mUserInfoCollector.getRoll_no());
mPhotoUriList.add(Uri.parse(mUserInfoCollector.getPhoto_url()));
Log.d("TAG", mUserInfoCollector.getName());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
usersRef.addListenerForSingleValueEvent(valueEventListener);
The output in the console will be the name of all users.
Note, a more elegant way would to create only a single List<loginInfo_Collector> and add objects of type loginInfo_Collector inside it and not create 3 different lists as you do.

How to retrieve data from firebase database without listening for events [duplicate]

This question already has answers here:
How to return DataSnapshot value as a result of a method?
(6 answers)
Closed 4 years ago.
Hello I am using Firebase database for my android app.I have my data stored on firebase database and I want to get it whenever the fragment gets created but when I start my app the method(which gets data from firebase database) returns null
and whenever I resume the fragment with the android device the recyclerView gets populated with the data from firebase.
My question is why the list returns null first time. and How can I get data whenever the fragment gets created
OffersFragment
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View offersView = inflater.inflate(R.layout.fragment_offers, container, false);
mOffersRecyclerView = offersView.findViewById(R.id.rv_offers);
mAddCard = offersView.findViewById(R.id.fab_add_card);
setUp();
return offersView;
}
#Override
public void setPresenter(OffersContract.Presenter presenter) {
mPresenter = presenter;
}
#Override
public void setUp() {
mOffersList = mPresenter.getOffersList();
mOffersAdapter = new OffersAdapter(getActivity(), mOffersList);
mOffersRecyclerView.setAdapter(mOffersAdapter);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
mOffersRecyclerView.setLayoutManager(layoutManager);
mOffersRecyclerView.setHasFixedSize(false);
performActions(mAddCard);
}
OffersPresenter
#Override
public List<Offers> getOffersList() {
return databaseHelper.getOffers();
}
DatabaseHelper -> getOffers()
public List<Offers> getOffers() {
DatabaseReference offerReference = getDatabase().child("offers");
offerReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final Iterable<DataSnapshot> children = dataSnapshot.getChildren();
for (DataSnapshot d : children) {
offersList.add(d.getValue(Offers.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return offersList;
}
The firebase callbacks are async so the method getOffers will return null before it gets updated by callback.
You have to pass your adapter instance and list instance to firebase callback so that it update from there
Try this mechanism:
In your activity initialize your list
#Override
public void setUp() {
mOffersList = new ArrayList<Offers> ();
mOffersAdapter = new OffersAdapter(getActivity(), mOffersList);
mOffersRecyclerView.setAdapter(mOffersAdapter);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
mOffersRecyclerView.setLayoutManager(layoutManager);
mOffersRecyclerView.setHasFixedSize(false);
performActions(mAddCard);
}
Update your getter
#Override
public void getOffersList(List<Offers> offers, OffersAdapter adapter) {
return databaseHelper.getOffers(offers, adapter);
}
Helper will be like this:
public void getOffers(List<Offers> offers,OffersAdapter adapter) {
if (offers == null) offers = new ArrayList<Offers> ();
DatabaseReference offerReference = getDatabase().child("offers");
offerReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final Iterable<DataSnapshot> children = dataSnapshot.getChildren();
for (DataSnapshot d : children) {
offers.add(d.getValue(Offers.class));
}
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Keep in mind you are using value event which will gets called if any data changes and as a result data will be added multiple times. Use single value event or Child event listener to solve the issue if you have any.
It returns null because you are placing your return offersList; at the end of public List<Offers> getOffers() Remove the return statement and make getOffers() void and add logic to Get/Update your data and/or commit() your Fragment inside of onDataChange(DataSnapshot dataSnapshot), so it will return your list when the data is fetched the first time.
What I usually do is to commit the fragment or set its arguments inside of
onDataChange(DataSnapshot dataSnapshot)

onDataChange not called

I'm trying to read from Firebase (offline), but the method onDataChange is never called,
private class MyAuthStateListener implements Firebase.AuthStateListener {
#Override
public void onAuthStateChanged(AuthData authData) {
if (authData != null) {
// user is logged in
userId = authData.getUid();
mFirebase = mFirebase.child(authData.getUid()).child("xxx");
Query mQuery = mFirebase.orderByChild("externalId").equalTo(externalId);
mQuery.addListenerForSingleValueEvent(new MyValueEventListener());
}
}
}
private class MyValueEventListener implements ValueEventListener {
#Override
public void onDataChange(DataSnapshot mDataSnapshot) {
if (mDataSnapshot.hasChildren()) {
Iterable<DataSnapshot> i = mDataSnapshot.getChildren();
Iterator<DataSnapshot> mIterator = i.iterator();
if (mIterator.hasNext()) {
mArrayList.clear();
}
while (mIterator.hasNext()) {
DataSnapshot c = mIterator.next();
mArrayList.add(c.getValue(mObject.class));
}
}
onTaskComplete(); // call the notifyDataSetChange() on the adapter.
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
}
Have I done something wrong?
Online it works well, but offline it won't works.
The new objects are created with a FAB that open a new activity (to modify the object) and than I return to that activity.
Thank you all for the help.

Categories

Resources