2

The images loaded by this custom adapter placed at wrong positions i.e correct movie banner is not placed at correct list view item. and keeps on changing for a while. here is my custom adapter with ASYNCTASK which is loading images from URL

    import java.net.URL;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.List;

    import android.content.Context;
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.Uri;
    import android.os.AsyncTask;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ArrayAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;
    import androlizer.yify.torrent.R;
    import androlizer.yify.torrents.models.UpcomingMovieListModel;

    public class UpcomingMoviesCustomAdapter extends ArrayAdapter<UpcomingMovieListModel> {


        Context context;
        public UpcomingMoviesCustomAdapter(
                Context context, int resource, List<UpcomingMovieListModel> objects) {
            super(context, resource, objects);
            this.context = context;
        }

        static class ViewHolder
        {
            TextView movieTitle_textView;
            TextView uploader_textView;
            TextView date_textView;
            ImageView movie_icon_imageView;
            ImageView imdb_url_imageView;
        }

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

            final ViewHolder holder;
            // getting data
            final UpcomingMovieListModel movie = getItem(position);     

            if (convertView == null) 
            {
                convertView = View.inflate(context, R.layout.movie_upcoming_row, null);                
                holder = new ViewHolder();

                holder.movieTitle_textView = (TextView) convertView.findViewById(R.id.movie_upcoming_movie_title);
                holder.uploader_textView = (TextView) convertView.findViewById(R.id.movie_upcoming_uploader);
                holder.date_textView = (TextView) convertView.findViewById(R.id.movie_upcoming_date);
                holder.imdb_url_imageView = (ImageView)convertView.findViewById(R.id.movie_upcoming_imageView_imdblink);
                holder.movie_icon_imageView = (ImageView)convertView.findViewById(R.id.movie_upcoming_movie_image_view);
                convertView.setTag(holder);
            }
            else
            {
                holder = (ViewHolder)convertView.getTag();
            }

            if (movie != null) 
            {
                holder.movieTitle_textView.setText(movie.getM_title());
                holder.uploader_textView.setText(movie.getUploader());

                SimpleDateFormat origFormat= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                //Store it as a date object
                Date date = null;
                try {
                    date = origFormat.parse(movie.getDate_added());
                } catch (ParseException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                //Output it as a string that uses the new format
                SimpleDateFormat newFormat= new SimpleDateFormat("MMMMMMMMM dd, yyyy 'at' hh:mm a");

                String desiredDateFormat = newFormat.format(date);
                holder.date_textView.setText(desiredDateFormat);

                holder.imdb_url_imageView.setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(movie.getImdb_url())));
                    }
                }); 
            }

            new ImageLoader().execute(convertView.g, movie.getM_cover());
            return convertView;

        }

        public class ImageLoader extends AsyncTask<Object, String, Bitmap> {

            private View view;
            private Bitmap bitmap = null;

            @Override
            protected Bitmap doInBackground(Object... parameters) {

                // Get the passed arguments here
                view = (View) parameters[0];
                String uri = (String)parameters[1];

                // Create bitmap from passed in Uri here
                // ...
                try {
                    URL req = new URL(uri);
                    bitmap = BitmapFactory.decodeStream(req.openConnection()
                            .getInputStream());
                } catch (Exception e) {
                    // TODO: handle exception
                }
                return bitmap;
            }

            @Override
            protected void onPostExecute(Bitmap bitmap) {
                if (bitmap != null && view != null) {
                    ImageView splash = (ImageView) view.findViewById(R.id.movie_upcoming_movie_image_view);
                    splash.setImageBitmap(bitmap);          
                }
            }
        }
    }
Nitin Misra
  • 4,472
  • 3
  • 34
  • 52

2 Answers2

2

I think the problem is that you don't stop a previous ImageLoader when a list item is reused: when a list item is reused another ImageLoader is attached to it but without removing the previous that was attached to the same list item instance.

Because of this can happen that the first ImageLoader could finish its job after the last one that sets the wrong image. Also you need to cache the downloaded images otherwise already downloaded images will downloaded again.

The right thing to do(TM) should be to set the image bitmap in the holder instance related to the list item and instead to stop the loader, make it acts on the holder.

gipi
  • 2,432
  • 22
  • 25
  • how to stop `ImageLoader ` please tell – Nitin Misra Jun 13 '13 at 16:01
  • you can use the [cancel()](http://developer.android.com/reference/android/os/AsyncTask.html#cancel(boolean)) method of the `AsyncTask` class. – gipi Jun 13 '13 at 20:43
  • 2
    If you are interested in a ready-to-use solution use this http://square.github.io/picasso/ – gipi Jun 15 '13 at 07:26
  • hey thanx gipi buddy its very useful to me , when i got sufficient reputation i'll surely distribute to you thank u man – Nitin Misra Jun 16 '13 at 03:54
  • `if (!((movies.getParent_comment_id()).toString().equals("null"))) { LayoutParams params = new LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT ); params.setMargins(20, 0, 0, 0); holder.comments_linearLayout.setLayoutParams(params); }` this is the code in my custom adapter when initially list is build proper margin is applied to valid items when i scroll down and again scroll up all the rows in list shifts the margin left by 20 what i'm doing wrong please reply soon – Nitin Misra Jun 29 '13 at 05:55
0

What is happening here is that you are loading your images and setting them to the ImageView for your current list item, but not storing that Bitmap anywhere else. ListItems in Android are reused so that when you scroll, the device doesn't have to store many ListItems in memory. As a result, the OS will set new images to your list items as you scroll, which won't be the ones that you want.

The solution is to store your Bitmaps in the ArrayList that backs your ListView. In this case, it is List<UpcomingMovieListModel> objects that holds your data for the list view. There are several changes that need to be made here, so I'm not going to provide the full code, but I will describe the process.

  1. Extend your UpcomingMovieListModel object to have a field called movieIcon.
  2. Pass the current UpcomingMovieListModel object to your AsyncTask and have the AsyncTask set movieIcon to the downloaded image.
  3. In your getView method, set the ImageView to the value of movie.movieIcon if it is not null.
  4. In onPostExecute for your AsyncTask call this.notifyDataSetChanged() after you set movieIcon to the newly downloaded image. This tells the ListView that you have changed the underlying data, and it should refresh to show the icon for the current movie in each item on the screen.
  5. Do not call setImageBitmap directly in onPostExecute. This will be taken care of in getView when you notify that the dataset has changed.

If you change these things, you should see all the right images, because you'll be storing them correctly for the underlying data.

mattgmg1990
  • 5,576
  • 4
  • 21
  • 26
  • movie.getM_cover() contains the url of the image – Nitin Misra Jun 13 '13 at 15:41
  • what is `movieIcon` is this contain Image Url or Image itself shall i provide `UpcomingMovieListModel ` – Nitin Misra Jun 13 '13 at 15:57
  • I am proposing that you add a completely new field to `UpcomingMovieListModel` that is of type Bitmap. As in `public Bitmap movieIcon;` – mattgmg1990 Jun 13 '13 at 17:24
  • hey matt thanx for help buddy What i have done now i load the images in activity itself then pass them to model and ultimately my custom adapter sets it into `imageView` thanx for suggesting me that i must have field `movieIcon` in my model class. – Nitin Misra Jun 14 '13 at 04:01
  • `if (!((movies.getParent_comment_id()).toString().equals("null"))) { LayoutParams params = new LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT ); params.setMargins(20, 0, 0, 0); holder.comments_linearLayout.setLayoutParams(params); }` – Nitin Misra Jun 29 '13 at 05:51
  • this is the code in my custom adapter when initially list is build proper margin is applied to valid items when i scroll down and again scroll up all the rows in list shifts the margin left by 20 what i'm doing wrong please reply soon – Nitin Misra Jun 29 '13 at 05:53