0

Hi i am a noob to android, I am trying to load some data to a ListView using custom adapter. I am using the following code for loading data. First time, it working well. But while I am trying to load more, then data loads and image is showing random while scrolling. After showing some random images in that list finally shows the correct image. It is repeating on the next scroll also

Here is my getView code

 @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = convertView;
        final ViewHolder holder;
        if (v == null) {
            LayoutInflater vi =
                (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(R.layout.review_list_m, null);
            v.setMinimumHeight(height);
            holder = new ViewHolder();
            holder.item1 = (TextView) v.findViewById(R.id.name);
            holder.image = (ImageView) v.findViewById(R.id.posterView);
            v.setTag(holder);
        }
        else
            holder=(ViewHolder)v.getTag();

        int colorPos = position % colors.length;
        v.setBackgroundColor(Color.parseColor(colors[colorPos]));


        final Custom custom = entries.get(position);
        if (custom != null) {
            holder.image.setBackgroundColor(Color.parseColor(colors[colorPos]));
            holder.image.setLayoutParams(new LinearLayout.LayoutParams(height-10,width-10));
            String imgUrl=custom.getImage();

            AsyncHttpClient client = new AsyncHttpClient();
              String[] allowedContentTypes = new String[] { "image/png", "image/jpeg" };
              client.get(imgUrl, new BinaryHttpResponseHandler(allowedContentTypes) {
                  @Override
                  public void onSuccess(byte[] fileData) {
                      // Do something with the file
                      ByteArrayInputStream inputStream = new ByteArrayInputStream(fileData);
                      Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                      holder.image.setImageBitmap(bitmap);
                  }
              });

              holder.item1.setHeight(height/3);
            Log.v("PATH",custom.getcustomBig());
            holder.item1.setText(custom.getcustomBig());

        }
        return v;
    }

Any idea ? Please help

UPDATE

   @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = convertView;
        final ViewHolder holder;
        if (v == null) {
            LayoutInflater vi =
                (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(R.layout.review_list_m, null);
            v.setMinimumHeight(height);
            holder = new ViewHolder();
            holder.item1 = (TextView) v.findViewById(R.id.name);
            holder.image = (ImageView) v.findViewById(R.id.posterView);
            v.setTag(holder);
        }
        else
            holder=(ViewHolder)v.getTag();

        int colorPos = position % colors.length;
        v.setBackgroundColor(Color.parseColor(colors[colorPos]));


        final Custom custom = entries.get(position);
        if (custom != null) {
            holder.image.setBackgroundColor(Color.parseColor(colors[colorPos]));
            holder.image.setLayoutParams(new LinearLayout.LayoutParams(height-10,width-10));
            final String imgUrl=custom.getImage();
            holder.image.setTag(imgUrl);


            holder.image.setImageBitmap(null);
            Bitmap cachedBitmap = cache.get(imgUrl);
            if( cachedBitmap == null) {

                Log.v("HERE","DOWNLOADING");
                AsyncHttpClient client = new AsyncHttpClient();
              String[] allowedContentTypes = new String[] { "image/png", "image/jpeg" };
              client.get(imgUrl, new BinaryHttpResponseHandler(allowedContentTypes) {
                  @Override
                  public void onSuccess(byte[] fileData) {
                      // Do something with the file
                      ByteArrayInputStream inputStream = new ByteArrayInputStream(fileData);
                      Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

                       holder.image.setImageBitmap(bitmap);
                                      }
              });
            }
            else 
            {
                holder.image.setImageBitmap(cachedBitmap);
            }

              holder.item1.setHeight(height/3);
            //Log.v("PATH",custom.getcustomBig());
            holder.item1.setText(custom.getcustomBig());

        }
ramesh
  • 4,008
  • 13
  • 72
  • 117
  • your problem is with this line : `if (custom != null)` see this link to understanding listviews recycling http://stackoverflow.com/questions/11945563/how-listviews-recycling-mechanism-works – Shayan Pourvatan Jan 11 '14 at 17:29
  • for solving problem delete this two line final Custom custom = entries.get(position); if (custom != null) – Shayan Pourvatan Jan 11 '14 at 17:30

3 Answers3

1

Problably you should set null bitmap to holder.image if v != null. Otherwise Android can show bitmap from other cell until new image is downloaded via async http request.

Example code (for problem with redownload images), it wasn't testes, but should give you idea how should it looks:

HashMap<String, Bitmap> cache = new HashMap<String, Bitmap>();

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    //....

    final Custom custom = entries.get(position);
    if (custom != null) {
        holder.image.setBackgroundColor(Color.parseColor(colors[colorPos]));
        holder.image.setLayoutParams(new LinearLayout.LayoutParams(height-10,width-10));
        String imgUrl=custom.getImage();

        Bitmap cachedBitmap = cache.get(imgUrl);

        if( cachedBitmap == null) {
            AsyncHttpClient client = new AsyncHttpClient();
            String[] allowedContentTypes = new String[] { "image/png", "image/jpeg" };
            client.get(imgUrl, new BinaryHttpResponseHandler(allowedContentTypes) {
                @Override
                public void onSuccess(byte[] fileData) {
                    // Do something with the file
                    ByteArrayInputStream inputStream = new ByteArrayInputStream(fileData);
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                    holder.image.setImageBitmap(bitmap);
                    cache.add( imgUrl, bitmap );
                }
            });
        }
        else 
        {
            holder.image.setImageBitmap(cachedBitmap);
        }

        holder.item1.setHeight(height/3);
        Log.v("PATH",custom.getcustomBig());
        holder.item1.setText(custom.getcustomBig());
    }
pstrag
  • 617
  • 5
  • 16
  • while adding this line, holder.image.setImageBitmap(null); the image stops loading randomly. But the image loaads again while scroll . Any idea ? – ramesh Jan 11 '14 at 17:44
  • I don't understand, do you mean you don't see for a while image which was downloaded previous (e.g. you scroll again to first cell and image in this cell is empty for a while)? – pstrag Jan 11 '14 at 17:53
  • Yes.. showing first 3 rows, when I scroll down the images in rest of the rows showing empty and loads after scrolling. When I scrolls up, the same thing. any idea ? – ramesh Jan 12 '14 at 03:22
  • You can add SparseArray (where key is Custom id, or position and value is Bitmap) or HashMap (where key is bitmap url and value is bitmap) where you store downloaded bitmaps and when you need use it again. So you can then check if image was download previous and if was you should use cache, not start new async request. – pstrag Jan 12 '14 at 15:28
  • hi @thomasz can you please show / give me a piece of code ? I am new to this – ramesh Jan 12 '14 at 15:35
  • thanks... need to add the line HashMap cache = new HashMap(); at the top ? – ramesh Jan 12 '14 at 15:54
  • in the class, it's instance variable – pstrag Jan 12 '14 at 15:56
  • hi. I tried your code. Now my code looks like above ( Updated my question ). Now also Log.v("HERE","DOWNLOADING"); logging and images download on each scroll. – ramesh Jan 12 '14 at 16:34
  • I think you forget about "cache.add( imgUrl, bitmap );" – pstrag Jan 12 '14 at 16:38
  • thanks.. while adding that line, showing the following error "The method add(String, Bitmap) is undefined for the type HashMap" – ramesh Jan 13 '14 at 11:20
  • problem solved when I add cache.put( imgUrl, bitmap ); – ramesh Jan 14 '14 at 08:58
0

EDITed answer Validate your imageview's tag hasn't change before you set the Bitmap. You could try:

    /**
 * Caches an image (or a group of them) async.
 * @author 
 *
 */
public static class ImageCacher extends AsyncTask<String, String, Integer>{

    Context context;
    ImageView iv;
    Bitmap b;


    public ImageCacher(Context context, ImageView iv){
        this.context = context;
        this.iv = iv;
    }

    @Override
    protected Integer doInBackground(String... params) {

        for(final String param:params){
            //check if already CACHED
            String filename = String.format("%d", param.hashCode());
            File file = new File(context.getFilesDir(), filename);
            if(file.exists()){
                try {
                    b = BitmapFactory.decodeFile(file.getAbsolutePath());
                } catch (Exception e) {
                }
                if(b != null){
                    if(iv != null){
                        iv.post(new Runnable() {

                            public void run() {
                                String tag = (String) iv.getTag();
                                if(tag != null){
                                    if(tag.matches(param))
                                        iv.setImageBitmap(b);
                                }

                            }
                        });
                    }

                    return 0;
                }else{
                    file.delete();
                }
            }

            //download
            file = new File(context.getFilesDir(), filename);
            b = saveImageFromUrl(context, param);
            if(b != null){
                if(iv != null){
                    iv.post(new Runnable() {

                        public void run() {
                            String tag = (String) iv.getTag();
                            if(tag != null){
                                if(tag.matches(param))
                                    iv.setImageBitmap(b);
                            }

                        }
                    });
                }
            }
        }

        return 0;
    }

}

/**
 * Gets an image given its url
 * @param param
 */
public static Bitmap saveImageFromUrl(Context context, String fullUrl) {
    Bitmap b = null;

    try {
        URL url = new URL(fullUrl);
        URLConnection conn = url.openConnection();
        conn.setDoInput(true);
        conn.connect();
        //save bitmap to file
        InputStream is = conn.getInputStream();
        String filename = String.format("%d", fullUrl.hashCode());
        File file = new File(context.getFilesDir(), filename);
        if(file.exists()){
            //delete
            file.delete();
            file=null;
        }
        file = new File(context.getFilesDir(), filename);

        FileOutputStream out = new FileOutputStream(file);
        byte buffer[] = new byte[256];
        while(true){
            int cnt = is.read(buffer);
            if(cnt <=0){
                break;
            }
            out.write(buffer,0, cnt);
        }

        out.flush();
        out.close();
        is.close();

        b = BitmapFactory.decodeFile(file.getAbsolutePath());

    } catch (Exception e) {
        e.printStackTrace();
    }

    return b;
}

/**
 * Gets an already cached photo
 * 
 * @param context
 * @param fullUrl
 * @return
 */
public static Bitmap getCachedPhoto(Context context, String fullUrl){
    System.gc();
    String filename = String.format("%d", fullUrl.hashCode());
    File file = new File(context.getFilesDir(), filename);
    if(file.exists()){
        try {
            Bitmap b = BitmapFactory.decodeFile(file.getAbsolutePath());
            return b;
        } catch (Exception e) {
        }
    }

    return null;
}

And then, to set the image in the adapter, you can do like:

//set info
    vh.iv.setTag("");
    String foto = your_url_goes_here;
    vh.iv.setImageResource(R.drawable.fotodefault_2x);
    if(!TextUtils.isEmpty(foto)){
        vh.iv.setTag(foto);
        Bitmap b = getCachedPhoto(context, foto);
        if(b != null){
            vh.iv.setImageBitmap(b);
        }else{
            new ImageCacher(context, vh.iv).execute(foto);
        }
    }

Don't forget to correctly encapsulate the class and methods. Hope it helps.

Junior Buckeridge
  • 2,075
  • 2
  • 21
  • 27
  • can you please give me a little more idea for caching algorithm ? – ramesh Jan 12 '14 at 03:37
  • check the edited answer. I suggest you a code I'm using right now. Feel free to make your modifications and improvements. Regards. – Junior Buckeridge Jan 12 '14 at 19:59
  • In the code I'm using the internal storage for cache. You may wanna use external storage if the files can be large. Also I set a default drawable resource before setting the correct image, so if it takes a few seconds, the user can see something and not a white space or an old image. – Junior Buckeridge Jan 12 '14 at 20:05
0

I had this random pictures problem with my arrayAdapter. Then I used Picasso which takes care of imageView reusing in listview: http://square.github.io/picasso/

Example of code:

    Picasso.with(mContext)
            .load(url)
            .resize(size, size)
            .centerCrop()
            .placeholder(R.drawable.image_holder)
            .into(holder.imgDefaultImage);