0

This simple app gets a tag from the user and display in a ListView first 10 Flicker images that match the tag. I used this simple tutorial, i just modified it so it can work with flicker search, instead of hard coded links. It works but somehow it loads images one upon another in some views. So user sees that in a view image changes for a while. And if user scrolls, it reloads them again. If it's a pointer issue, i couldn't resolve it. I changed static ViewHolder to a non-static class, but nothing changed. Why it happens and how can i fix it?

Main class:

public class FlickerSearchMain extends AppCompatActivity implements AdapterView.OnItemClickListener, View.OnClickListener{
        private EditText tag;
        private Button send;
        private ListView listView;
        ArrayList<ListItem> listData;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_flicker_search_main);

            send = (Button) findViewById(R.id.send_button);
            send.setOnClickListener(this);  
            tag = (EditText) findViewById(R.id.tag_input);

            //ArrayList<ListItem> listData = getListData();
            listView = (ListView) findViewById(R.id.list);
            listView.setOnItemClickListener(this);


        }

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

            int itemPosition = position;
            Toast.makeText(getApplicationContext(),
                    "Position :"+ itemPosition, Toast.LENGTH_SHORT)
                    .show();
        }


        @Override
        public void onClick(View v) {
            if(v == send){
                String input = tag.getText().toString();
                new FlickerRequest(this).execute(input);
            }
        }


        private class FlickerRequest extends AsyncTask<String, Void, ArrayList<ListItem>>{
            ArrayList<ListItem> elements_list;
            Context context;

            public FlickerRequest(Context context){
                this.context = context;
                this.elements_list = new ArrayList<ListItem>();

            }

            @Override
            protected ArrayList<ListItem> doInBackground(String... params) {
                String tag = params[0];

                int SIZE = 10;
                URL flicker;
                URLConnection urlcon;
                BufferedReader in = null;

                try {
                    flicker = new URL("https://api.flickr.com/services/feeds/photos_public.gne?tags="
                            + tag + "&format=json");
                    urlcon = flicker.openConnection();
                    in = new BufferedReader(new InputStreamReader(urlcon.getInputStream()));

                    String inputLine;
                    StringBuilder strbuilder = new StringBuilder();

                    in.skip(15);

                    while ((inputLine = in.readLine()) != null)
                        strbuilder.append(inputLine);

                    in.close();

                    JSONObject job = new JSONObject(strbuilder.toString());
                    JSONArray items = job.getJSONArray("items");

                    for(int i=0; i<SIZE; i++) {
                        JSONObject item = items.getJSONObject(i);
                        ListItem  newdata = new ListItem();
                        newdata.setHeadline(item.getString("title"));
                        newdata.setAuthor(item.getString("author"));
                        newdata.setDate(item.getString("date_taken"));
                        newdata.setUrl(item.getJSONObject("media").getString("m"));

                        elements_list.add(newdata);
                    }

                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (JSONException e) {
                    e.printStackTrace();
                }

                return elements_list;
            }

            protected void onPostExecute(ArrayList<ListItem> result) {
                if(listData == null) {
                    listData = result;
                    listView.setAdapter(new FlickerAdapter(context, listData));
                }
                else{
                    listData = result;
                    ((BaseAdapter) listView.getAdapter()).notifyDataSetChanged();
                }

            }

        }
    }

Adapter Class:

    public class FlickerAdapter extends BaseAdapter {
        private ArrayList<ListItem> listData;
        private LayoutInflater layoutInflater;

        public FlickerAdapter(Context context, ArrayList<ListItem> listData) {
            this.listData = listData;
            layoutInflater = LayoutInflater.from(context);
        }

        @Override
        public int getCount() {
            return listData.size();
        }

        @Override
        public Object getItem(int position) {
            return listData.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if (convertView == null) {
                convertView = layoutInflater.inflate(R.layout.list_row_layout, null);
                holder = new ViewHolder();
                holder.headlineView = (TextView) convertView.findViewById(R.id.title);
                holder.authorView = (TextView) convertView.findViewById(R.id.author);
                holder.dateView = (TextView) convertView.findViewById(R.id.date);
                holder.imageView = (ImageView) convertView.findViewById(R.id.image);
                convertView.setTag(holder);

            } else {
                holder = (ViewHolder) convertView.getTag();
            }

            ListItem newsItem = listData.get(position);
            holder.headlineView.setText(newsItem.getHeadline());
            holder.authorView.setText("By, " + newsItem.getAuthor());
            holder.dateView.setText(newsItem.getDate());

            if (holder.imageView != null) {
                new ImageDownloaderTask(holder.imageView).execute(newsItem.getUrl());
            }

            return convertView;
        }

        static class ViewHolder {
            TextView headlineView;
            TextView authorView;
            TextView dateView;
            ImageView imageView;
        }
    }

AsynTask ImageLoader class

    class ImageDownloaderTask extends AsyncTask<String, Void, Bitmap> {
        private final WeakReference<ImageView> imageViewReference;

        public ImageDownloaderTask(ImageView imageView) {
            imageViewReference = new WeakReference<ImageView>(imageView);
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            return downloadBitmap(params[0]);
        }



        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (isCancelled()) {
                bitmap = null;
            }

            if (imageViewReference != null) {
                ImageView imageView = imageViewReference.get();
                if (imageView != null) {
                    if (bitmap != null) {
                        imageView.setImageBitmap(bitmap);
                    } else {
                        //Drawable placeholder = imageView.getContext().getResources().getDrawable(R.drawable.penguin);
                        //imageView.setImageDrawable(placeholder);
                    }
                }

            }
        }

        private Bitmap downloadBitmap(String url) {
            HttpURLConnection urlConnection = null;
            try {
                URL uri = new URL(url);
                urlConnection = (HttpURLConnection) uri.openConnection();

                int statusCode = urlConnection.getResponseCode();
                if (statusCode != HttpURLConnection.HTTP_OK) {
                    return null;
                }

                InputStream inputStream = urlConnection.getInputStream();
                if (inputStream != null) {
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                    return bitmap;
                }
            } catch (Exception e) {
                urlConnection.disconnect();
                Log.w("ImageDownloader", "Error downloading image from " + url);
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
            }
            return null;
        }
    }
user3717434
  • 215
  • 4
  • 19
  • 1
    That's because your cell are reused and the ```AsynkTask``` has a reference to a reused bitmap. You have to find a way to cancel the task when new item is created or remove the reference of the ImageView from ```AsyncTask```. – danypata May 12 '16 at 13:42
  • Use a image loader library like glide, it handles the asynchronous loading for you and caches images so they won´t load again when the user is scrolling – Christian Stengel May 12 '16 at 14:14

1 Answers1

0

You have to make sure that the task that is filling the ImageView is the most current task for that ImageView, because the task may not complete until after the ImageView has been recycled.

Here's what I did:

        if (holder.imageView != null) {
            Drawable placeholder = holder.imageView.getContext().getResources().getDrawable(R.drawable.penguin);
            holder.imageView.setImageDrawable(placeholder);

            ImageDownloaderTask task = new ImageDownloaderTask(holder.imageView);
            holder.imageView.setTag(R.id.tag_key, task);
            task.execute(newsItem.getUrl());
        }

...

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }

        if (bitmap != null && imageViewReference != null) {
            ImageView imageView = imageViewReference.get();
            if (imageView != null && imageView.getTag(R.id.tag_key) == this) {
                imageView.setImageBitmap(bitmap);
                imageView.setTag(R.id.tag_key, null);
            }
        }
    }

In your resources:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="tag_key" type="id" />
</resources>
kris larson
  • 30,387
  • 5
  • 62
  • 74
  • Thanks for the answer, but you can't assign return value of task.execute() to a task. I tested this code just changing this part as `ImgDTask task = new ImgDTask(holder.imageView); holder.imageView.setTag(R.id.tag_key, task.execute(newsItem.getUrl()));` but this time images get loaded very late and if i scroll they disappear and get loaded again lately. Seeing a placeholder instead of every image is a little bit ugly. – user3717434 May 14 '16 at 22:49
  • Good grief, I can't even write working code without an IDE. Updating the answer to the correct code. So yeah, it's not an ideal solution, but it does fix your problem of the wrong images showing up. Your next step would be to set up an LRUCache for the images, so you don't have to reload them when the user scrolls up again. Or you could just do what @ChristianStengel said: use Glide or Picasso and be done with it. – kris larson May 15 '16 at 19:40
  • Nobody can write working code without IDE :) Thanks anyway, i'll try it that way. – user3717434 May 16 '16 at 14:26
  • @krislarson can you answer this too? I don't want to learn picasso for now. I am learning new things https://stackoverflow.com/questions/44216331/image-thumbnails-not-setting-correctly – Pushan Gupta May 27 '17 at 11:43