Ishita Sinha
Ishita Sinha

Reputation: 2294

Unable to display images on a GridView

The first activity on my app shows the thumbnails of some popular movies. I am pulling this data from The Movie DB API. I am being able to download the image URLs and other details fine. However, I am unable to display the images on the GridView on my app. I am trying to write a custom ImageAdapter class to display images using Picasso library. Here is the code for that. The sections that have been commented out are the ones that I have tried and failed:

public class ImageAdapter extends BaseAdapter {
private LayoutInflater inflater;
private int mLayout;
private int mId;
private List<?> mImageList = new ArrayList<>();

public ImageAdapter(LayoutInflater i, int layout, int id, List<?> images) {
    inflater = i;
    mLayout = layout;
    mId = id;
    mImageList = images;
}

public int getCount() {
    return mImageList.size();
}

public Object getItem(int position) {
    return mImageList.get(position);
}

public long getItemId(int position) {
    return position;
}
//TODO check errors in the section below.
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
    /*View view;
    ImageView imageView;
    if (convertView == null) {
        view = inflater.inflate(mLayout, parent, false);
    } else {
        view = convertView;
    }
    try {
        if (mId == 0) {
            //  If no custom field is assigned, assume the whole resource is an ImageView.
            imageView = (ImageView) view;
        } else {
            //  Otherwise, find the ImageView field within the layout
            imageView = (ImageView) view.findViewById(mId);
        }
    } catch (ClassCastException e) {
        Log.e("ImageAdapter", "You must supply a resource ID for an ImageView");
        throw new IllegalStateException(
                "ImageAdapter requires the resource ID to be an ImageView", e);
    }

    Log.v("ImageAdapter", "Inside ImageAdapter.");
    Log.v("ImageAdapter",mImageList.get(position).toString());
Picasso.with(inflater.getContext()).load(mImageList.get(position).toString()).into(imageView);
   /* Uri uri = Uri.parse(mImageList.get(position).toString());
    imageView.setImageURI(uri);*/

    ImageView imageView;
    if (convertView == null) {
        // if it's not recycled, initialize some attributes
        imageView = new ImageView(inflater.getContext());
        imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setPadding(8, 8, 8, 8);
    } else {
        imageView = (ImageView) convertView;
    }

    Log.v("ImageAdapter",mImageList.get(position).toString());

    notifyDataSetChanged();
    Picasso.with(inflater.getContext()).load(mImageList.get(position).toString()).into(imageView);

    return imageView;

    /*if (convertView == null) {
        // if it's not recycled, initialize some attributes
        imageView = (ImageView) convertView.findViewById(mId);
        imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setPadding(8, 8, 8, 8);
    } else {
        imageView = (ImageView) convertView;
    }*/

    //return imageView;
}

}

And here is the class where I am getting the data from the API:

public class MainActivityFragment extends Fragment {

List<Uri> posterURLs = new ArrayList<Uri>();
ImageAdapter populater;
String[] title, overview, popularity, rating, releaseDate;

public MainActivityFragment() {
}

@Override
public void onStart() {
    super.onStart();
    FetchMovieData data = new FetchMovieData();
    data.execute();
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_main, container, false);

    GridView gridview = (GridView) rootView.findViewById(R.id.gridview);
    populater = new ImageAdapter(inflater,R.layout.image_view_poster,R.id.one_poster,posterURLs);//TODO handle this in ImageAdapter
    gridview.setAdapter(populater);

    gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View v,
                                int position, long id) {
            Toast.makeText(getActivity(), "" + position,
                    Toast.LENGTH_SHORT).show();
        }
    });
    return rootView;
}

public class FetchMovieData extends AsyncTask<Void, Void, String[]> {

    //This is used so that if the name of this class is ever changed, the LOG_TAG will
    //change accordingly to reflect that. This way, we don't hardcode the name of the
    //class in the Log messages.
    private final String LOG_TAG = FetchMovieData.class.getSimpleName();


    /**
     * Prepare image URL for presentation.
     */
    private String formatURL(String relativeURL) {
        String imageBaseURL = "http://image.tmdb.org/t/p/";
        String size = "w185";
        relativeURL = relativeURL.substring(1);
        Uri uri = Uri.parse(imageBaseURL).buildUpon()
                .appendPath(size)
                .appendPath(relativeURL).build();
        return uri.toString();
    }

    /**
     * Take the String representing the complete JSON data and
     * pull out the data we need to construct the Strings needed for the wireframes.
     */
    private String[] getMovieDataFromJson(String movieJsonStr)
            throws JSONException {

        // These are the names of the JSON objects that need to be extracted.
        final String RESULT_LIST = "results";
        final String TITLE = "original_title";
        final String POSTER_URL = "poster_path";
        final String OVERVIEW = "overview";
        final String POPULARITY = "popularity";
        final String RATING = "vote_average";
        final String RELEASE_DATE = "release_date";

        JSONObject allMovieData = new JSONObject(movieJsonStr);
        JSONArray resultsArray = allMovieData.getJSONArray(RESULT_LIST);

        String[] posterPaths = new String[resultsArray.length()];
        title = new String[resultsArray.length()];
        overview = new String[resultsArray.length()];
        popularity = new String[resultsArray.length()];
        rating = new String[resultsArray.length()];
        releaseDate = new String[resultsArray.length()];

        for(int i = 0; i < resultsArray.length(); i++) {

            // Get the JSON object representing one movie's details
            JSONObject eachMovie = resultsArray.getJSONObject(i);

            title[i] = eachMovie.getString(TITLE);
            String relativeURL = eachMovie.getString(POSTER_URL);
            posterPaths[i] = formatURL(relativeURL);
            overview[i] = eachMovie.getString(OVERVIEW);
            popularity[i] = eachMovie.getString(POPULARITY);
            rating[i] = eachMovie.getString(RATING);
            releaseDate[i] = eachMovie.getString(RELEASE_DATE);
            Log.v("poster path", posterPaths[i]);
        }

        return posterPaths;

    }

    @Override
    protected String[] doInBackground(Void... params) {
        HttpURLConnection urlConnection = null;
        BufferedReader reader = null;

        //For building the URL for the weather query from OpenWeatherMap
        final String BASE_URL = "http://api.themoviedb.org/3/discover/movie?";
        final String SORT_PARAM = "sort_by";
        final String API_PARAM = "api_key";

        String sort_by = "popularity.desc",
                apiKey = "";

        // Will contain the raw JSON response as a string.
        String movieJsonStr = null;

        try {
            // Construct the URL for the OpenWeatherMap query
            // Possible parameters are available at OWM's forecast API page, at
            // http://openweathermap.org/API#forecast
            Uri queryUri = Uri.parse(BASE_URL).buildUpon()
                    //.appendQueryParameter(SORT_PARAM,params[0])
                    .appendQueryParameter(SORT_PARAM, sort_by)
                    .appendQueryParameter(API_PARAM,apiKey).build();

            URL queryUrl = new URL(queryUri.toString());
            // Create the request to TheMovieDB, and open the connection
            urlConnection = (HttpURLConnection) queryUrl.openConnection();
            urlConnection.setRequestMethod("GET");
            urlConnection.connect();

            // Read the input stream into a String
            InputStream inputStream = urlConnection.getInputStream();
            StringBuffer buffer = new StringBuffer();
            if (inputStream == null) {
                Log.v(LOG_TAG, "Couldn't open input stream.");
                // Nothing to do.
                return null;
            }
            reader = new BufferedReader(new InputStreamReader(inputStream));

            String line;
            while ((line = reader.readLine()) != null) {
                // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
                // But it does make debugging a *lot* easier if you print out the completed
                // buffer for debugging.
                buffer.append(line + "\n");
            }

            if (buffer.length() == 0) {
                Log.v(LOG_TAG, "Input stream was empty.");
                // Stream was empty.  No point in parsing.
                return null;
            }

            //if all's well, parse the required data and return it to the system
            //(which then calls the onPostExecute() method with this data).
            movieJsonStr = buffer.toString();
            Log.v(LOG_TAG,movieJsonStr);
            return getMovieDataFromJson(movieJsonStr);
            //return getWeatherDataFromJson(forecastJsonStr, numDays);

        } catch (IOException e) {
            Log.e(LOG_TAG, "Error: Couldn't get movie data. ", e);
            // If the code didn't successfully get the weather data, there's no point in attempting
            // to parse it.
            return null;
        } catch (JSONException e) {
            Log.e(LOG_TAG, "Error in parsing: ", e);
            //If there is an error in parsing the JSON data, there's nothing to display.
            return null;
        }
        finally
        {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (final IOException e) {
                    Log.e(LOG_TAG, "Error closing stream", e);
                }
            }
        }

    }

    @Override
    protected void onPostExecute(String[] strings) {
        super.onPostExecute(strings);
        for (int i=0; i<strings.length;i++){
            Uri uri = Uri.parse(strings[i]);
            posterURLs.add(uri);
        }
    }
}

}

And here is the log from LogCat from when the app launches:

01-14 20:23:46.338 1176-1176/com.example.ishita.popularmovies I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@3d81b4d8 time:321945330
01-14 20:23:46.876 1176-2146/com.example.ishita.popularmovies V/FetchMovieData: {"page":1,"results":[{"poster_path":"\/fYzpM9GmpBlIC893fNjoWCwE24H.jpg","adult":false,"overview":"Thirty years after defeating the Galactic Empire, Han Solo and his allies face a new threat from the evil Kylo Ren and his army of Stormtroopers.","release_date":"2015-12-18","genre_ids":[28,12,878,14],"id":140607,"original_title":"Star Wars: The Force Awakens","original_language":"en","title":"Star Wars: The Force Awakens","backdrop_path":"\/njv65RTipNSTozFLuF85jL0bcQe.jpg","popularity":87.856903,"vote_count":2172,"video":false,"vote_average":7.92},{"poster_path":"\/oXUWEc5i3wYyFnL1Ycu8ppxxPvs.jpg","adult":false,"overview":"In the 1820s, a frontiersman, Hugh Glass, sets out on a path of vengeance against those who left him for dead after a bear mauling.","release_date":"2015-12-25","genre_ids":[37,18,12,53],"id":281957,"original_title":"The Revenant","original_language":"en","title":"The Revenant","backdrop_path":"\/6vb1S6H3FD6UQCjza78TptPB8GL.jpg","popularity":34.612251,"vote_count":360,"video":false,"vote_average":7.01},{"poster_path":"\/5aGhaIHYuQbqlHWvWYqMCnj40y2.jpg","adult":false,"overview":"During a manned mission to Mars, Astronaut Mark Watney is presumed dead after a fierce storm and left behind by his crew. But Watney has survived and finds himself stranded and alone on the hostile planet. With only meager supplies, he must draw upon his ingenuity, wit and spirit to subsist and find a way to signal to Earth that he is alive.","release_date":"2015-10-02","genre_ids":[18,12,878],"id":286217,"original_title":"The Martian","original_language":"en","title":"The Martian","backdrop_path":"\/sy3e2e4JwdAtd2oZGA2uUilZe8j.jpg","popularity":30.878855,"vote_count":1908,"video":false,"vote_average":7.66},{"poster_path":"\/jjBgi2r5cRt36xF6iNUEhzscEcb.jpg","adult":false,"overview":"Twenty-two years after the events of Jurassic Park, Isla Nublar now features a fully functioning dinosaur theme park, Jurassic World, as originally envisioned by John Hammond.","release_date":"2015-06-12","genre_ids":[28,12,878,53],"id":135397,"original_title":"Jurassic World","original_language":"en","title":"Jurassic World","backdrop_path":"\/dkMD5qlogeRMiEixC4YNPUvax2T.jpg","popularity":25.952499,"vote_count":3532,"video":false,"vote_average":6.76},{"poster_path":"\/kqjL17yufvn9OVLyXYpvtyrFfak.jpg","adult":false,"overview":"An apocalyptic story set in the furthest reaches of our planet, in a stark desert landscape where humanity is broken, and most everyone is crazed fighting for the necessities of life. Within this world exist two rebels on the run who just might be able to restore order. There's Max, a man of action and a man of few words, who seeks peace of mind following the loss of his wife and child in the aftermath of the chaos. And Furiosa, a woman of action and a woman who believes her path to survival may be achieved if she can make it across the desert back to her childhood homeland.","release_date":"2015-05-14","genre_ids":[53,28,12],"id":76341,"original_title":"Mad Max: Fury Road","original_language":"en","title":"Mad Max: Fury Road","backdrop_path":"\/tbhdm8UJAb4ViCTsulYFL3lxMCd.jpg","popularity":22.542466,"vote_count":3268,"video":false,"vote_average":7.52},{"poster_path":"\/nBNZadXqJSdt05SHLqgT0HuC5Gm.jpg","adult":false,"overview":"Interstellar chronicles the adventures of a group of explorers who make use of a newly discovered wormhole to surpass the limitations on human space travel and conquer the vast distances involved in an interstellar voyage.","release_date":"2014-11-05","genre_ids":[12,18,878],"id":157336,"original_title":"Interstellar","original_language":"en","title":"Interstellar","backdrop_path":"\/xu9zaAevzQ5nnrsXN6JcahLnG4i.jpg","popularity":19.138919,"vote_count":4090,"video":false,"vote_average":8.26},{"poster_path":"\/fqe8JxDNO8B8QfOGTdjh6sPCdSC.jpg","adult":false,"overview":"Bounty hunters seek shelter from a raging blizzard and get caught up in a plot of betrayal and deception.","release_date":"2015-12-25","genre_ids":[53,37,18,9648],"id":273248,"original_title":"The Hateful Ei
01-14 20:23:46.886 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/fYzpM9GmpBlIC893fNjoWCwE24H.jpg
01-14 20:23:46.886 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/oXUWEc5i3wYyFnL1Ycu8ppxxPvs.jpg
01-14 20:23:46.886 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/5aGhaIHYuQbqlHWvWYqMCnj40y2.jpg
01-14 20:23:46.886 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/jjBgi2r5cRt36xF6iNUEhzscEcb.jpg
01-14 20:23:46.886 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/kqjL17yufvn9OVLyXYpvtyrFfak.jpg
01-14 20:23:46.886 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/nBNZadXqJSdt05SHLqgT0HuC5Gm.jpg
01-14 20:23:46.886 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/fqe8JxDNO8B8QfOGTdjh6sPCdSC.jpg
01-14 20:23:46.886 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/pEbyD5ZtnhYXbpwJQ8pkaMOzlcB.jpg
01-14 20:23:46.886 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/q0R4crx2SehcEEQEkYObktdeFy.jpg
01-14 20:23:46.886 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/z2sJd1OvAGZLxgjBdSnQoLCfn3M.jpg
01-14 20:23:46.886 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/5JU9ytZJyR3zmClGmVm9q4Geqbd.jpg
01-14 20:23:46.887 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/cWERd8rgbw7bCMZlwP207HUXxym.jpg
01-14 20:23:46.887 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/D6e8RJf2qUstnfkTslTXNTUAlT.jpg
01-14 20:23:46.887 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/vgAHvS0bT3fpcpnJqT6uDTUsHTo.jpg
01-14 20:23:46.887 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/vQ7oVX2j7BnMDYyzuFeG1epqvGb.jpg
01-14 20:23:46.887 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/noUp0XOqIcmgefRnRZa1nhtRvWO.jpg
01-14 20:23:46.887 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/mSvpKOWbyFtLro9BjfEGqUw5dXE.jpg
01-14 20:23:46.887 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/p2SdfGmQRaw8xhFbexlHL7srMM8.jpg
01-14 20:23:46.887 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/y31QB9kn3XSudA15tV7UWQ9XLuW.jpg
01-14 20:23:46.888 1176-2146/com.example.ishita.popularmovies V/poster path: http://image.tmdb.org/t/p/w185/aAmfIX3TT40zUHGcCKrlOZRKC7u.jpg

As you can see, no log is being printed from the ImageAdapter even though I have Log.v messages there. I am new to android and cannot figure out what's happening. Someone, please help me!

Thanks!

EDIT:

Hi, All,

Thanks for all your answers. I removed the notifyDataSetChanged() from the ImageAdapter class and solved the problem by making the following changes to the ImageAdapter class:

public class ImageAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List<?> mImageList = new ArrayList<>();

public ImageAdapter(LayoutInflater i, List<?> images) {
    inflater = i;
    mImageList = images;
}

public int getCount() {
    return mImageList.size();
}

public Object getItem(int position) {
    return mImageList.get(position);
}

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

public View getView(int position, View convertView, ViewGroup parent) {
    ImageView imageView;
    if (convertView == null) {
        // if it's not recycled, create a new ImageView
        imageView = new ImageView(inflater.getContext());
    } else {
        imageView = (ImageView) convertView;
    }

    Picasso.with(inflater.getContext()).load(mImageList.get(position).toString()).into(imageView);
    imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
    imageView.setAdjustViewBounds(true);
    return imageView;
}
}

Although this shows the images on the UI, it takes a very long time to show up the images. It takes around 20 seconds before I can see any pictures at all, and sometimes it even doesn't show the pictures, although I can see in the logs that the picture URLs are being fetched correctly. My network is not slow, and the logs show the URLs almost immediately. Is this normal?

Upvotes: 2

Views: 850

Answers (2)

tiny sunlight
tiny sunlight

Reputation: 6251

Remove notifyDataSetChanged() in getView() and add populater.notifyDataSetChanged() to end of onPostExecute.

If it doesn't work.

Move

populater = new ImageAdapter(inflater,R.layout.image_view_poster,R.id.one_poster,posterURLs);
gridview.setAdapter(populater); 

to end of onPostExecute.

Upvotes: 1

Will Evers
Will Evers

Reputation: 942

create and set the adapter/data in the onPostExecute of your async task

Upvotes: 1

Related Questions