images inside recyclerview being invisible onscroll - java

I'm trying to read if there is any image or not, then if image is null, I make the imageview in post card gone. If not then load it using Glide.
It works when I load the recyclerview for first time. But after some scrolls, those imageviews get invisible(View.gone)
onBindViewHolder:
DataSnapshot d = topics.get(position);
holder.text.setText(d.child("caption").getValue(String.class));
if (d.child("type").getValue(String.class)!=null&&d.child("type").getValue(String.class).equals("image")){
Glide.with(c).load(d.child("image").getValue(String.class)).placeholder(R.drawable.background).centerCrop().into(holder.image);
}
else{
holder.image.setVisibility(View.GONE);
}
holder.name.setText(d.child("username").getValue(String.class));
holder.category.setText(d.child("category").getValue(String.class));
long millis = System.currentTimeMillis();
long posttime = d.child("time").getValue(Long.class);
long timedifference = millis-posttime;
int days = (int) (timedifference / (1000*60*60*24));
if (days<2){
holder.time.setText("Today");
}else {
holder.time.setText(days+" days ago");
}
if (d.child("pic").exists()){
Glide.with(c).load(d.child("pic").getValue(String.class)).placeholder(R.drawable.background).centerCrop().into(holder.pic);
}
So where am I doing wrong?

It is better to set the visibility first in your case as you are passing the image view to Glide to setup.
We never know what will happen with the glide library update, in case it library wants to access the dimensions. Especially you are making it GONE in else part, which is a bit different from making INVISIBLE.
if (d.child("type").getValue(String.class)!=null&&d.child("type").getValue(String.class).equals("image")){
holder.image.setVisibility(View.VISIBLE);
Glide.with(c).load(d.child("image").getValue(String.class)).placeholder(R.drawable.background).centerCrop().into(holder.image);
}
else{
holder.image.setVisibility(View.GONE);
}

Try this code snippet :
if (d.child("type").getValue(String.class)!=null&&d.child("type").getValue(String.class).equals("image")){
Glide.with(c).load(d.child("image").getValue(String.class)).placeholder(R.drawable.background).centerCrop().into(holder.image);
holder.image.setVisibility(View.VISIBLE);
}
else{
holder.image.setVisibility(View.GONE);
}
When you are setting visibility gone it won't be visible by it self.
you need to visible it when glide load the image and gone when image
is null.
Add holder.image.setVisibility(View.VISIBLE); in if condition.

RecyclerView creates new views first time for first visible data objects and when you scroll to load other items it reuses those views which has been scrolled to top and reuses those views again for new data items.
So whatever the visibility status of that view was when it was created for the first time, it will get retain and will be used for the other items as well.
What you need to do is to set the view VISIBLE explicitly to show it again:
if (d.child("type").getValue(String.class)!=null&&d.child("type").getValue(String.class).equals("image")){
holder.image.setVisibility(View.VISIBLE)
Glide.with(c).load(d.child("image").getValue(String.class)).placeholder(R.drawable.background).centerCrop().into(holder.image);
}
else{
holder.image.setVisibility(View.GONE);
}

Related

How to fade between ImageViews in an Android app?

I know how to do this for 2 images but what about 3 images.
public void fadeBardock(View view) {
ImageView bardock = (ImageView) findViewById(R.id.bardock);
ImageView goku = (ImageView) findViewById(R.id.goku);
ImageView gohan = (ImageView) findViewById(R.id.gohan);
bardock.animate().alpha(0 f).setDuration(3000);
goku.animate().alpha(0 f).setDuration(4000);
gohan.animate().alpha(1 f).setDuration(5000);
}
After clicking on button I'm directly getting the third image without getting the second image.
Try this code:
bardock.animate().alpha(0f).setDuration(3000);
goku.setAlpha(0f);
goku.animate().alpha(1f).setDuration(3000).setStartDelay(1500);
goku.animate().alpha(0f).setDuration(3000).setStartDelay(4500);
gohan.setAlpha(0f);
gohan.animate().alpha(1f).setDuration(3000).setStartDelay(6000);
You can change duration and start delays to achieve your optimal result.
On More than one imageviews,
1. Create an array of ImageView.
2. Initialize the views in it.
3. assign unique id's to each view in same loop.
[ Another thing is, you can initialize the views in one for loop in onCreate() and then on button click, apply another for loop for animation.]
Now, using an index of array, assign an animation to each view with delay from previous views.
Start animation after the loop and check.
Hope it helps.

ListView (Inside CardView) visually bugs upon resizing. Gif inside

FIXED: I was incorrectly updating my ListView adapter. see: How to refresh Android listview?
I have been struggling with an incredibly weird bug for too long of a time, and I wonder if any of you have had similar problems.
A detailed explanation of the bug will be less clear than for me to just show you, so I have attached a gif that displays exactly what goes wrong below.
http://giphy.com/gifs/xT9DPGixQnDIEHlVU4
Question 5 is included to show you how things are supposed to wrong. Then, when the CardView and the ListView inside it resize because the amount of options changes at question 6, the following goes wrong:
- The colors of the listview items do not get updated according to their correctness. (should be either green or a light shade of red)
- The checked RadioButton weirdly gets unchecked, right before I click the button.
Note: this bug only appears once upon resizing of the Listview. Everything proceeds to work fine once a similar size optionlist is supplied at the next question
These actions take place upon the second click of the button, which calls the following code
/*
FOR EACH LISTITEM:
> Make button unclickable
> Animate background change according to correctness of answer
*/
for (int i = 0; i < currentQuestion.getOptionList().size(); i++) {
ListView optionList = (ListView) rootView.findViewById(R.id.optionList);
View item = (View) optionList.getChildAt(i);
item.findViewById(R.id.optionRadioButton).setClickable(false);
final LinearLayout llayout = (LinearLayout) item.findViewById(R.id.itemLinearLayout);
int colorTo = getResources().getColor(R.color.lightPrimary);
if (currentQuestion.getOptionList().get(i).equals(currentQuestion.getAnswer())) {
colorTo = getResources().getColor(R.color.correct);
}
int colorFrom = getResources().getColor(R.color.white);
ValueAnimator colorAnimation = backgroundColorAnimator(
colorFrom,
colorTo,
llayout,
i);
colorAnimation.start();
}
// Bottom part of list view is animated SEPERATELY.
// please note that THIS DOES GET UPDATED ACCORDINGLY.
int colorFrom = getResources().getColor(R.color.white);
int colorTo = getResources().getColor(R.color.lightPrimary);
// Also increments question correct counter (important
if (correctAnswerSelected) {
colorTo = getResources().getColor(R.color.correct);
currentExercise.questionCorrectlyAnswered();
}
ValueAnimator anim = backgroundColorAnimator(
colorFrom,
colorTo,
fabContainer,currentQuestion.getOptionList().size()+1);
anim.start();
My hypothesis: Somehow, upon resizing the CardView / RelativeLayout / ListView, the ListView gets reinitiated and I call a different version of it. I suspect this is the cause because the lower part of the CardView (a FAB within a FrameLayout) does change color accordingly.
Any and all help is appreciated, I am at a complete loss with this question
Thanks in advance.

Error RecyclerView with Picasso Android

I have recyclerview that contains image.
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
// Get element from your dataset at this position and replace the contents of the view
// with that element
if(mEntities.get(position).url.equals("kosong")) {
Log.e("LOAD", "KOSONG " + position);
viewHolder.getTextDataView().setText(mEntities.get(position).data);
}
else{
Log.e("LOAD", "ISI " + position);
Picasso.with(mContext).load(mEntities.get(position).url).skipMemoryCache().into(viewHolder.imageView);
viewHolder.getTextDataView().setText(mEntities.get(position).data);
}
}
Im success to make the image loaded to recyclerview in correct list, but somehow it duplicate in another list in that recyclerview. Why ?
Thanks for your answer :)
So from what I understand if your url contains "kosong" you shouldn't display any image right?
However you're not clearing the imageview of it's previous image. Keep in mind that listviews/recycler views recycle their views. So if you display an image on your imageview and later on (scrolling up and down) that imageview is recycled it will contain the last image that was set to it.
Remember to clear the image when you don't want to use it (if(mEntities.get(position).url.equals("kosong"))) using something like:
viewHolder.imageView.setImageResource(android.R.color.transparent);
So it should be something like this:
if(mEntities.get(position).url.equals("kosong")) {
Log.e("LOAD", "KOSONG " + position);
viewHolder.getTextDataView().setText(mEntities.get(position).data);
//we don't need to display an image here however it's possible that our listview contains another image from a recycled row. Let's clear it
viewHolder.imageView.setImageResource(android.R.color.transparent);
}
else{
Log.e("LOAD", "ISI " + position);
Picasso.with(mContext).load(mEntities.get(position).url).skipMemoryCache().into(viewHolder.imageView);
viewHolder.getTextDataView().setText(mEntities.get(position).data);
}

Images getting mixed up in listview

I am developing a chat application, where i have implemented image sending. So without the images everything works fine. But now I'm facing issues in displaying the images that are sent / received. For that what i've done is downloaded the image first and saved it in external storage. My idea was simple fetch the bitmap set it in an imageview on the getView method. Nothing wrong with fetching the image and displaying but listview ruins everthing.
This is my code-
ListView getView()
ViewHolder holder = null;
if(convertView == null ){
convertView = inflater.inflate(R.layout.chat_list, null);
holder = new ViewHolder();
holder.mMsg = (TextView) convertView.findViewById(R.id.text);
holder.mDate = (TextView) convertView.findViewById(R.id.text1);
holder.mLinLay=(LinearLayout) convertView.findViewById(R.id.linSide1);
holder.mRelLay=(RelativeLayout) convertView.findViewById(R.id.relSide);
holder.img=(ImageView)convertView.findViewById(R.id.image);
holder.progCircle=(ProgressBar)convertView.findViewById(R.id.pbHeaderProgress);
convertView.setTag(holder);
} else{
holder = (ViewHolder) convertView.getTag();
}
holder.position=position;
holder.img.setTag(position+"img");
HashMap<String, String> hm = list.get(position);
String date=hm.get("date");
long l = Long.parseLong(date);
long unixTime = System.currentTimeMillis();
CharSequence datedate=DateUtils.getRelativeTimeSpanString(l, unixTime, 1);
String status = hm.get("status");
String who = hm.get("who");
if(hm.get("message").contains("[[pic_message]]"))
{
String picName=hm.get("message").replace("[[pic_message]]", "");
final String picPath1=Environment.getExternalStorageDirectory()+"/App/sent_pics/"+picName+".jpg";
File file = new File(picPath);
if(file.exists())
{
new AsyncTask<ViewHolder, Void, Bitmap>() {
private ViewHolder v;
Bitmap bm;
#Override
protected Bitmap doInBackground(ViewHolder... params) {
v = params[0];
bm = decodeSampledBitmapFromResource(picPath1,100,100);
return bm;
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (v.img.getTag().equals(position+"img")) {
v.img.setImageBitmap(result);
}
}
}.execute(holder);
}else{
holder.img.setImageBitmap(null);
}
}else{
holder.img.setImageBitmap(null);
Spannable msg =getSmiledText(getApplicationContext(),hm.get("message"));
holder.mMsg.setText(msg);
}
holder.mDate.setText(datedate);
return convertView;
What the problem is-
The listview shows up, but the images are displayed at random listview items. I searched a lot for this kind of problem, tried them but still no luck. The asyntask code is from Google- Use A Background Thread.. As you see from above i didn't set any holder.mMsg.setText(msg); but still whenever the images are shown they are accompanied by a msg, which is not desirable. Also the scrolling is kindda laggy when the images are shown. I have seen many messaging apps, they all run so smoothly, and the items are never mixed up. So what do they use.Also, while googling this problem, one answer suggested that we should remove the converView if else and then everything will be fine. But that will have performance issues which i don't want.
So my question is what should i do? How do the chat apps like, hangouts,bbm,whatsapp achieve this so smoothly?
EDIT
The pictures are displayed with no problem but the main problem is that they are diplayed at random orders.
Like this- i scroll till the image message, they are displayed. Then I again scroll and now the image is displayed in other listview item. My guess is that the same view(which originallly holds the image) is now being used by other, but the image stays there. How can i fix that ? And make the listview smooth (even with images) like the IM apps?
It's because Android ListView reuses Views, so while your Asynctask execute the donInBackground the user is already doing something else and you code don't know anything about it. (So it will put the image)
I would use Picasso to avoid the problem and let someone else handle your problem.
I handled this problem in my application (a contact phone) using:
A cache, to avoid to send multiple requests for the same image even when i already have in memory his image
I send a WeakReference<> to the Asynctask so when it's done check if the ImageView is still valid and put the bitmap inside it. Update the cache.
(Specific to me) Since sometimes i could happen that a contact don't have a photo i just created another List with all users without a photo (so i don't try to download it)
Everytime a .getview is called check if the image is inside the cache and if it's inside the cache put directly it, or start the asynctask.
Bitmap bitmap = cache.get(number);
if (bitmap == null) {
Log.d(TAG, "I will download image for " + name + ".");
// i set a dummy image to avoid to show the wrong picture
holder.contactPhoto.setImageResource(R.drawable.ic_action_person);
loadUserProfilePhoto(number, holder.contactPhoto);
}
else {
// show it directly
Log.d(TAG, "I already have " + name + " in cache.");
holder.contactPhoto.setImageBitmap(bitmap);
}
If you see, it could happen that it shows another picture instead of the user-photo while it's still loading it so i set a dummy picture instead of the old photo.
It's not the best implementation, but it's to show you that it could be very hard to implement it by yourself and do it right.
Read Picasso documentation and see how it could implement it in your code (it's simple, it's just one line.)
The issue is with listview which continuously keep refreshing it.
So your all code is fine, but just set default image in holder.img instead of making it null holder.img.setImageBitmap(null);
And may be in async too, you need set default_image while your image is being loaded.
If you want display images on the fly I think you should use SurfaceView. It is a lot faster and has a higher refresh rate. I made a video streaming application where I tried to display each video frame on the ImageView but that failed. But when I tried out SurfaceVIew it worked out fine.

Add view to linear layout in background thread one at a time, not once all are done

I have a linear layout that I am adding several views to. Think linear layout of result items. It is possible for there to be up to 150 result items. Generating each view takes a bit, so I want them to show up as they become available.
Here is my current code:
for (final Dealership loc: locations) {
final int x = resultNumber;
view.post(new Runnable(){
public void run(){
if(loc != null && parent != null && currentLocation != null) {
View v = getResultView(x, loc, parent, currentLocation);
v.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
if(parentLayout != null) {
parentLayout.addView(v);
}
}
}
});
resultNumber++;
}
This works in that it runs in the background and adds all the items. The only problem is that all the views appear at once, after a few seconds. I would really like it if they appeared as soon as they were created.
Is there a way I can modify this code so that it works as desired? Am I going about this the wrong way?
Thanks!
It seems like instead of a LinearLayout, you'll want to instead go with a ListView.
A ListView would give you a scrolling list of elements that are backed by a Collection of some sort (Array, List, etc.), and you could update that list on the fly. This is helpful for when you are slowly aggregating data from an external resource.
ListView also does some optimizing under-the-hood, and will only render row views when they are shown on the screen. If it is taking some time to render all 150 views in your LinearLayout, a ListView would help you here as well.

Categories

Resources