I am new to firebase and I am in love with this real-time database thing.
However, I am not sure how to display nested data in a recycled view.
Following is my database structure :-
I want to display the data at 0,1,2...The data consist of information about different books.
This is what I have tried so far:-
private void getSelectedBooks() {
FirebaseRecyclerAdapter<BooksInfo, BooksViewHolder> adapter = new FirebaseRecyclerAdapter<BooksInfo, BooksViewHolder>(
BooksInfo.class,
R.layout.book_list,
BooksViewHolder.class,
nextOrder.child(String.valueOf(index))
) {
#Override
protected void populateViewHolder(BooksViewHolder viewHolder, final BooksInfo model, final int position) {
Log.d("BOOKDETAILS",model.getTitle());
// viewHolder.setTitle(model.getTitle());
// viewHolder.setISBN(model.getISBN());
// viewHolder.setDiscount(model.get());
// viewHolder.setQty(model.getQuantity());
// viewHolder.setPrice(model.getPrice());
// viewHolder.setGrossValue(model.get);
// viewHolder.setNetValue(model.get);
viewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
DatabaseReference ref = getRef(position);
String key = ref.getKey();
Toast.makeText(AddOrderActivity.this, key, Toast.LENGTH_SHORT).show();
// getRetailerInfo(key);
}
});
}
};
selectedBookRecyclerView.setAdapter(adapter);
mDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.child(String.valueOf(index)).exists()) {
// run some code
// DisableProgress();
} else {
// DisableProgress();
// Toast.makeText(RetailerActivity.this, "No retailers to display", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Here DatabaseReference nextOrder = mDatabase.child(userId).child("order_details").child(String.valueOf(index));
Any help or suggestion is appreciated.Thank you.
Related
I've just recently started to learn some coding, a little python and java.
I'm trying to make a whatsapp clone as a test, and I've hit a wall.
Basically I have a UID showing as the name of a group chat, and I want to swap it for a groupId I have saved in firebase at the same level as the Uid.
FirebaseDatabase.getInstance().getReference().child("chat").child(chatId).child("info").child("groupId)
I've been following simcoder youtube video and there's a lot of checks and balances happening between user and chat UIDs and I'm getting lost in the middle of all that.
I've tried adding groupId to the adapter and the chatObject, and I can see the data in debug, but I just can't get it to populate the recycler view. I've had a search of the forums here, but can't quite get it to work.
I'm hoping someone can help me out, many thanks
private RecyclerView mChatList;
private RecyclerView.Adapter mChatListAdapter;
private RecyclerView.LayoutManager mChatListLayoutManager;
ArrayList<ChatObject> chatList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_group_chat);
OneSignal.startInit(this).init();
OneSignal.setSubscription(true);
OneSignal.idsAvailable(new OneSignal.IdsAvailableHandler() {
#Override
public void idsAvailable(String userId, String registrationId) {
FirebaseDatabase.getInstance().getReference().child("user").child(FirebaseAuth.getInstance().getUid()).child("notificationKey").setValue(userId);
}
});
OneSignal.setInFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification);
Button mLogout = findViewById(R.id.logout);
Button mFindUser = findViewById(R.id.finduser);
mFindUser.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(), FindUserActivity.class));
}
});
mLogout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
OneSignal.setSubscription(false);
FirebaseAuth.getInstance().signOut();
Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
return;
}
});
getPermissions();
initializeRecyclerView();
getUserChatList();
}
private void getUserChatList(){
DatabaseReference mUserChatDB = FirebaseDatabase.getInstance().getReference().child("user").child(FirebaseAuth.getInstance().getUid()).child("chat");
mUserChatDB.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
for (DataSnapshot childSnapshot : dataSnapshot.getChildren()){
ChatObject mChat = new ChatObject(childSnapshot.getKey());
boolean exists = false;
for (ChatObject mChatIterator : chatList){
if(mChatIterator.getChatId().equals(mChat.getChatId()))
exists = true;
}
if (exists)
continue;
chatList.add(mChat);
getChatData(mChat.getChatId());
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void getChatData(String chatId) {
DatabaseReference mChatDB = FirebaseDatabase.getInstance().getReference().child("chat").child(chatId).child("info");
mChatDB.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()){
String chatId = "";
if(dataSnapshot.child("id").getValue() != null)
chatId = dataSnapshot.child("id").getValue().toString();
for(DataSnapshot userSnapshot : dataSnapshot.child("users").getChildren()){
for(ChatObject mChat : chatList)
if(mChat.getChatId().equals(chatId)){
UserObject mUser = new UserObject(userSnapshot.getKey());
mChat.addUserToArrayList(mUser);
getUserData(mUser);
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void getUserData(UserObject mUser) {
DatabaseReference mUserDb = FirebaseDatabase.getInstance().getReference().child("user").child(mUser.getUid());
mUserDb.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
UserObject mUser = new UserObject(dataSnapshot.getKey());
if(dataSnapshot.child("notificationKey").getValue() != null)
mUser.setNotificationKey(dataSnapshot.child("notificationKey").getValue().toString());
for(ChatObject mChat : chatList)
for(UserObject mUserIt : mChat.getUserObjectArrayList()){
if(mUserIt.getUid().equals(mUser.getUid())){
mUserIt.setNotificationKey(mUser.getNotificationKey());
}
}
mChatListAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void initializeRecyclerView() {
chatList = new ArrayList<>();
mChatList = findViewById(R.id.chatList);
mChatList.setNestedScrollingEnabled(false);
mChatList.setHasFixedSize(false);
mChatListLayoutManager = new LinearLayoutManager(getApplicationContext(), RecyclerView.VERTICAL,false);
mChatList.setLayoutManager(mChatListLayoutManager);
mChatListAdapter = new ChatListAdapter(chatList);
mChatList.setAdapter(mChatListAdapter);
}
From what I see you are passing to the adapter an empty list so that's why there's not data to be displayed.
And also, where you are using mChatListAdapter.notifyDataSetChanged();, you don't set before a populated list to the adapter.
I'm not sure which is the data you want to display but you have to collect in a list from the firebase all the information you want to display, for example, if UserObject is what you want, you have to make an ArrayList<UserObject>, to add there all the objects, pass it to the adapter and then call adapter.notifyDataSetChanged() .
You can make function in the adapter, like :
public void setData(ArrayList<YourObject> list){
yourAdapterList = list
this.notifyDataSetChanged()
}
And then call it from your activity/fragment like adapter.setData( /* your populated list */)
My code is as below... I am new to this line so any help is appreciated.
#Override
public void onStart() {
super.onStart();
FirebaseRecyclerOptions<Contacts> options = new FirebaseRecyclerOptions.Builder<Contacts>().setQuery(ChatsRef, Contacts.class).build();
FirebaseRecyclerAdapter<Contacts, ChatsViewHolder> adapter = new FirebaseRecyclerAdapter<Contacts, ChatsViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull final ChatsViewHolder holder, int position, #NonNull Contacts model) {
final String usersIDs = getRef(position).getKey();
final String[] retImage = {"default_image"};
UsersRef.child(usersIDs).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
if (dataSnapshot.hasChild("image")) {
retImage[0] = dataSnapshot.child("image").getValue().toString();
Picasso.get().load(retImage[0]).into(holder.profileImage);
}
final String retName = dataSnapshot.child("name").getValue().toString();
holder.userName.setText(retName);
if (dataSnapshot.child("userState").hasChild("state")) {
String state = dataSnapshot.child("userState").child("state").getValue().toString();
if (state.equals("online")) {
holder.onlineIcon.setVisibility(View.VISIBLE);
}
else if (state.equals("offline")) {
holder.onlineIcon.setVisibility(View.INVISIBLE);
}
}
else {
holder.onlineIcon.setVisibility(View.INVISIBLE);
}
// i am having trouble from this portion onwards
final String[] retLastMessage = {null};
final String[] retLastMessageTime = {null};
final String[] retLastMessageDate = {null};
RootRef.child("Contacts").child(currentUserID).child(usersIDs).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild("LastMessage")) {
retLastMessage[0] = dataSnapshot.child("LastMessage").getValue().toString();
retLastMessageTime[0] = dataSnapshot.child("LastMessageTime").getValue().toString();
retLastMessageDate[0] = dataSnapshot.child("LastMessageDate").getValue().toString();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
holder.userLastMessage.setText(retLastMessage[0]);
String retLastMessageTimeDate = retLastMessageTime[0] + " " + retLastMessageDate[0];
holder.userLastMsgTime.setVisibility(View.VISIBLE);
holder.userLastMsgTime.setText(retLastMessageTimeDate);
//upto this I guess
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent chatIntent = new Intent(getContext(), ChatActivity.class);
chatIntent.putExtra("visit_user_id", usersIDs);
chatIntent.putExtra("visit_user_name", retName);
chatIntent.putExtra("visit_image", retImage[0]);
startActivity(chatIntent);
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#NonNull
#Override
public ChatsViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.users_display_layout, viewGroup, false);
return new ChatsViewHolder(view);
}
};
chatsList.setAdapter(adapter);
adapter.startListening();
}
app is crashing when it tries to retrieve these data from firebase
please help. I am using the above code to actually get the final message and time of last message to display it in chats fragment of my app. the last message and last message time are saved in contacts node
this is how the database entry looks like
So you need to do that :
DatabaseReference reference =
FirebaseDatabase.getInstance().getReference("Contacts").child("2").child("3");
// of course you are not going to use 2 and 3 as a child but it's just to illustrate my point
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
after changing the code a little the app ran smoothly
the changed code is:
final String[] retLastMessage = {null};
final String[] retLastMessageTime = {null};
final String[] retLastMessageDate = {null};
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Contacts").child(currentUserID).child(usersIDs);
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild("LastMessage")) {
retLastMessage[0] = dataSnapshot.child("LastMessage").getValue().toString();
retLastMessageTime[0] = dataSnapshot.child("LastMessageTime").getValue().toString();
retLastMessageDate[0] = dataSnapshot.child("LastMessageDate").getValue().toString();
holder.userLastMessage.setVisibility(View.VISIBLE);
holder.userLastMessage.setText(retLastMessage[0]);
String retLastMessageTimeDate = retLastMessageTime[0] + " " + retLastMessageDate[0];
holder.userLastMsgTime.setVisibility(View.VISIBLE);
holder.userLastMsgTime.setText(retLastMessageTimeDate);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
rest of the code remains same. the problem was i was assigning values after the ValueEventListener. i don't know the proper reason but aster assigning the values inside the ValueEventLister the code seems to run fine.
I have a problem with Recyclerview item click. I fetch data to list() method and add via addItem() method in recyclerview custom adapter when scroll down in addOnScrollListener. I get item position with click interface on Fragment. First fetch data work perfectly but when fetch loadmore, can't retrive item positon to new data with onButtonLClick() method.
// in onBindViewHolder;
holder.lnl.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
rcylviewItemNotify.onButtonLClick(position);
}catch (Throwable e){
//interface can be null
}
}
});
// addItem() method in adapter;
public void addItem(List<Image> img) {
for (Image im : img) {
arrayList.add(im);
}
notifyDataSetChanged();
}
// interface code;
public interface RcylviewItemNotify {
void onButtonLClick(int position);
}
// in Fragment code;
public void list() {
GetServices service = RetrofitInstance.getRetrofitInstance().create(GetServices.class);
Call<Images> call = service.getImages();
call.enqueue(new Callback<Images>() {
#Override
public void onResponse(Call<Images> call, Response<Images> response) {
Images body = response.body();
records = body.getImages();
adapter.addItem(records);
}
#Override
public void onFailure(Call<Images> call, Throwable t) {
Toast.makeText(getActivity(), "Network hatası onFailure", Toast.LENGTH_SHORT).show();
reflesh.setRefreshing(false);
}
});
}
#Override
public void onButtonLClick(int position) {
final String clickId = String.valueOf(records.get(position).getID());
Toast.makeText(getActivity(), "ID: " + clickId, Toast.LENGTH_SHORT).show();
}
// recycler settings;
public void loadView() {
layoutManager = new GridLayoutManager(getActivity(), 2);
recyclerView.setLayoutManager(layoutManager);
Collections.reverse(records);
adapter = new RecyclerViewAdapter(this,(ArrayList<Image>) records, getActivity());
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
reflesh.setRefreshing(false);
}
I'm not sure if this is your issue but you should be using the ViewHolder to get the position. Inside of your onBindViewHolder:
#Override
public void onClick(View view){
int itemPosition = holder.getAdapterPosition();
// Then do whatever you need to with the position
}
I am using Firebase Recycler Adapter in my app to display items from my database, but I want a situation whereby newer items will be at the top of the fragment so i tried to use orderByChild() like this:
Query conversationQuery = mConvDatabase.orderByChild("timestamp");
but that didn't solve the problem.
What i really want to do is to update chat fragment anytime a user receives a new message to move the new conversation to the top just like every other messenger app.
this is my ChatFragment
#Override
public void onStart() {
super.onStart();
Query conversationQuery = mConvDatabase.orderByChild("timestamp");
FirebaseRecyclerAdapter<Conv, ConvViewHolder> firebaseConvAdapter = new FirebaseRecyclerAdapter<Conv, ConvViewHolder>(
Conv.class,
R.layout.users_layout,
ConvViewHolder.class,
conversationQuery
) {
#Override
protected void populateViewHolder(final ConvViewHolder convViewHolder, final Conv conv, int i) {
final String list_user_id = getRef(i).getKey();
// Query MessageQuery = mMessageDatabase.child(list_user_id);
Query lastMessageQuery = mMessageDatabase.child(list_user_id).limitToLast(1);
lastMessageQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String data = dataSnapshot.child("message").getValue().toString();
String type_image = dataSnapshot.child("type").getValue().toString();
boolean seen = Boolean.parseBoolean(dataSnapshot.getKey());
if (type_image.equals("image")){
convViewHolder.setMessage("image",conv.isSeen());
}else {
convViewHolder.setMessage(data, conv.isSeen());
}}
#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) {
}
});
mUsersDatabase.child(list_user_id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final String userName = dataSnapshot.child("name").getValue().toString();
String userThumb = dataSnapshot.child("thumb_image").getValue().toString();
if(dataSnapshot.hasChild("online")) {
String userOnline = dataSnapshot.child("online").getValue().toString();
convViewHolder.setUserOnline(userOnline, getActivity());
}
mConvList.scrollToPosition(0);
convViewHolder.setName(userName);
convViewHolder.setUserImage(userThumb, getContext());
convViewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent chatIntent = new Intent(getContext(), ChatActivity.class);
chatIntent.putExtra("user_id", list_user_id);
chatIntent.putExtra("user_name", userName);
startActivity(chatIntent);
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
mConvList.setAdapter(firebaseConvAdapter);
firebaseConvAdapter.notifyDataSetChanged();
}
Is there a way i can manipulate the time stamp sent to database so it can be later retrieved in an appropriate manner? or can i just change the arrangement locally in my code as data is been loaded?
You can do something like this.
//Display
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
linearLayoutManager.setStackFromEnd(true);
linearLayoutManager.setReverseLayout(true);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(firebaseRecyclerAdapter);
firebaseRecyclerAdapter.startListening();
firebaseRecyclerAdapter.notifyDataSetChanged();
My Firebase database is like that -
users -
first user ID
- name - "abc"
- image - "url"
- one_word - "abc"
following -
first user ID -
second User ID - "0"
Following node shows that First user is following second user.
Here is my code -
#Override
protected void onStart() {
super.onStart();
imageView.setVisibility(View.GONE);
FirebaseRecyclerAdapter<followers_following_class,following_Adapter>firebaseRecyclerAdapter =
new FirebaseRecyclerAdapter<followers_following_class, following_Adapter>
(
followers_following_class.class,
R.layout.find_friend_card,
following_Adapter.class,
databaseReference
) {
#Override
protected void populateViewHolder(final following_Adapter viewHolder, final followers_following_class model, int position) {
final String user_id = getRef(position).getKey();
users.child(user_id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final String name = dataSnapshot.child("name").getValue().toString();
final String image = dataSnapshot.child("image").getValue().toString();
final String line = dataSnapshot.child("line").getValue().toString();
final String wins = dataSnapshot.child("one_word").getValue().toString();
viewHolder.setName(name);
viewHolder.setImage(following.this,image);
viewHolder.setLine(line);
viewHolder.setOne_word(wins);
if(getItemCount() == 0){
imageView.setVisibility(View.VISIBLE);
}
viewHolder.vieww.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!user_id.equals(my_id)){
Intent intent = new Intent(following.this,Friend_profile_view.class);
intent.putExtra("user_id",user_id);
intent.putExtra("image",image);
intent.putExtra("one_word",wins);
intent.putExtra("name",name);
startActivity(intent);
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
list.setAdapter(firebaseRecyclerAdapter);
}
public static class following_Adapter extends RecyclerView.ViewHolder {
View vieww;
public following_Adapter(View itemView) {
super(itemView);
this.vieww = itemView;
}
public void setImage( final following following, final String image) {
final CircleImageView circleImageView = (CircleImageView)vieww.findViewById(R.id.find_friend_profile_image_card);
if(!image.equals("default_image")) {
Picasso.with(following).load(image).networkPolicy(NetworkPolicy.OFFLINE).into(circleImageView, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
Picasso.with(following).load(image).into(circleImageView);
}
});
}
}
public void setName(String name) {
TextView textView = (TextView)vieww.findViewById(R.id.find_friends_name_card);
textView.setText(name);
}
public void setLine(String line) {
ImageView imageView = (ImageView)vieww.findViewById(R.id.online_or_not);
if(line.equals("offline")){
imageView.setVisibility(View.INVISIBLE);
}
}
public void setOne_word(String wins) {
TextView textView = (TextView)vieww.findViewById(R.id.user_level);
textView.setText(wins);
}
}
Is there any way where i can apply firebase recycler adapter for one node but retrieve data form another node with same key without using addValueEventListener ?
And also most of my app uses firebase recyclerview in all activities so when i observed my android profiler , my RAM usage is increasing while switching between activities i have also used finish(); ended the addValuelistener in onDistroy method but it is still not working.
There are 3 eventListeners that you can use according to your needs, namely valueEventListener, childEventListener and singleValueEventListener.
This will be a good read for this, Firebase Docs.
When working with lists, your application should listen for child events rather than the value events used for single objects.
Child events are triggered in response to specific operations that happen to the children of a node from an operation such as a new child added through the push() method or a child being updated through the updateChildren() method. Each of these together can be useful for listening to changes to a specific node in a database.
In code, childEventListener looks like this:
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
// A new comment has been added, add it to the displayed list
Comment comment = dataSnapshot.getValue(Comment.class);
// ...
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
// A comment has changed, use the key to determine if we are displaying this
// comment and if so displayed the changed comment.
Comment newComment = dataSnapshot.getValue(Comment.class);
String commentKey = dataSnapshot.getKey();
// ...
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
// A comment has changed, use the key to determine if we are displaying this
// comment and if so remove it.
String commentKey = dataSnapshot.getKey();
// ...
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
// A comment has changed position, use the key to determine if we are
// displaying this comment and if so move it.
Comment movedComment = dataSnapshot.getValue(Comment.class);
String commentKey = dataSnapshot.getKey();
// ...
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "postComments:onCancelled", databaseError.toException());
Toast.makeText(mContext, "Failed to load comments.",
Toast.LENGTH_SHORT).show();
}
};
ref.addChildEventListener(childEventListener);
Also, retrieving data without the use of eventListeners is not possible. And if you want to listen to children of your one node, simultaneously, then childEventListener will be a great tool.