I am trying to retrieve data from firebase database, when i try to retrieve data for the very first time after installing the app nothing is displayed in recycler view but when i press back and once again try to retrieve data,its displayed properly, suggest me changed which i should do in my code so that data is properly displayed in first attempt
Method Used to return ArrayList
public ArrayList<SaveAddInformation> retrieve(){
AdvertisementRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot eventSnapshot : dataSnapshot.getChildren()) {
SaveAddInformation mModel = eventSnapshot.getValue(SaveAddInformation.class);
Log.d("DATA" ,""+ mModel);
adinfolist.add(mModel);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return adinfolist;
}
Adpater Class
Context c;
ArrayList<SaveAddInformation> adinfolist;
public MyAdapter(Context c, ArrayList<SaveAddInformation> adinfolist) {
this.c = c;
this.adinfolist = adinfolist;
}
#Override
public MyViewholder onCreateViewHolder(ViewGroup parent, int viewType) {
View v= LayoutInflater.from(c).inflate(R.layout.design_row,parent,false);
MyViewholder myViewholder=new MyViewholder(v);
return myViewholder;
}
#Override
public void onBindViewHolder(MyViewholder holder, int position) {
SaveAddInformation save=adinfolist.get(position);
holder.titleTv.setText(save.getTitleS());
holder.rentamt.setText(save.getRent_amount());
holder.rentdays.setText(save.getRentDayS());
holder.desc.setText(save.getDescriptionS());
holder.setImage(getApplicationContext(), save.getImageuri());
// holder.titleTv.setText((CharSequence) adinfolist.get(position));
}
#Override
public int getItemCount() {
return adinfolist.size();
}
}
ViewHolder Class
public MyViewholder(View itemView) {
super(itemView);
titleTv=(TextView)itemView.findViewById(R.id.Titletextv);
rentamt=(TextView)itemView.findViewById(R.id.rent_amount);
rentdays=(TextView)itemView.findViewById(R.id.days_rent);
desc=(TextView)itemView.findViewById(R.id.descTV);
}
public void setImage(Context applicationContext, String imageuri) {
adimg=(ImageView)itemView.findViewById(R.id.ad_image);
// We Need TO pass Context
Picasso.with(applicationContext).load(imageuri).into(adimg);
}
If you are sure that the code has no errors, I would like to remind you that Firebase connections are async.
So the first time the function retrieve() returns the value of arrayList is actually empty.
What you can do is call adapter.notifyDataSetChanged() after the for loop in onDataChange() method.
Related
I am using DiffUtil async callback to update my list.
The problem is that it only displays the dataset once, at the first call. Later, when the dataset updates, the list only displays the items that were initially displayed. So the problem is that the dataset updates, but the list is stuck on its first state.(With the items that were initially created)
The dataset is being updated via a button click initiated by the user, this is how it looks:
private void updateUsersList() {
#Override
public void onResponse(JSONArray response) { // the JSON ARRAY response of user ids ["uid1", "uid334", "uid1123"]
for (int i = 0; i < response.length(); i++) {
try {
String userKey = response.get(i).toString(); // the currently iterated user id
final DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userKeyRef = rootRef.child("users").child(userKey); // reference to currently iterated user
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
myDataset.add(new User(dataSnapshot.getKey(), dataSnapshot.child("imageUrl").getValue().toString())); //add new user: id and image url
mAdapter.updateList(myDataset);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage());
}
};
userKeyRef.addListenerForSingleValueEvent(listener);
}
catch (JSONException e) { Log.d(TAG, "message " + e); }
}
}
And this is my adapter using async DiffUtil:
public class MyAdapter extends RecyclerView.Adapter {
private AsyncListDiffer<User> mAsyncListDiffer;
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView singleItemTextView;
public ImageView singleItemImage;
public View layout;
public ConstraintLayout constraintLayout;
public MyViewHolder(View v) {
super(v);
layout = v;
singleItemImage = (ImageView) v.findViewById(R.id.icon);
singleItemTextView = (TextView) v.findViewById(R.id.singleitemtv);
constraintLayout = (ConstraintLayout) v.findViewById(R.id.nbConstraintLayout);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter() {
DiffUtil.ItemCallback<User> diffUtilCallback = new DiffUtil.ItemCallback<User>() {
#Override
public boolean areItemsTheSame(#NonNull User newUser, #NonNull User oldUser) {
return newUser.getUserId().equals(oldUser.getUserId());
}
#Override
public boolean areContentsTheSame(#NonNull User newUser, #NonNull User oldUser) {
return newUser.equals(oldUser);
}
};
mAsyncListDiffer = new AsyncListDiffer<>(this, diffUtilCallback);
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.nb_image_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
User user = mAsyncListDiffer.getCurrentList().get(position);
Uri userImage = user.getImageUrl();
Log.d("test123", "user uri: " + userImage.toString());
holder.singleItemTextView.setText(user.getUserId());
Glide.with(holder.itemView.getContext() /* context */)
.load(userImage)
.into(holder.singleItemImage);
holder.constraintLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(v.getContext(), DisplayUserActivity.class);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return mAsyncListDiffer.getCurrentList().size();
}
public void updateList(ArrayList<User> newList) {
mAsyncListDiffer.submitList(newList);
}
}
So why does my dataset update, but the list is stuck on first update only?
I get a Data snapshot of the user's ids. These ids are also the same as the image ids of the matching profile pictures of the users.
I want to populate the RecyclerView with each image that matches the RecyclerView, but what happens is, that it populate the correct user id, but it only populates the last image in the list of the data snapshot instead of all. So I end up with a list of user ids and a single image at the bottom:
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
imageName = ds.getKey();
user = new User(imageName);
myDataset.add(user);
storageReference.child("profileImages").child(imageName).getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
user.setImageUri(uri);
// Got the download URL
mAdapter.notifyDataSetChanged();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// Handle any errors
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
imagesRef.addListenerForSingleValueEvent(eventListener);
This is how it looks:
This is the adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private ArrayList<User> mDataset;
private MyViewHolder myHolder;
private User user;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView userIdTextView;
public ImageView userProfileImageView;
public View layout;
public MyViewHolder(View v) {
super(v);
layout = v;
userProfileImageView = (ImageView) v.findViewById(R.id.profile_image);
public TextView userIdTextView = (TextView) v.findViewById(R.id.user_id_text_view);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(ArrayList<User> myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
myHolder = holder;
user = mDataset.get(position);
Uri userImage = user.getImageUri();
myHolder.userIdTextView.setText(user.getUsername());
Glide.with(myHolder.itemView.getContext() /* context */)
.load(userImage)
.apply(new RequestOptions().override(300, 300))
.into(myHolder.userProfileImageView);
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataset.size();
}
}
database structure:
database
|_____users
|___uid1
|___uid2
|___uid3
Your "user" variable cannot be used in that way inside "onSuccess()" callback method. That variable refers always to the LAST "new User()", so when the "download" procedure reach the "onSuccess()" the value of "user" value is THE LATEST executed "new User()". You need to pass something like an "userID" to the download method and when the "onSuccess()" is executed you have to find the right User using that ID, and then set its image.
As getDownloadUrl is async method other user objects are changed and only last object is referenced when onSuccess() is called so last objects uri is set.
Use HashMap<String,User> userMap = new HashMap<String,User>();
Before getDownloadUrl
userMap.put(imageName,user);
in onSuccess()
User user = userMap.get(imageName);
user.setImageUri(uri);
Make imageName final.
My ArrayList Seems to be empty when i try to retrieve data for first time due to async nature of firebase API,with second attempt to load data Its loaded properly,here is code related to the same.
Method which is used to retrieve data
AdvertisementRef.orderByChild("Category").equalTo(categoryS).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot eventSnapshot : dataSnapshot.getChildren()) {
SaveAddInformation mModel = eventSnapshot.getValue(SaveAddInformation.class);
Log.d("DATA" ,""+ mModel);
adinfolist.add(mModel);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getMessage());
}
});
return adinfolist;
}
MainActivity
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setHasFixedSize(true);
advertismentref= FirebaseDatabase.getInstance().getReference("ADVERTISEMENT INFORMATION");
helper=new Firebase_helper(advertismentref);
adapter=new AdvertisementAdapter(helper.retrieve(/*catS*/),this);
// adapter = new AdvertisementAdapter(this, helper.retrieve());
rv.setAdapter(adapter);
ViewHolder and Adapter Classes
public class AdvertisementAdapter extends RecyclerView.Adapter <AdvertisementAdapter.AdvetisementViewHolder>{
ArrayList<SaveAddInformation>saveAddInformations=new ArrayList<SaveAddInformation>();
Context ctx;
public AdvertisementAdapter(ArrayList<SaveAddInformation> saveAddInformations,Context ctx) {
this.saveAddInformations = saveAddInformations;
this.ctx=ctx;
}
#Override
public AdvertisementAdapter.AdvetisementViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v= LayoutInflater.from(parent.getContext()).inflate(R.layout.design_row,parent,false);
AdvetisementViewHolder myViewholder=new AdvetisementViewHolder(v,ctx,saveAddInformations);
return myViewholder;
}
#Override
public void onBindViewHolder(AdvertisementAdapter.AdvetisementViewHolder holder, int position) {
SaveAddInformation save=saveAddInformations.get(position);
String title=save.getTitleS();
holder.titleTv.setText(title);
String rentamt=save.getRent_amount();
holder.rentamt.setText(rentamt);
String rentdays=save.getRentDaysSpin();
holder.rentdays.setText(rentdays);
String description=save.getProductDetails();
holder.desc.setText(description);
String imguri=save.getImageuri();
holder.setImage(getApplicationContext(), imguri);
}
#Override
public int getItemCount() {
return saveAddInformations.size();
}
public static class AdvetisementViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
TextView titleTv,rentamt,rentdays,desc;
Context ctx;
ImageView adimg;
ArrayList<SaveAddInformation>saveAddInformations=new ArrayList<SaveAddInformation>();
public AdvetisementViewHolder(View itemView,Context ctx,ArrayList<SaveAddInformation>saveAddInformations) {
super(itemView);
this.saveAddInformations=saveAddInformations;
itemView.setOnClickListener(this);
this.ctx=ctx;
titleTv=(TextView)itemView.findViewById(R.id.Titletextv);
rentamt=(TextView)itemView.findViewById(R.id.rent_amount);
rentdays=(TextView)itemView.findViewById(R.id.days_rent);
desc=(TextView)itemView.findViewById(R.id.descTV);
}
public void setImage(Context applicationContext, String imguri) {
adimg=(ImageView)itemView.findViewById(R.id.ad_image);
// We Need TO pass Context
Picasso.with(applicationContext).load(imguri).into(adimg);
}
I have just moved retrieve method into main activity and called adapter.notifyDataSetChanged(); in onDataChange and onCancelled method and it worked!!!
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 :)
I'm using Firebase with RecyclerView,I'd like to retrieve the child with a specific position to delete it. I know that I should use a query of orderBy, but I need to get a specific child by its position.
Is there a way to do this? I only found a way to get the number of children with getChildrenCount() method of the snapshot, but didn't find something by passing the position value.
Thank you.
protected void populateViewHolder(final ReportViewHolder viewHolder, final Report report, int position) {
viewHolder.txtTitle.setText(report.title);
viewHolder.txtMessage.setText(report.message);
viewHolder.txtDate.setText(report.date);
viewHolder.txtuserName.setText(report.userName);
viewHolder.btnDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String key = report.key;
Query myTopPostsQuery = mDatabaseReference.child("user-reports/"+userID).orderByKey().equalTo(report.key);
myTopPostsQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
snapshot.getRef().removeValue();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
mDatabaseReference.child("reports/"+ key).removeValue();
}
});
}
};
I want to use the position of populateViewHolder, to get the child position and then delete its node.
Now I added a field for report called key and using orderByKey().equalTo(report.key); to delete it.
I think you have to add delete and add methods to your adapter class.
public class AllAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<Article> articleList = new ArrayList<>();
Context context;
public AllAdapter(Context context){
this.context = context;
}
public void addItem(Article article){
articleList.add(article);
notifyDataSetChanged();
}
public void deleteItem(int position){
articleList.remove(position);
notiftyDateSetChanged(position);
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_post_item,null);
return new ArticleViewHolder(view);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
}
#Override
public int getItemCount() {
return articleList.size();
}
class ArticleViewHolder extends RecyclerView.ViewHolder{
public ArticleViewHolder(View itemView) {
super(itemView);
}
}
}