Recycler view mixing up the contents of its items - java

I'm using a recycler view to load a list of posts made by and organization. I've used firestore as the backend.
For loading the image I'm first getting the download Url from storage reference and then using Glide to load image in to the image view.
The issue i'm facing is that since a call to the storage reference is an asynchronous one, by the time it gets the download uri from the server the position of the adapter in OnBindviewHolder is already changed Therefore image gets mixed up.
I've provided the OnBindViewHolder code and the method where i get the download url
#Override
public void onBindViewHolder(#NonNull final studentFavouriteUniversityPosts.MyViewHolder holder, int position) {
if(postsList.get(holder.getAdapterPosition()).getImageUrl()!=null &&
!postsList.get(holder.getAdapterPosition()).getImageUrl().isEmpty()){
holder.setPostImage(holder.getAdapterPosition());
}
}
public void setPostImage(int position) {
postImage.setVisibility(View.VISIBLE);
placeholder.placeholder(R.color.white);
//Getting the download uri from the Fire store storage and displaying it using glide.
storageReference.child(postsList.get(position).getImageUrl())
.getDownloadUrl()
.addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
// Log.d("URI",uri.toString());
Glide.with(context).applyDefaultRequestOptions(placeholder).load(uri).into(postImage);
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("ERROR","err loading image file");
}
});
}
Can anyone of you help me out on this?
EDIT
I changed the structure a bit. So instead of running the storage reference task in adapter, i'm storing the dowload url in the object. Thus the setPostImage method only has the Glide part.
So the method looks like this
public void setPostImage(String downloadURL) {
postImage.setVisibility(View.VISIBLE);
Glide.with(context).applyDefaultRequestOptions(placeholder).load(downloadURL).into(postImage);
}
Still i'm getting this issue. I dont know how to resolve it

First get the list of urls from firestore from the activity or fragment where you are
setting the adapter
1. public void getImages() { storageReference.child("the child name")
.getDownloadUrl()
.addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
// Log.d("URI",uri.toString());
adapter.imagesRetrieved();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("ERROR","err loading image file");
}
});
}
2. Then inside on success listener pass the group of values to the adapter method from the success listener and define the method inside the adapter
public void imagesRetreived() {
notifyDataChanged(images);
}
private void notifyDataChanged(Images images) {
this.images = images;
notifydatasetchanged(); //this one is recycler view inbuilt method
}

Related

Rewind button with picasso

I am making an application that generates a random image by a url when touching the button and presents it in the imageview and I use picasso and to change the image you have to clear the cache and I would like someone to tell me how I could go back to the previous image, like a back button
Any help is appreciated from the heart
My code to generate a new image:
nextButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String quest1 = "https://proxxcraft.com/";
Picasso.get().load(quest1).memoryPolicy(MemoryPolicy.NO_CACHE).into(memeRandomView);
Picasso.get().isLoggingEnabled();
}
});
you should save the image as a bitmap, save it in an array or some other data structure. I know how to do it with glide but you should check for how to set callbacks to know when the image is loaded.
private void loadImage() {
Picasso.with(this)
.load("https://proxxcraft.com/")
.into(mContentTarget);
}
private Target mContentTarget = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
//save the bitmap in an array so you can use it later.
Imageview.setImageBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};

Firebase Storage and Image View

I want to get image from Firebase Storage and than show at Image View. I trying this code but not working. Always I getting my "ERROR" toast message.
App is opening but I dont see my JPEG.
** I found one way but it is not enough for me.
If I delete 'com.google.firebase:firebase-auth:19.3.2' from build gradle, this code working.
I need FirebaseAuth because I have User login and logout page.
Do you know another way for Firebase storage and image ?
Thank you for help.
enter image description here
private StorageReference mStorageReference;
ImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vardiya);
mStorageReference=FirebaseStorage.getInstance().getReference().child("vardiyaandroid.jpeg");
final File localfile;
try {
localfile = File.createTempFile("vardiyaandroid","jpeg");
mStorageReference.getFile(localfile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
#Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
Toast.makeText(vardiya.this,"Resim Hazır",Toast.LENGTH_LONG).show();
Bitmap bitmap = BitmapFactory.decodeFile(localfile.getAbsolutePath());
(imageView=findViewById(R.id.imageView)).setImageBitmap(bitmap);
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(vardiya.this,"ERROR",Toast.LENGTH_LONG).show();
}
});
} catch (IOException e) {
e.printStackTrace();
}
I found my false.
You will change this code
mStorageReference=FirebaseStorage.getInstance().getReference().child("vardiyaandroid.jpeg");
with
mStorageReference=FirebaseStorage.getInstance().getReferenceFromUrl("gs://YOURAPPID.appspot.com/vardiyaandroid.jpeg");
StorageReference pathreferance =mStorageReference.child("vardiyaandroid.jpeg");
and the storege rules must be this:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, writ, auth: if true;
}
}
}

notifyDataSetChange don't work properly on custom adapter

I'm trying to add some item to db, add on success to reload my listview in a Fragment.
Actually I do have something like that.
db.collection("quizz").document().set(Quizz).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(context, getString(R.string.quizz_done), Toast.LENGTH_SHORT).show();
Quizz nQuizz = new Quizz(""+items.size()+1,""+mScore,uid,now);
items.add(nQuizz);
adapterquizz.notifyDataSetChanged();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(context,getString(R.string.quizz_not_done), Toast.LENGTH_SHORT).show();
}
});
U only update the list when u init new instance of your adapter. U need to create an update function inside your adapter:
public void updateData(List<Quizz> yourNewList) {
data.clear(); // remove this if u just want to insert new item instead of clear all
data.addAll(yourNewList)
notifyDatasetChange()
}
Call this from activity when u need to update data.
You don't need to pass the list to your adapter constructor anymore.

How to disable button in RecyclerView and from AsyncTask

I am trying to disable button after click in RecyclerView from Retrofit response.
Application is using RecyclerView to populate list and I am using Retrofit to communicate to back-end REST API. There are two buttons inside one item and Retrofit client is activated on click. And if response from API is successful button should be disabled. I came across on two problems:
first few items works just fine, but after few scrolls buttons I have never clicked on are disabled also;
second was that little few random buttons further on in list are still clickable.
public void onBindViewHolder(final NewsViewHolder holder, int position) {
holder.btnPositive.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// init Retrofit Client
JSONPlaceHolderAPI mAPIService;
mAPIService = ApiUtils.getAPIServiceFetch();
mAPIService.getNews(url).enqueue(new Callback<Result>() {
#Override
public void onResponse(Call<Result> call, Response<Result>
response) {
holder.btnPositive.setVisibility(View.GONE);
}
}
#Override
public void onFailure(Call<Result> call, Throwable t) {
// do code
}
});
}
});
holder.btnNegative.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
JSONPlaceHolderAPI mAPIService;
mAPIService = ApiUtils.getAPIServiceFetch();
mAPIService.getNews(url).enqueue(new Callback<Result>() {
#Override
public void onResponse(Call<Result> call, Response<Result>
response) {
holder.btnNegative.setEnabled(false);
}
}
#Override
public void onFailure(Call<Result> call, Throwable t) {
// do code
}
});
}
});
}
I suppose problem is somewhere in Retrofit where it uses background threads or AsyncTask.
This happens as the recycler view recycles(as the name suggests) the view that is visible to you and gives the same set of ids to every set of items which causes this problem .
This problem has 2 solution :
1) Stop the recycle behaviour of recyclerview as follows:-
#Override
public void onBindViewHolder(#NonNull MyViewHolder myViewHolder, int i) {
myViewHolder.setIsRecyclable(false);
}
But i strongly deny to use this cause recycling is reason why we use recyclerview.
2) Using a POJO Class:-
The best solution for it is to use a POJO class which will have two variables the first one is value and the second one is a boolean variable which show is the item disabled or not . Set the value of POJO boolean variable to true for the items u want to disable and in onBindViewHolder method only disable the buttons for the items which has false set in boolean variable .
If you still are confused contact me i will send you an example of it .
You need to hold states of those buttons. You can hold states in items(such as isNegativeEnabled, willPositiveDisplay etc.) of the list that populates recyclerview. When you click positive button, change willPositiveDisplay to false.
list[position].willPositiveDisplay = false;
And check list[position].willPositiveDisplay is true set visibility as visible, if not set as gone. And do this for negative button.

Storage, FireBase, Android - StorageException has occurred. Object does not exist at location [duplicate]

I already tried using Glide.with(opDetails.this).using(new FirebaseImageLoader()).load(uri).into(imgPhoto1); but had some problems with .using().
I am now trying to use picasso. Althougt the compiler shows no error, no image is displayed to the ImageView and i cant understand why.
storage = FirebaseStorage.getInstance();
storageRef = storage.getReference();
storageRef.child("images/4736af35-608d-4b97-95ba-029ef471c5eb.png").getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
Picasso.with(opDetails.this).load(uri).into(imgPhoto1);
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
}
});
}
Update:
Firebase Storage Image
Storage Rules
Thanks in advance.
Try this:-
storageRef.child("images/4736af35-608d-4b97-95ba-029ef471c5eb").getDownloadUrl().addOnSuccessListener....

Categories

Resources