Firebase listener in listener skipped - java

In my app I use the firebase database. There are questions and the corresponding comments stored in seperat nodes. Now I try to get the questions with the one listener and access the comments with a second listener. Unfortunately I‘m confused by their behavior: the recyclerView always gets an empty questionsList, like the second listener is skipped. But after the recyclerView got the list and the adapter is set up, my LogCat starts printing the questions and the comment information.
But why is the recyclerView populated and used before the for loop that processes the data has finished?
The method to fetch the information:
private void getQuestionsFromDatabase() {
mQuestions.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
questionList = new ArrayList<>();
for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
final String title = dataSnapshot1.child("title").getValue().toString();
final String question = dataSnapshot1.child("question").getValue().toString();
final String commentId = dataSnapshot1.child("commentId").getValue().toString();
mComments.child(commentId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
count = dataSnapshot.getChildrenCount();
QuestionModel questionModel = new QuestionModel(title, question, commentId, String.valueOf(count));
questionList.add(questionModel);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Log.d("questionList length: ", String.valueOf(questionList.size()));
recyclerViewAdapter = new RecyclerViewQuestionAdapter(questionList, getActivity());
recyclerViewlayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(recyclerViewlayoutManager);
recyclerView.setAdapter(recyclerViewAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}

It is used before, because the onDataChange is asynchronous, which means that the compiler will not wait until data is fetched from the database, instead it will execute the code after the listener. Therefore to solve your problem, you should do the following:
mComments.child(commentId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
count = dataSnapshot.getChildrenCount();
QuestionModel questionModel = new QuestionModel(title, question, commentId, String.valueOf(count));
questionList.add(questionModel);
Log.d("questionList length: ", String.valueOf(questionList.size()));
recyclerViewAdapter = new RecyclerViewQuestionAdapter(questionList, getActivity());
recyclerViewlayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(recyclerViewlayoutManager);
recyclerView.setAdapter(recyclerViewAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}

Related

recyclerview in FirebaseRecyclerviewAdapter turn all longth but write just one

I have a regular RecyclerView in FRA I set up in onbindviewholder class. İt turns longth as you see in app there is a space for all items normally I should see there reply text but just one can see.
dbreply.child(post_key).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot ds:snapshot.getChildren()) {
for (DataSnapshot snapshot1:ds.getChildren()) {
reply data = snapshot1.getValue(reply.class);
list.add(data);
holder.rcv.setLayoutManager(new LinearLayoutManager(getContext()));
myAdapter = new MyAdapter(getContext(), list);
holder.rcv.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged();
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) { }});
You are getting that behavior because you are creating a new instance of MyAdapter at each iteration of the inner loop. What you should is to get all those 4 lines of code out of the loop, as you can see below:
for (DataSnapshot ds:snapshot.getChildren()) {
for (DataSnapshot snapshot1:ds.getChildren()) {
reply data = snapshot1.getValue(reply.class);
list.add(data);
}
holder.rcv.setLayoutManager(new LinearLayoutManager(getContext()));
myAdapter = new MyAdapter(getContext(), list);
holder.rcv.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged();
}

Firebase database set objects in favorite list

I have a problem clearing list in fragments. I need to clear the list when happening update in fragment. I already put it in different places but it does not help me.
When an update happens in fragment it shows the same list but that list also add elements from the previous list. If I put list.clear(); in the moment when it rakes arguments from firebase it did not give me needed effect, when I put it in onCreate() method then app gives me error.
public class Favourite extends Fragment {
private RecyclerView recyclerView;
private ArrayList<TourReader1> artistList;
private ToursAdapter1 adapter1;
private DatabaseReference reference,reference2;
String uid;
public Favourite() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
//Get current user id
uid = user.getUid();
//Connect to specific point in database Tours
reference = FirebaseDatabase.getInstance().getReference("Tours");
//Create add to method valuelistener or reference which is specific point in database
reference2 = FirebaseDatabase.getInstance().getReference("user").child(uid);
reference.addValueEventListener(valueEventListener);
reference.keepSynced(true);
}
ValueEventListener valueEventListener = (new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot1) {
//get all information from specific point of database
for (DataSnapshot datasnapshot : snapshot1.getChildren()) {
//get current name of starting point from where we recive information from database
String firstname = datasnapshot.getKey();
//valuelistener for another point from database
reference2.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot dSnapshot2:snapshot.getChildren()){
String seconame = dSnapshot2.getKey();
Boolean control = dSnapshot2.child("like").getValue(Boolean.class);
if(control != null&&firstname.equals(seconame) &&control.equals(true)) {
TourReader1 artist = datasnapshot.getValue(TourReader1.class);
artistList.add(artist);
}
}
adapter1.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
artistList = new ArrayList<>();
View rootview = inflater.inflate(R.layout.favourite, container, false);
recyclerView = rootview.findViewById(R.id.list);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapter1 = new ToursAdapter1(getContext(), artistList);
recyclerView.setAdapter(adapter1);
GridLayoutManager mGridLayoutManager = new GridLayoutManager(getContext(), 2);
recyclerView.setLayoutManager(mGridLayoutManager);
return rootview;
}
}
You should clear the artistList using artistList.clear() before your for loop in onDataChangeas this list is having the data from the last callback triggered.
Ok, I found the answer myself, I clear my list in the adapter by tourlist.clear() and remove userpoints.keepSynced(true); when like icon is false.
holder.sparkButton.setEventListener(new SparkEventListener() {
#Override
public void onEvent(ImageView button, boolean buttonState) {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String uid = user.getUid();
DatabaseReference userpoints = FirebaseDatabase.getInstance().getReference("user").child(uid);
ToursLike registerDatabase = new ToursLike(true);
if(buttonState){
userpoints.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (!dataSnapshot.hasChild(tour.Name)) {
userpoints.child(tour.Name).setValue(registerDatabase);
userpoints.keepSynced(true);
tourList.clear();
}
else{
userpoints.child(tour.Name).child("like").setValue(true);
userpoints.keepSynced(true);
tourList.clear();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
else{
userpoints.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
userpoints.child(tour.Name).child("like").setValue(false);
tourList.clear();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
#Override
public void onEventAnimationEnd(ImageView button, boolean buttonState) {
}
#Override
public void onEventAnimationStart(ImageView button, boolean buttonState) {
}
});
}

Getting the key of its selected child value in a Spinner using Android Studio and Firebase

I'm trying to get the key of a certain child value by allowing the user to choose from a Spinner.
The Spinner has "Product_Name" values as its choices. By choosing one, the program should get its key and use it to get another child value for other uses.
Example:
PRODUCTS->
-LoUXnfUCEj4k4A3dkte->
Product_Name:"Steak"
By choosing "Steak" in the Spinner, I have to get "-LoUXnfUCEj4k4A3dkte"
databaseRefSelectItem = FirebaseDatabase.getInstance().getReference("PRODUCTS");
final DatabaseReference mDatabase = databaseRefSelectItem;
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull final DataSnapshot dataSnapshot) {
//We create an array list to hold the values brought from the database and show them in the spinner
final List<String> titleList = new ArrayList<String>();
for(final DataSnapshot snapshot : dataSnapshot.getChildren()) {
titleProduct = snapshot.child("Product_Name").getValue(String.class);
//populate the spinner with that array list
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(AddTransactionActivity.this, android.R.layout.simple_spinner_item, titleList);
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
selectProduct.setAdapter(arrayAdapter);
titleList.add(titleProduct);
//Click event for each spinner element
selectProduct.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
//pass the reference from that value into another snapshot in order to query those values, here you need to get your node id and inside just get your number , name and so on
selectItem = titleList.get(i);
mDatabase.child(selectItem).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(final DataSnapshot dataSnapshot2) {
key = dataSnapshot2.getKey();
currentItemStock = dataSnapshot2.child(key).child("Current_Stock").getValue(String.class);
currentStk.setText(key);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
How can I get "-LoUXnfUCEj4k4A3dkte"?
Notes:
-LoUXnfUCEj4k4A3dkte is randomly generated.
Use .getKey() to get the key of the snapshot like:
for(final DataSnapshot snapshot : dataSnapshot.getChildren()) {
if (snapshot.child("Product_Name").getValue(String.class).equals("Steak")){
String theKey = snapshot.getKey(); //This will return -LoUXnfUCEj4k4A3dkte
}
}
Will return key of the snapshot at that reference.
Found a solution.
Thank you as well to Yash Krishan for the code snippet :).
databaseRefSelectItem = FirebaseDatabase.getInstance().getReference("PRODUCTS");
final DatabaseReference mDatabase = databaseRefSelectItem;
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull final DataSnapshot dataSnapshot) {
//We create an array list to hold the values brought from the database and show them in the spinner
final List<String> titleList = new ArrayList<String>();
for(final DataSnapshot snapshot : dataSnapshot.getChildren()) {
titleProduct = snapshot.child("Product_Name").getValue(String.class);
//populate the spinner with that array list
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(AddTransactionActivity.this, android.R.layout.simple_spinner_item, titleList);
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
selectProduct.setAdapter(arrayAdapter);
titleList.add(titleProduct);
//Click event for each spinner element
selectProduct.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
//pass the reference from that value into another snapshot in order to query those values, here you need to get your node id and inside just get your number , name and so on
selectItem = titleList.get(i);
if (titleProduct.equals(selectItem)){
key = dataSnapshot.child(selectItem).getKey(); //This will return -LoUXnfUCEj4k4A3dkte
}
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull final DataSnapshot dataSnapshot2) {
for(final DataSnapshot snapshot : dataSnapshot.getChildren()) {
if (snapshot.child("Product_Name").getValue(String.class).equals(selectItem)){
key = snapshot.getKey().toString();
}
keyholder = dataSnapshot.child(key).child("Current_Stock").getValue(String.class);
}
currentStk.setText(keyholder);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});

Chat messages saved in firebase database but not able to view in listview

For my chat application, I have saved EditText data to firebase but not I am not able to display the same in ListView. I am able to see the EditText data in Firebase database and I want to retrieve it in a ListView. Anybody can help me?
public class Chatbox extends AppCompatActivity {
private DatabaseReference databaseReference;
private Button btnsend;
private EditText editText;
private ListView listView;
private ArrayList<String> arrayList = new ArrayList<>();
private ArrayAdapter<String> adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chatbox);
databaseReference = FirebaseDatabase.getInstance().getReference();
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, arrayList);
btnsend = findViewById(R.id.sendbutton);
editText = findViewById(R.id.typemessage);
listView = findViewById(R.id.listmessages);
btnsend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
databaseReference.child("Chat").child("Name").push().setValue(editText.getText().toString());
}
});
databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
DatabaseReference chatreference=databaseReference.child("Chat");
chatreference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
try {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
String chatmsg = snapshot.child("Name").getKey();
Toast.makeText(Chatbox.this, "keys " + chatmsg, Toast.LENGTH_SHORT).show();
arrayList.add(chatmsg);
}
listView.setAdapter(adapter);
}
catch (Exception ex)
{
Toast.makeText(Chatbox.this, "Error " + ex.getMessage(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Try this
FirebaseDatabase.getInstance()
.getReference("Chat")
.child("Name")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
List<String> chatMessages = new ArrayList<>();
for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
try {
String chatMessage = String.valueOf(dataSnapshot.child(dataSnapshot1.getKey()).getValue());
chatMessages.add(chatMessage);
} catch (DatabaseException e) {
e.printStackTrace();
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
callback.onFailure(null);
}
});
i guess you want to retrive data not the key:
databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
DatabaseReference chatreference=databaseReference.child("Chat");
chatreference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
String chatmsg = snapshot.child("Name").getValue();
Toast.makeText(Chatbox.this, "keys " + chatmsg, Toast.LENGTH_SHORT).show();
arrayList.add(chatmsg);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
or want you to store both in a array list key and value together?
You're adding the messages to the database with:
databaseReference.child("Chat").child("Name")
That means you must also read the messages from the same path:
databaseReference.child("Chat").child("Name").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
String chatmsg = snapshot.getValue(String.class);
arrayList.add(chatmsg);
}
listView.setAdapter(adapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException()l
}
});
The changes:
Listen to the same node as you're adding the messages to.
Removed the nested listener, which is not needed.
Get the value from each child snapshot, instead of trying to read a non-existing Name property.
Removed the try...catch as the default behavior is to write messages to logcat, which is more useful than showing them in a toast.
Implemented onCancelled, because you should never ignore errors.

ValueEventListener never triggered

I have a FragmenPageAdpater class where I am trying to get info about the current user so that I can load the right fragment for this specific user.
The ValueEventListener never gets triggered and it keeps skipping the body part.
public SessionListAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
mAuth = FirebaseAuth.getInstance();
FirebaseDatabase myData = FirebaseDatabase.getInstance();
final DatabaseReference dataUser = myData.getReference().child("Users").child(mAuth.getCurrentUser().getUid());
dataUser.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.child("type").getValue(String.class).equals("Tutor")) //if(readType.equals("Tutor")) if user is a Tutor
user = dataSnapshot.getValue(Tutor.class);
else
user = dataSnapshot.getValue(Student.class);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Here is the necessary portion of my database structure.
You need to use addListenerForSingleValueEvent like the following. I am referring to this Firebase documentation.
dataUser.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.child("type").getValue(String.class).equals("Tutor"))
user = dataSnapshot.getValue(Tutor.class);
else
user = dataSnapshot.getValue(Student.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
Hope that helps!

Categories