I want data from firebase to be printed out in recyclerview list.
The loop is correct but, it didn't replace my data.
This is from the track_record code.
private void readData(){
reference = FirebaseDatabase.getInstance().getReference("FireAlarm");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
dataList.clear();
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
Data data = snapshot.getValue(Data.class);
dataList.add(data);
}
recordAdapter = new RecordAdapter(track_record.this,dataList);
recyclerView.setAdapter(recordAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
this is from my adapter
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_data,parent,false);
return new RecordAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Data data = dataList.get(position);
holder.outputData.setText(data.getAlarm());
}
#Override
public int getItemCount() {
return dataList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView outputData;
public ViewHolder(View itemView){
super(itemView);
outputData = itemView.findViewById(R.id.dataFetch);
}
}
this is from xml the data should be replaced
<TextView
android:id="#+id/dataFetch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="data"
android:textSize="20dp"
android:textAlignment="center"
android:background="#F5B6B6"
android:textColor="#000000"/>
this is my database
this is current output
You must add child value event listener to FireAlarm, the listener will be triggered as soon as it is attached to a DatabaseReference object (in your code the object is reference) by calling onChildAdded() method and triggered each time data in the child added, removed, changed,...
Your readData should be rewritten as below:
reference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
Data data = (Data) dataSnapshot.getValue(Data.class);
dataList.add(data);
//then notify data changed
recordAdapter.notifyDataSetChange();
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
Data data = (Data) dataSnapshot.getValue(Data.class);
//then find position of `data` in datalist to change some information of the item at position.
//then notify data changed
recordAdapter.notifyDataSetChange();
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
Data data = (Data) dataSnapshot.getValue(Data.class);
//then find data in datalist to remove, after removed, notify data set changed
recordAdapter.notifyDataSetChange();
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
You should use addChildEventListener instead of addValueEventListener, and write the code inside onChildAdded.
You can find the explanation from documentation:
When working with lists, your application should listen for child
events rather than the value events used for single objects.
Related
I am using recyclerview and firebase realtime database together to allow user to send messages to other users.When a message is sent I need to start that activity again to reload the updates.Is there anyway to do this automatically so that when the user clicks on send the message is displayed directly.
Action when send button is clicked
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String message = messageBox.getText().toString();
if (message.equals("")){
Toast.makeText(privatemessageactivity.this, "You cant send an empty message", Toast.LENGTH_SHORT).show();
}else{
messagedref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if (!snapshot.exists()){
messagedref.setValue(true);
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
Map<String,Object> mymessage = new HashMap<>();
mymessage.put("Image",myImage);
mymessage.put("Message",message);
sendmessageref.push().setValue(mymessage);
}
}
});
Action to retrieve the data
sendmessageref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if (snapshot.exists()){
for (DataSnapshot snapshot1 : snapshot.getChildren()){
MessageClass ld = snapshot1.getValue(MessageClass.class);
list.add(ld);
}
adapter = new MessageAdapter(list);
recyclerView.setAdapter(adapter);
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
My adapter class and my ViewHolder class
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.ViewHolder>{
private List<MessageClass> list;
public MessageAdapter(List<MessageClass> list) {
this.list = list;
}
#NonNull
#Override
public MessageAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.messagelayout,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MessageAdapter.ViewHolder holder, int position) {
MessageClass ld = list.get(position);
holder.message.setText(ld.getMessage());
if (!ld.getImage().equals("noimage")){
Glide.with(getApplicationContext()).load(ld.getImage()).into(holder.circleImageView);
}else{
Glide.with(getApplicationContext()).load(R.drawable.profile).into(holder.circleImageView);
}
}
#Override
public int getItemCount() {
return list.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView message;
CircleImageView circleImageView;
public ViewHolder(#NonNull View itemView) {
super(itemView);
message = itemView.findViewById(R.id.messagesmessage);
circleImageView = itemView.findViewById(R.id.messagespp);
}
}
}
The whole purpose of using Firebase is that you don't have to explicitly reload the messages. If you use addValueEventListener instead of addListenerForSingleValueEvent, Firebase will keep listening for changes on the server, and will call your onDataChange again if there as any changes.
So a common way to keep your message list up to date is:
adapter = new MessageAdapter(list);
recyclerView.setAdapter(adapter);
sendmessageref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
list.clear();
for (DataSnapshot snapshot1 : snapshot.getChildren()){
MessageClass ld = snapshot1.getValue(MessageClass.class);
list.add(ld);
}
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
throw error.toException();
}
});
The changes:
Uses addValueEventListener instead of addListenerForSingleValueEvent so that it gets called both for the initial data and when there are changes.
Create and assign the adapter outside of onDataChange, since you'll want to set the adapter only once, and then update its data.
Clear the list in onDataChange, since we'll now get called multiple times, and each time we get a full DataSnapshot of all the relevant data.
Notify the adapter of the new data, with adapter.notifyDataSetChanged() so that it can refresh the UI,.
Implement onCancelled, because it's a bad practice to ignore possible errors.
I have a fragment with a search bar (edit text) and a recycler view underneath. To display my recycler view I use an adapter. I am trying to display all the friends of a user. The problem is the recycler view items don't appear unless I tap in the search bar (without typing anything).
Furthermore, when loading the images from my database into the image views they get mixed up.
My database is as follows:
{
"Friends": {
"id3":{
"id1": "name1",
"id2": "name2",
...
},
"id4":{
"id1": "name1",
"id3": "name3",
...
}, ...
},
"Users": {
"id1":{
info about user1
},
"id2":{
info about user2
},
"id3":{
info about user3
},
"id4":{
info about user4
}, ...
}
}
I debugged a little bit and found my onCreateViewHolder never gets called when the fragment gets created (not sure if this is relevant).
I also tried to use another query which simply displays all users in the database (so just iterating through the children of the node "Users"). That did work so I imagine the problem is with my queries. However I cannot figure out where I am going wrong.
How I link my recycler view to my adapter:
recyclerView = view.findViewById(R.id.frag_invite_recycler);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
searchBar = view.findViewById(R.id.frag_invite_search_bar);
mUsers = new ArrayList<>();
mAdapter = new EventUserAdapter(getContext(),mUsers);
recyclerView.setAdapter(mAdapter);
My friends query:
Query friends = FirebaseDatabase.getInstance().getReference("Friends").child(firebaseUser.getUid()).orderByValue();
friends.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
mUsers.clear();
for (DataSnapshot snp : dataSnapshot.getChildren()) {
Query users = FirebaseDatabase.getInstance().getReference("Users").child(snp.getKey());
users.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
assert user != null;
mUsers.add(user);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
mAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
My adapter:
#NonNull
#Override
public EventViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(mContext).inflate(R.layout.user_event, viewGroup, false);
return new EventViewHolder(view, mListener);
}
#Override
public void onBindViewHolder(#NonNull EventViewHolder eventViewHolder, int i) {
firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
final User user = mUsers.get(i);
eventViewHolder.username.setText(user.getUsername());
if (user.getImgURL() != null) Glide.with(eventViewHolder.icon.getContext()).load(user.getImgURL()).into(eventViewHolder.icon);
}
#Override
public int getItemCount() {
return mUsers.size();
}
My ViewHolder:
public class EventViewHolder extends RecyclerView.ViewHolder {
public TextView username;
public ImageView check;
public CircleImageView icon;
public EventViewHolder(#NonNull View itemView, final onItemClickListener listner) {
super(itemView);
username = itemView.findViewById(R.id.user_event_name);
icon = itemView.findViewById(R.id.user_event_image);
check = itemView.findViewById(R.id.user_event_check);
}
}
I think the problem is with the location of mAdapter.notifyDataSetChanged(); in My friends query section.
You are calling the command outside of the inner callback from users.addValueEventListener and this might be too early, your users array just been cleared and hasn't been populated yet.
Try to do something like:
friends.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
mUsers.clear();
mAdapter.notifyDataSetChanged(); // for the current views actually reset
// you could also do mAdpater.notifyItemRangeRemoved(0, mUsers.size()) but before clearing the list of course
for (DataSnapshot snp : dataSnapshot.getChildren()) {
Query users = FirebaseDatabase.getInstance().getReference("Users").child(snp.getKey());
users.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
assert user != null;
mUsers.add(user);
mAdapter.notifyItemInserted(mUsers.size() - 1); // to display the current user in the recyclerview
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
If still not working, maybe you're using two different lists of users, one in activity and second in the adapter? check if you are saving the original list's reference or generating new one based on the original's values (at the adpater's constructor). Then, updating the activity's list won't affect the adapter's list.
I hope this will solve your problem!
I have a firebase strucuture as
The recycler view is only displaying the last added or say latest added node to the database instead of displaying each and every node
the database reference i am using is
r2 = FirebaseDatabase.getInstance().getReference().child("Uploads").child("Wheat").child(S).child(D).child(T).child(FirebaseAuth.getInstance().getUid());
and the code for recycler view is as
public class MyPost extends AppCompatActivity {
private RecyclerView recyclerView;
private DatabaseReference r, r2, r3;
private String S, D, T;
FirebaseRecyclerAdapter<RecyclerCropView, ViewHolder2> Fbra1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_post);
r = FirebaseDatabase.getInstance().getReference().child("User_Data").child(MainDashboard.type).child(FirebaseAuth.getInstance().getUid());
recyclerView = (RecyclerView) findViewById(R.id.R_view2);
r.addListenerForSingleValueEvent(rr);
}
private ValueEventListener rr = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
S = dataSnapshot.child("State").getValue().toString();
D = dataSnapshot.child("City").getValue().toString();
T = dataSnapshot.child("Tehsil").getValue().toString();
// Toast.makeText(MyPost.this,S+D+T.toString(),Toast.LENGTH_LONG).show();
r2 = FirebaseDatabase.getInstance().getReference().child("Uploads").child("Wheat").child(S).child(D).child(T).child(FirebaseAuth.getInstance().getUid());
// Toast.makeText(MyPost.this,r2.toString(),Toast.LENGTH_LONG).show();
r2.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
r3=r2.child(dataSnapshot.getKey());
Toast.makeText(MyPost.this, r3.toString(), Toast.LENGTH_LONG).show();
FirebaseRecyclerOptions<RecyclerCropView> options = new FirebaseRecyclerOptions.Builder<RecyclerCropView>().setQuery(r3, RecyclerCropView.class).build();
Fbra1 = new FirebaseRecyclerAdapter<RecyclerCropView, ViewHolder2>(options) {
#Override
protected void onBindViewHolder(#NonNull ViewHolder2 holder, int position, #NonNull RecyclerCropView model) {
holder.setProductImage(model.getProduct_Image());
holder.setProduct(model.getProduct());
holder.setMax(model.getMaximumPrice());
holder.setQuantityUnit(model.getQuantityUnit());
holder.setQuantity(model.getQuantity());
}
#NonNull
#Override
public ViewHolder2 onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_post, parent, false);
return new ViewHolder2(v);
}
};
Fbra1.notifyDataSetChanged();
recyclerView.hasFixedSize();
recyclerView.setLayoutManager(new LinearLayoutManager(MyPost.this));
Fbra1.startListening();
recyclerView.setAdapter(Fbra1);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
#Override
protected void onRestart() {
super.onRestart();
finish();
}
}
Recycler view is only showing very last item that was added to database.
Note:-
The item.xml file has width set to wrap content.
if i remove the childEventListener the recyclerView shows multiple items with
null on the place of TextView(These are those textView on which data retrived was supposed to be set)
Here is the problem: r2.addChildEventListener
Classes implementing this interface can be used to receive events about changes in the child locations of a given DatabaseReference ref. Attach the listener to a location using addChildEventListener(ChildEventListener) and the appropriate method will be triggered when changes occur.
Firebase Docs
What you should use is a r2.addListenerForSingleValueEvent
This returns the children of the specific node. But now since you are using FirebaseUI library, you will not need to listen as it is a listener itself.
So your code should listen to the root of the posts. This r3=r2.child(dataSnapshot.getKey()); is unnecessary because it only gets one entry
Change to this
FirebaseRecyclerOptions<RecyclerCropView> options = new FirebaseRecyclerOptions.Builder<RecyclerCropView>().setQuery(r2, RecyclerCropView.class).build();
Fbra1 = new FirebaseRecyclerAdapter<RecyclerCropView, ViewHolder2>(options) {
#Override
protected void onBindViewHolder(#NonNull ViewHolder2 holder, int position, #NonNull RecyclerCropView model) {
holder.setProductImage(model.getProduct_Image());
holder.setProduct(model.getProduct());
holder.setMax(model.getMaximumPrice());
holder.setQuantityUnit(model.getQuantityUnit());
holder.setQuantity(model.getQuantity());
}
#NonNull
#Override
public ViewHolder2 onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_post, parent, false);
return new ViewHolder2(v);
}
};
Fbra1.notifyDataSetChanged();
recyclerView.hasFixedSize();
recyclerView.setLayoutManager(new LinearLayoutManager(MyPost.this));
Fbra1.startListening();
recyclerView.setAdapter(Fbra1);
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (final DataSnapshot post : dataSnapshot.getChildren()) {
FirebaseRecyclerAdapter<GetModel, RecyclerViewAdapter.ViewHolder> mAdapter = new FirebaseRecyclerAdapter<GetModel, RecyclerViewAdapter.ViewHolder>
(GetModel.class, R.layout.rview, RecyclerViewAdapter.ViewHolder.class, query) {
#Override
protected void populateViewHolder(final RecyclerViewAdapter.ViewHolder viewHolder, final GetModel model, final int position) {
viewHolder.textview.setText(model.getName());
}
};
recyclerView.setAdapter(mAdapter);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
This code set
viewHolder.textview.setText(model.getName());
with the last child's name only instead of each child's name.
How to fix this?
I have two arraylist and I want to display the data in both list in the same recyclerview. My code looks like this.
My adapter
class NotificationAdapter extends RecyclerView.Adapter<NotificationViewHolder> {
//..
List<Notification> notifList = new ArrayList<>();
List<AcceptedInvitation> acceptedInvitations = new ArrayList<>();
Context context;
LayoutInflater inflater;
public NotificationAdapter(Context context){
this.context = context;
inflater = LayoutInflater.from(context);
}
public void addNotification(Notification notification){
notifList.add(notification);
notifyDataSetChanged();
notifyItemInserted(notifList.size());
}
public void addAcceptedInvitation(AcceptedInvitation invitation){
acceptedInvitations.add(invitation);
notifyDataSetChanged();
notifyItemInserted(acceptedInvitations.size());
}
#Override
public NotificationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.rv_notifications, parent, false);
NotificationViewHolder holder = new NotificationViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(final NotificationViewHolder holder, int position) {
String user1= null;
String user2= null;
if(position < notifList.size()){
user1= notifList.get(position).getUserName();
}else {
user2= acceptedInvitations.get(position).getUserName();
}
#Override
public int getItemCount() {
return notifList.size() + acceptedInvitations.size();
}
}
RecyclerView ViewHolder
public class NotificationViewHolder extends RecyclerView.ViewHolder {
//..
TextView userName;
public NotificationViewHolder(View itemView) {
super(itemView);
userName = (TextView) itemView.findViewById(R.id.display_name);
}
}
I have an error in onBindViewHolder() part. I'm using Firebase in retrieving the data by the way.
Main Activity(loadData() is called when the activity starts)
public void loadData(){
//..
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference().child("users").child(key).child("invitations");
ref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Notification notification = dataSnapshot.getValue(Notification.class);
adapter.addNotification(notification);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
DatabaseReference invitationRef = database.getReference().child("users").child(key).child("acceptedInvitations");
invitationRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
AcceptedInvitation acceptedInvitation = dataSnapshot.getValue(AcceptedInvitation.class);
Toast.makeText(getActivity(), dataSnapshot.getValue().toString(), Toast.LENGTH_LONG).show();
adapter.addAcceptedInvitation(acceptedInvitation);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I can already retrieve the data from invitations and acceptedInvitations json-array, but i have an error when I tried to populate the data from the acceptedInvitations, basically when I call "addAcceptedInvitation(acceptedInvitation)".
Error.
threadid=1: thread exiting with uncaught exception (group=0x41a81c80)
I assume you want to combine the Notification data and AcceptedInvitation data. Please note that when you call two Firebase Database call, the second one will be executed after the first one is finished. So it is okay to use this code below (because if not, this code is risky).
First, remove notifyItemInserted() inside addNotification and addAcceptedInvitation as it is no use if it's called directly after notifyDataSetChanged(). But if you prefer keeping notifyItemInserted() and removing notifyDataSetChanged(), it will be more complicated.
Then lets point out what went wrong. When you call notifyDataSetChanged(), your adapter will refresh all of its item. In your getItemCount(), you mention that your adapter have (notifList.size() + acceptedInvitations.size()). So if you have 3 notifList and 2 acceptedInvitations, your adapter will refresh from its first value to (3+2) fifth value.
Knowing that, the onBindViewHolder will be called with position value from 0 to 4 (0 is first, 4 is fifth). Look at the code:
#Override
public void onBindViewHolder(final NotificationViewHolder holder, int position) {
...
if(position < notifList.size()){
user1 = notifList.get(position).getUserName();
} else {
user2 = acceptedInvitations.get(position).getUserName();
}
}
Notice that when position is 0 or 1 or 2, it is fine because its executing notifList.get(position) and notifList on those index is exist. But when position is 3, its executing acceptedInvitations.get(position) that will triger error because acceptedInvitations only have value on index 0 and 1 not in 3
After understanding that, your code should look like this:
#Override
public void onBindViewHolder(final NotificationViewHolder holder, int position) {
...
if(position < notifList.size()){
user1 = notifList.get(position).getUserName();
} else {
user2 = acceptedInvitations.get(position - notifList.size()).getUserName();
}
}
Hope this help :)