Android Firebase Recycler view UI inside onDataChange() - android

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?

Related

How to update recyclerview from firebase realtime database for a chat app

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.

Retrieve specific child from Firebase Realtime database into the RecyclerView - Android

I'm quite new to firebase and android world. I want to retrieve only specific groups in which the current logged in user has joined.
Here is my Firebase realtime database structure
However, all the users who logged in are displayed with all the groups exist in the database:
My app screenshot
I tried to filter the output inside the onBindViewHolder method of FirebaseRecylerAdapter but it dont seem to be working :
protected void onBindViewHolder(#NonNull final GroupChatViewHolder holder, final int position, #NonNull Group model) {
final String groups = getRef(position).getKey();
final String retGroupName = groups.toString();
RootRef.child("Groups").child(retGroupName).child("Users").child(currentUser.getUid())
.addValueEventListener(new ValueEventListener() {
#Override
// If current user exist in that group
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
holder.txtGroupName.setText(retGroupName);
// Retrieve groupCreatedBy
RootRef.child("Groups").child(retGroupName).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String retGroupCreatedBy = dataSnapshot.child("groupCreatedBy").getValue().toString();
// Find username of the group creator
RootRef.child("Users").child(retGroupCreatedBy).child("profilename")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
holder.txtGroupCreatedBy.setText("Group created by: " + dataSnapshot.getValue().toString());
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Here are my full codes. I implement all the process including the onBindViewHolder in a fragment class as shown below:
public class GroupChatFragment extends Fragment {
private View view;
private RecyclerView chatList;
FirebaseAuth mAuth = FirebaseAuth.getInstance();
FirebaseUser currentUser = mAuth.getCurrentUser();
DatabaseReference RootRef = FirebaseDatabase.getInstance().getReference();
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_group_chat, container, false);
chatList = (RecyclerView) view.findViewById(R.id.group_chat_list);
chatList.setLayoutManager(new LinearLayoutManager(getContext()));
Toolbar toolbar = view.findViewById(R.id.toolbar_chat);
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
return view;
}
#Override
public void onStart() {
super.onStart();
FirebaseRecyclerOptions<Group> options = new FirebaseRecyclerOptions.Builder<Group>()
.setQuery(RootRef.child("Groups"), Group.class)
.build();
FirebaseRecyclerAdapter<Group, GroupChatViewHolder> adapter =
new FirebaseRecyclerAdapter<Group, GroupChatViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull final GroupChatViewHolder holder, final int position, #NonNull Group model) {
...
}
};
chatList.setAdapter(adapter);
adapter.startListening();
}
public static class GroupChatViewHolder extends RecyclerView.ViewHolder {
TextView txtGroupName, txtGroupCreatedBy;
public GroupChatViewHolder(#NonNull View itemView) {
super(itemView);
txtGroupName = itemView.findViewById(R.id.group_name_display);
txtGroupCreatedBy = itemView.findViewById(R.id.group_createdby_display);
}
}
}

How do I make recyclerView that retrives data from firebase, using getKey()?

I would like to know how can I do a recyclerView that shows data that I got from this database reference:
Salas= new ArrayList<String>();
DatabaseReference referenceSalas = FirebaseDatabase.getInstance().getReference("salas/");
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("usuarios/");
FirebaseAuth autenticacao = FirebaseAuth.getInstance();
String emailUsu = autenticacao.getCurrentUser().getEmail();
reference.orderByChild("email").equalTo(emailUsu).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot datas : dataSnapshot.getChildren()) {
nomeProf = datas.child("nome").getValue().toString();
referenceSalas.orderByChild("nomeProf").equalTo(nomeProf).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot datas : dataSnapshot.getChildren()) {
Salas.add(datas.getKey());
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException();
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException();
}
});
I tried to do the RecyclerView like this:
public class salasFragment extends Fragment {
private RecyclerView mRecycleViewSalas;
private adapterSalas adapterSalas;
private ArrayList<salas> listaSalas= new ArrayList<>();
private LinearLayoutManager linearLayoutManager;
private TextView txtSalas, txtTeste;
private String nomeProf;
private String teste="", piru;
private DatabaseReference reference = FirebaseDatabase.getInstance().getReference("usuarios/");
private DatabaseReference referenceSalas = FirebaseDatabase.getInstance().getReference("salas/");
private ValueEventListener valueEventListenerSalas;
ArrayList<String> salasAula = new ArrayList<String>();
public salasFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_salas, container, false);
final Context context = view.getContext();
txtSalas= view.findViewById(R.id.txtSalas);
txtTeste= view.findViewById(R.id.txtTeste);
mRecycleViewSalas= view.findViewById(R.id.recyclerSalas);
adapterSalas= new adapterSalas(listaSalas, context);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(context);
mRecycleViewSalas.setLayoutManager(layoutManager);
mRecycleViewSalas.setHasFixedSize(true);
mRecycleViewSalas.setAdapter(adapterSalas);
return view; }
#Override
public void onStart() {
super.onStart();
recuperarSalas();
}
#Override
public void onStop() {
super.onStop();
reference.removeEventListener(valueEventListenerSalas);
}
public void recuperarSalas(){
FirebaseAuth autenticacao = FirebaseAuth.getInstance();
String emailUsu = autenticacao.getCurrentUser().getEmail();
valueEventListenerSalas = reference.orderByChild("email").equalTo(emailUsu).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot datas : dataSnapshot.getChildren()) {
nomeProf = datas.child("nome").getValue().toString();
referenceSalas.orderByChild("nomeProf").equalTo(nomeProf).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot datas : dataSnapshot.getChildren()) {
salas salas=datas.getKey(salas.class);
listaSalas.add(salas);
}
adapterSalas.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException();
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException();
}
});
}
}
my adapter:
public class adapterSalas extends RecyclerView.Adapter<adapterSalas.myViewHolder> {
private List<salas> Salas;
private Context context;
public adapterSalas(List<salas> listaSalas, Context c ) {
this.Salas= listaSalas;
this.context = c;
}
#NonNull
#Override
public myViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int i) {
View itemLista = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_salas, parent, false);
return new myViewHolder(itemLista);
}
#Override
public void onBindViewHolder(#NonNull myViewHolder holder, int position) {
salas sala = Salas.get(position);
holder.btn1.setText(sala.getPrimeiro());
}
#Override
public int getItemCount() {
return Salas.size();
}
public class myViewHolder extends RecyclerView.ViewHolder{
Button btn1;
public myViewHolder(#NonNull View itemView) {
super(itemView);
btn1 = itemView.findViewById(R.id.btn1);
}
}
}
Although this line "salas salas=datas.getKey(salas.class);" does not work properly when I use "getKey", It only works when "getValue" is used. There is no way of me doing this project with "getValue" instead of "getKey". So there is any way that can make this recyclerView works properly with "getKey" ?
Have you got the data from the firebase already?
If you got the data in an arraylist already, simply plug it in an adapter (you will need to create a RecyclerView adapter, a class to describe the value of each RecyclerView items) and set it to the RecyclerView and you are good to go!
You can use FirebasRecyclerAdapter as you recyclerView adapter. check out this link for a guide on how to use it.
https://medium.com/android-grid/how-to-use-firebaserecycleradpater-with-latest-firebase-dependencies-in-android-aff7a33adb8b

How to stop item trembling on recyclerview?

I have implemented a notification center using a
recyclerview displayed on a fragment , when I click on the item it writes a value on the database and then base of the condition the green Notification Bell appears/dissapears, the problem here is that all items get affected from trembling effect
Image:
https://im4.ezgif.com/tmp/ezgif-4-1adb4a5b29e3.gif
My adapter
public void onBindViewHolder(#NonNull final ViewHolder holder, final int position) {
NotifsRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.child(currentUserID).child("comments").child(delUid).hasChild("state"))
{
String state = dataSnapshot.child(currentUserID).child("comments").child(delUid).child("state").getValue().toString();
if (state.equals("unread"))
{
holder.notifImg.setVisibility(View.VISIBLE);
}
else {
holder.notifImg.setVisibility(View.GONE);
}}}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v)
{ NotifsRef.child(currentUserID).child("comments").child(delUid).child("state").setValue("read");
}
});
}
#Override
public int getItemCount() {
return mNotifications.size();
}
Fragment:
{
OnCreate{...}
notificationList = (RecyclerView) notificationFragmentView.findViewById(R.id.notifications_list);
notificationAdapter = new NotificationAdapter(getContext(), mNotifications);
notificationList.setAdapter(notificationAdapter);
notificationList.setHasFixedSize(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
linearLayoutManager.setReverseLayout(true);
linearLayoutManager.setStackFromEnd(true);
notificationList.setLayoutManager(linearLayoutManager);
}
private void readMsgList()
{
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Notifications");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{ mNotifications.clear();
for (DataSnapshot snapshot : dataSnapshot.child(currentUserID).child("comments").getChildren())
{
Notifications notifications = snapshot.getValue(Notifications.class);
mNotifications.add(notifications);
}
notificationAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}});
}
I have tried to change the listener from ValueEventListener to SingleValueEventListener but without any result.
Is there any way to correct this ?

Issue with FirebaseRecyclerAdapter, on a null object reference when delete data

The display from the Firebase database works fine, but when I try to delete an item from the database, the application crashes, although the deletion occurs. Writes on a null object reference in the string:
String postDescription = dataSnapshot.child("desc").getValue().toString();
Here is my code:
public class PostFragment extends Fragment {
private RecyclerView recyclerPost;
private DatabaseReference postReference;
private View view;
private LinearLayoutManager layoutManager;
public PostFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_post, container, false);
recyclerPost = view.findViewById(R.id.recycler_post);
postReference = FirebaseDatabase.getInstance().getReference().child("Posts");
postReference.keepSynced(true);
layoutManager = new LinearLayoutManager(getContext());
layoutManager.setReverseLayout(true);
layoutManager.setStackFromEnd(true);
recyclerPost.setHasFixedSize(true);
recyclerPost.setLayoutManager(layoutManager);
return view;
}
#Override
public void onStart() {
super.onStart();
Query query = postReference.orderByChild("timestamp");
FirebaseRecyclerOptions<Posts> options =
new FirebaseRecyclerOptions.Builder<Posts>()
.setQuery(query, Posts.class)
.build();
FirebaseRecyclerAdapter adapter = new FirebaseRecyclerAdapter<Posts, PostViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull final PostViewHolder holder, int position, #NonNull final Posts model) {
final String postId = getRef(position).getKey();
postReference.child(postId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String postDescription = dataSnapshot.child("desc").getValue().toString();
holder.postDesc.setText(postDescription);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
holder.delBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
postReference.child(postId).removeValue();
}
});
}
#NonNull
#Override
public PostViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int position) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.blog_item, viewGroup, false);
return new PostViewHolder(view);
}
};
adapter.startListening();
recyclerPost.setAdapter(adapter);
}
public class PostViewHolder extends RecyclerView.ViewHolder {
private TextView postDesc;
private Button delBtn;
private View view;
public PostViewHolder(#NonNull View itemView) {
super(itemView);
view = itemView;
postDesc = (TextView) view.findViewById(R.id.post_description);
delBtn = (Button) view.findViewById(R.id.del_post_btn);
}
}
}
Please tell me the solution to this problem.
You can try the following:
postReference.child(postId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()){
String postDescription = dataSnapshot.child("desc").getValue().toString();
holder.postDesc.setText(postDescription);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
You can use the method exists():
public boolean exists ()
Returns true if the snapshot contains a non-null value.
It will check if the datasnapshot is in the
database, if it is not in the database then you can add a snackbar or a toast.
More information here:
https://firebase.google.com/docs/reference/android/com/google/firebase/database/DataSnapshot#exists()
Every time a post is added to the adapter/list view, you now create a listener with addValueEventListener in your onBindViewHolder. This listener gets the value from the database, and then keeps listening for changes until you remove it. Since you never remove these listeners, over time they add up and things likely get out of sync.
The simplest solution is to use addListenerForSingleValueEvent:
#Override
protected void onBindViewHolder(#NonNull final PostViewHolder holder, int position, #NonNull final Posts model) {
final String postId = getRef(position).getKey();
postReference.child(postId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String postDescription = dataSnapshot.child("desc").getValue().toString();
holder.postDesc.setText(postDescription);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
When you use addListenerForSingleValueEvent, the listener is removed right after onDataChange fires for the first time.

Categories

Resources