简体   繁体   中英

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:

  1. A cache, to avoid to send multiple requests for the same image even when i already have in memory his image
  2. 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.
  3. (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)
  4. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM