I am attempting to load images from firebase into a recycler adapter and display them
I have attempted two methods to try and solve this problem the first block of code resulted in the error"You cannot start a load on a not yet attached View or a Fragment where getActivity() returns null (which usually occurs when getActivity() is called before the Fragment is attached or after the Fragment is destroyed)"
The second method the application ran but no images were loaded from reading other similar issues I have seen the isAdded() check but I couldn't seem to get that to work the images are being uploaded into a folder in firebase called post_images and the rest of the data associated with the post (Instagram like app with posting) are in firestore
public BlogRecyclerAdapter(List<BlogPost> blog_list) {
this.blog_list = blog_list;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.blog_list_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
String desc_data = blog_list.get(position).getDesc();
holder.setDescText(desc_data);
String image_url = blog_list.get(position).getImage_url();
holder.setBlogImage(image_url);
}
#Override
public int getItemCount() {
return blog_list.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private View mView;
private TextView descView;
private ImageView blogImageView;
public Context mContext;
public ViewHolder(View itemView) {
super(itemView);
mView = itemView;
}
public void setDescText(String descText) {
descView = mView.findViewById(R.id.blog_desc);
descView.setText(descText);
}
public void setBlogImage(final String downloadUri) {
blogImageView = mView.findViewById(R.id.Post_image_view);
Glide.with(mContext).load(downloadUri).into(blogImageView);
}}}
Other method attempted
mCurrentUser = FirebaseAuth.getInstance().getCurrentUser();
mImageStorage = FirebaseStorage.getInstance().getReference();
final String current_uid = mCurrentUser.getUid();
mUserDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(current_uid);
mUserDatabase.keepSynced(true);
mUserDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
StorageReference filepath = mImageStorage.child("profile_images").child(current_uid + (".jpeg"));
Log.d("heere", "S");
// This gets the download url async
filepath.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
//The download url
final String downloadUrl = uri.toString();
Log.d("tag", downloadUrl);
if (!downloadUrl.equals("default")) {
Glide.with(mContext).load(downloadUrl).into(blogImageView);
// Glide.with(getApplicationContext()).load(downloadUrl).into(mDisplayImage);
}
}});}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});}}}
I would like the image to be displayed in the recycler view
Change this line
Glide.with(mContext).load(downloadUri).into(blogImageView);
to this instead:
Glide.with(itemView.getContext()).load(downloadUri).into(blogImageView);
All ViewHolder instances have a field itemView that is guaranteed to be non-null, and you can get the context from that.
Your mContext is not initialized
mContext must be a RecyclerView.Adapter global variable and not a ViewHolder variable
mContext must initialize in onCreateViewHolder function this way
this.mContext = parent.getContext();
Related
I'm using this method to load active users:
private void loadActiveUsers() {
usersList.clear();
Query activeUsersQuery = firebaseFirestore.collection("Users").whereEqualTo("active", true);
activeUsersQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot queryDocumentSnapshots, #Nullable FirebaseFirestoreException e) {
for (DocumentChange documentChange: queryDocumentSnapshots.getDocumentChanges()) {
if (documentChange.getType() == DocumentChange.Type.ADDED) {
String user_id = documentChange.getDocument().getId();
Users users = documentChange.getDocument().toObject(Users.class).withId(user_id);
usersList.add(users);
activeUsersRecyclerAdapter.notifyDataSetChanged();
}
if (documentChange.getType() == DocumentChange.Type.MODIFIED) {
String user_id = documentChange.getDocument().getId();
Users users = documentChange.getDocument().toObject(Users.class).withId(user_id);
usersList.remove(users);
usersList.clear();
usersList.add(users);
activeUsersRecyclerAdapter.notifyDataSetChanged();
}
if (documentChange.getType() == DocumentChange.Type.REMOVED) {
String user_id = documentChange.getDocument().getId();
Users users = documentChange.getDocument().toObject(Users.class).withId(user_id);
usersList.remove(users);
activeUsersRecyclerAdapter.notifyDataSetChanged();
}
}
}
});
}
When a new user becomes active, his name is added to the list but multiple times. When the refresh button is clicked, the list is back to normal. And if he is inactive, the recyclerview is messed up.
This is the adapter:
public class ActiveUsersRecyclerAdapter extends RecyclerView.Adapter<ActiveUsersRecyclerAdapter.ViewHolder> {
public List<Users> usersList;
public Context context;
public ActiveUsersRecyclerAdapter(Context context, List<Users> usersList) {
this.context = context;
this.usersList = usersList;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int i) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_list_item, parent, false);
context = parent.getContext();
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
final String userID = usersList.get(position).UserID;
String image = usersList.get(position).getImage();
holder.setImage(image);
String username = usersList.get(position).getUsername();
holder.setUsername(username);
holder.view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(context, userID, Toast.LENGTH_SHORT).show();
}
});
}
#Override
public int getItemCount() {
return usersList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public View view;
public CircleImageView imageView;
public TextView usernameView;
public ViewHolder(#NonNull View itemView) {
super(itemView);
view = itemView;
}
public void setImage(String image) {
imageView = view.findViewById(R.id.user_image);
Glide.with(context).load(image).into(imageView);
}
public void setUsername(String username) {
usernameView = view.findViewById(R.id.user_username);
usernameView.setText(username);
}
}
}
I tried the holder.setIsRecyclable(false) but it didn't work.
How can I prevent the data from being added multiple times or prevent the recycler from messing up?
When you call addSnapshotListener you're adding a permanent listener to the data. When you attach the listener it immediately starts loading the data and calls your onEvent, but it also stays actively monitoring the data and calls onEvent again if there's any relevant change (such as when another user becomes active). So if you use addSnapshotListener there's no need to call loadActiveUsers multiple times.
If you only want to load the active users when you call loadActiveUsers, consider using the get() method instead. When you call get() the data is only loaded that once single time. So in that case, you can recall loadActiveUsers whenever the user hits refresh to load the updated data.
In general I'd recommend using addSnapshotListener though, as it makes your UI be responsive to changes in the data (no matter where they originate from). But in that case be sure to only attach the listener once (typically when you create the activity) and remove it when you're done (typically when the activity is stopped, paused or hidden).
I am making an android app where I am using MongoDB and NodeJs as a backend service.I have some posts saved on MongoDb and I am retrieving them in recycler view.I have a button in recycler view when it is clicked I want to fetch an Object Id of an item.
I am successfully fetching all documents in recycler view but the problem is
when I clicked on button in particular item.They are showing Object Id of a document which is inserted recently and not showing correct Object Id of an item.
This is what I have done so far:
MyPostBookAdapter.java
public class MyPostedBookAdapter extends RecyclerView.Adapter<MyPostedBookAdapter.ViewHolder> {
List<PostedModel> listItem;
Activity context;
String id;
public MyPostedBookAdapter(List<PostedModel> listItem, Activity context){
this.listItem = listItem;
this.context = context;
}
#NonNull
#Override
public MyPostedBookAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.posted_book,viewGroup,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull final MyPostedBookAdapter.ViewHolder viewHolder, final int i) {
final PostedModel model = listItem.get(i);
//Object Id of post
id = model.getPostId();
viewHolder.userBookName.setText(model.getPurchaseBookName());
RequestOptions requestOptions = new RequestOptions();
requestOptions.placeholder(R.drawable.openbook);
Glide.with(context).load(model.getPurchaseImage()).apply(requestOptions).into(viewHolder.userPostBook);
viewHolder.delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context,id,Toast.LENGTH_SHORT).show();
}
});
}
#Override
public int getItemCount() {
return listItem.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView userPostBook;
TextView userBookName;
Button delete;
public ViewHolder(#NonNull View itemView) {
super(itemView);
userPostBook = (itemView).findViewById(R.id.userPostBook);
userBookName = (itemView).findViewById(R.id.userBookName);
delete = (itemView).findViewById(R.id.delete);
}
}
}
PostedModel.java
public class PostedModel {
String purchaseImage,purchaseBookName,postId;
public PostedModel(){
}
public PostedModel(String purchaseImage, String purchaseBookName,String postId){
this.purchaseBookName = purchaseBookName;
this.purchaseImage = purchaseImage;
this.postId = postId;
}
public String getPurchaseImage() {
return purchaseImage;
}
public void setPurchaseImage(String purchaseImage) {
this.purchaseImage = purchaseImage;
}
public String getPurchaseBookName() {
return purchaseBookName;
}
public void setPurchaseBookName(String purchaseBookName) {
this.purchaseBookName = purchaseBookName;
}
public String getPostId() {
return postId;
}
public void setPostId(String postId) {
this.postId = postId;
}
}
Please let me know how can I get ObjectId correspond to right item.
Any help would be appreciated.
THANKS
The Issue is,
Recycler view load every object given one by one, so variable id had the value of last object. So you need to take id from the selected view.
The Fix is,
#Override
public void onBindViewHolder(#NonNull final MyPostedBookAdapter.ViewHolder viewHolder, final int i) {
final PostedModel model = listItem.get(i);
//Object Id of post
id = model.getPostId();
// You need to set this id to viewHolder.
viewHolder.userBookName.setId(id);
viewHolder.userBookName.setText(model.getPurchaseBookName());
RequestOptions requestOptions = new RequestOptions();
requestOptions.placeholder(R.drawable.openbook);
Glide.with(context).load(model.getPurchaseImage()).apply(requestOptions).into(viewHolder.userPostBook);
viewHolder.delete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Here you can extract the id which we set at binding
int idOfView = v.userBookName.getId();
Toast.makeText(context,idOfView,Toast.LENGTH_SHORT).show();
}
});
}
Hope this will help you.
I'm using Firebase as a database and a preview to get information about the saved data, now I can bring both the image and the name of that image in my view, but now I would like to be able to click on the quick view and open a new fragment or activity where the same image or publication can be displayed that is clicked, that is, the photo, the name and the power to include other data within that new fragment or activity when clicking on the recyclerview post.
In a few words for example: a clothing store, you have a recycling view with your products and your name, click on that product opens but with all the other details and specifications you have.
I am currently using: Firebase navigation button with fragments
Fragment where the recyclerview is shown:
mRecyclerView = v.findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mUploads = new ArrayList<>();
mDatabaseRef = FirebaseDatabase.getInstance().getReference("Posts");
mDatabaseRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
Uploads uploads = postSnapshot.getValue(Uploads.class);
mUploads.add(uploads);
}
mAdapter = new ImageAdapter(getActivity(), mUploads);
mRecyclerView.setAdapter(mAdapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});return v;
//adapter code
public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ImageViewHolder> {
private Context mContext;
private List<Uploads> mUploads;
public ImageAdapter(Context context, List<Uploads> uploads) {
mContext = context;
mUploads = uploads;
}
#Override
public ImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(mContext).inflate(R.layout.home_item, parent, false);
return new ImageViewHolder(v);
}
#Override
public void onBindViewHolder(ImageViewHolder holder, final int position) {
Uploads uploadCurrent = mUploads.get(position);
holder.textViewName.setText(uploadCurrent.getName());
Picasso.get()
.load(uploadCurrent.getImageUrl())
.placeholder(R.mipmap.ic_launcher)
.into(holder.imageView);
}
#Override
public int getItemCount() {
return mUploads.size();
}
public class ImageViewHolder extends RecyclerView.ViewHolder {
public TextView textViewName;
public ImageView imageView;
public ImageViewHolder(View itemView) {
super(itemView);
textViewName = itemView.findViewById(R.id.Descripcion);
imageView = itemView.findViewById(R.id.ImagePro);
}
}
}
You have two options.
You can create a callback interface and trigger the callback method from your Adapter's onClick. The activity or fragment that implemented the interface and passed the instance of it to the Adapter will get the callback and you can launch a quick view from there. I prefer this method.
Here you can find a detailed example to achieve this.
Since you already have Context in your class, you can directly reference a method in an Activity or Fragment and create a quick view Fragment from there.
public void onBindViewHolder(ImageViewHolder holder, final int position) {
((YourActivity) context).startQuickviewFragment();
}
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.
Firebase Database----------
I've been trying to show all the images stored in firebase in an android application using recyclerview. Is there a way to do so?
I uploaded the photos I want to show in firebase storage and its download URL in firebase database. Each image has a primary ID. I don't know how I can get access to all the child elements of all the primary ID.
Please help me solve this problem.
if using a fragment you can do I like below on your on create method
#Override
public void onViewCreated(final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
databaseReference = FirebaseDatabase.getInstance().getReference().child("users");
databaseReference.keepSynced(true);
firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<AllUsers, AllUsersViewHolder>
(AllUsers.class, R.layout.all_users_display_layout, AllUsersViewHolder.class, databaseReference) {
#Override
protected void populateViewHolder(AllUsersViewHolder viewHolder, AllUsers model, final int position) {
viewHolder.setThumbImage(getContext(), model.getThumbImage());
}
};
all_user_list.setAdapter(firebaseRecyclerAdapter);
}
and create a static class that extends from RecyclerView.ViewHolder like below
public static class AllUsersViewHolder extends RecyclerView.ViewHolder {
View view;
public AllUsersViewHolder(View itemView) {
super(itemView);
view = itemView;
}
public void setThumbImage(final Context context, final String thumb_image) {
final CircleImageView all_user_image = (CircleImageView) view.findViewById(R.id.all_user_image);
if(!thumb_image.equals("default_image")) {
Picasso.with(context).load(thumb_image).networkPolicy(NetworkPolicy.OFFLINE)
.placeholder(R.drawable.default_image).into(all_user_image, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
Picasso.with(context).load(thumb_image).placeholder(R.drawable.default_image).into(all_user_image);
}
});
}
}
}
and create a helper class like below
public class AllUsers {
public String thumb_image;
public AllUsers() {
}
public AllUsers(String thumb_image) {
this.thumb_image = thumb_image;
}
public String getThumbImage() {
return thumb_image;
}
public void setThumbImage(String thumb_image) {
this.thumb_image = thumb_image;
}
}
after that you can retrieve your images to your recycler view