Medes
Medes

Reputation: 48

Custom Adapter doesn't always update correctly

I seem to have a problem with my ImageAdapter. It should update a GridView but does not do it.

Code:

public class MainActivityFragment extends Fragment {

    ImageAdapter imageAdapter;
    GridView gridView;

    public MainActivityFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_main, container, false);
        gridView = (GridView) rootView.findViewById(R.id.gridView);
        imageAdapter = new ImageAdapter(getActivity(),new ArrayList<Movie>());
        gridView.setAdapter(imageAdapter);
        return rootView;
    }

    @Override
    public void onStart() {
        super.onStart();
        UpdateMovies();
    }

    private void UpdateMovies() {
        FetchMoviesTask task = new FetchMoviesTask();
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
        String sort = preferences.getString(getString(R.string.pref_sort_key), getString(R.string.pref_sort_default));
        task.execute(sort);
    }

    public class ImageAdapter extends BaseAdapter {
        private Context mContext;
        private ArrayList<Movie> data;


        public ImageAdapter(Context c, ArrayList<Movie> movies) {
            mContext = c;
            data = movies;
        }

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

        public Object getItem(int position) {
            return null;
        }

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

        // create a new ImageView for each item referenced by the Adapter
        public View getView(int position, View convertView, ViewGroup parent) {
            LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(mContext.LAYOUT_INFLATER_SERVICE);
            View gridView;
            ImageView imageView;
            if (convertView == null) {
                // if it's not recycled, initialize some attributes
                gridView = new GridView(mContext);
                gridView = inflater.inflate(R.layout.grid_item_poster, null);
                imageView = (ImageView)gridView.findViewById(R.id.poster);
            } else {
                imageView = (ImageView) convertView;
            }
            if (data.size() != 0) {
                Log.v("IMAGE_ADAPTER","SetView= " + data.get(position).poster);
                Picasso.with(mContext).load(data.get(position).poster).into(imageView);
            }
            return imageView;
        }

        public void updatePosters(ArrayList<Movie> newMovies) {
            data.clear();
            data.addAll(newMovies);
            this.notifyDataSetChanged();
        }
    }
    public class FetchMoviesTask extends AsyncTask<String, Void, ArrayList<Movie>> {

        private final String LOG_TAG = FetchMoviesTask.class.getName();


        private ArrayList<Movie> getMovieDataFromJson(String movieJsonStr, int numDays)
                throws JSONException {

            // These are the names of the JSON objects that need to be extracted.
            final String MOV_LIST = "results";
            final String MOV_ID = "id";
            final String MOV_TITLE = "original_title";
            final String MOV_OVERVIEW = "overview";
            final String MOV_RATING = "vote_average";
            final String MOV_DATE = "release_date";
            final String MOV_POSTER = "poster_path";

            JSONObject listJson = new JSONObject(movieJsonStr);
            JSONArray movieArray = listJson.getJSONArray(MOV_LIST);

            ArrayList<Movie> movies = new ArrayList<>();
            for(int i = 0; i < movieArray.length(); i++) {

                int id;
                String title;
                String overview;
                String rating;
                String date;
                String poster;

                // Get the JSON object representing the day
                JSONObject movie = movieArray.getJSONObject(i);

                id = movie.getInt(MOV_ID);
                title = movie.getString(MOV_TITLE);
                overview = movie.getString(MOV_OVERVIEW);
                rating = movie.getString(MOV_RATING);
                date = movie.getString(MOV_DATE);
                poster = movie.getString(MOV_POSTER);

                Movie newMovie = new Movie(id, title, overview, rating, date, poster);

                movies.add(newMovie);
            }

            for (Movie s : movies) {
                Log.v(LOG_TAG, "Movie entry: " + s.print());
            }
            return movies;

        }

        @Override
        protected void onPostExecute(ArrayList<Movie> result) {
            if(result != null){
                Log.v(LOG_TAG,"DATA SET CHANGED! SIZE= " + result.size());
                imageAdapter.updatePosters(result);
            }
        }

        @Override
        protected ArrayList<Movie> doInBackground(String... params) {

            if(params.length == 0){
                return null;
            }

            // These two need to be declared outside the try/catch
            // so that they can be closed in the finally block.
            HttpURLConnection urlConnection = null;
            BufferedReader reader = null;

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

            String format = "JSON";
            String units = "metric";
            String apiKey = "********************";
            int numDays = 7;

            try {
                // Construct the URL for the OpenWeatherMap query
                // Possible parameters are avaiable at OWM's forecast API page, at
                // http://openweathermap.org/API#forecast
                final String MOVIE_BASE_URL = "http://api.themoviedb.org/3/discover/movie?";
                final String SORT_PARAM = "sort_by";
                final String DESC = ".desc";
                final String API_PARAM = "api_key";

                Uri builtUri = Uri.parse(MOVIE_BASE_URL).buildUpon()
                        .appendQueryParameter(SORT_PARAM,(params[0]+DESC))
                        .appendQueryParameter(API_PARAM,apiKey)
                        .build();

                URL url = new URL(builtUri.toString());

                Log.v(LOG_TAG, "Built URI " + builtUri.toString());

                // Create the request to OpenWeatherMap, and open the connection
                urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setRequestMethod("GET");
                urlConnection.connect();

                // Read the input stream into a String
                InputStream inputStream = urlConnection.getInputStream();
                StringBuffer buffer = new StringBuffer();
                if (inputStream == null) {
                    // 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) {
                    // Stream was empty.  No point in parsing.
                    return null;
                }
                movieJsonStr = buffer.toString();

                Log.v(LOG_TAG,"Movie JSON String: " + movieJsonStr);
            } catch (IOException e) {
                Log.e(LOG_TAG, "Error ", e);
                // If the code didn't successfully get the weather data, there's no point in attemping
                // to parse it.
                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);
                    }
                }
            }
            try {
                return getMovieDataFromJson(movieJsonStr, numDays);
            }catch (JSONException e){
                Log.e(LOG_TAG, e.getMessage(), e);
                e.printStackTrace();
            }
            return null;
        }
    }
}

fragment_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivityFragment">

<GridView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/gridView"
    android:columnWidth="180dp"
    android:gravity="center"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth"/>

grid_item_poster.xml

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="120dp"
    android:scaleType="centerCrop"
    android:id="@+id/poster"
    android:adjustViewBounds="true">

</ImageView>

The strange thing is that when I add a ArrayList with elements when I create my ImageAdapter in onCreateView, it does work but when I leave that list unpopulated it does not update.

Anyone got any clue what to do? Been searching for the solution the whole day now. :D

Thanks in advance.

Upvotes: 0

Views: 106

Answers (2)

Krzysztof Skrzynecki
Krzysztof Skrzynecki

Reputation: 2525

I have weird impression that something is wrong with your getView() method - I can be wrong but try this one:

public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(mContext.LAYOUT_INFLATER_SERVICE);
    ImageView imageView;
    if (convertView == null) {
        convertView = inflater.inflate(R.layout.grid_item_poster, null);
        imageView = (ImageView) convertView.findViewById(R.id.poster);
        convertView.setTag(imageView);
    } else {
        imageView = (ImageView) convertView.getTag();
    }
    if (data.size() != 0 && data.get(position) != null) {
        Log.v("IMAGE_ADAPTER","SetView= " + data.get(position).poster);
        Picasso.with(mContext).load(data.get(position).poster).into(imageView);
    } else {  
      //I think that you should reset image when there is a issue with data set
      imageView.setImageResource(-1);
    }
    return imageView;
}

Moreover this comment - create a new ImageView for each item referenced by the Adapter is invalid because adapter reuse views, which are outside of the screen, so sometimes instead of creating new instance of ImageView, list displays old one with new data

Edit

Try to create new data list instead of cleaning old one:

    public void updatePosters(ArrayList<Movie> newMovies) {
        data = new ArrayList<Movies>();
        data.addAll(newMovies);
        this.notifyDataSetChanged();
    }

Upvotes: 1

Marius Constantin
Marius Constantin

Reputation: 178

You getView is not correctly implemented. Firs of all it is very weird what you are trying to achieve there, you are inflating something just to get an ImageView from that layout. You could instead instantiate the ImageView inside the method:

public View getView(int position, View convertView, ViewGroup parent) {
    View view=convertView;
    if (view == null) {
        view=new ImageView(mContext);
        view.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout
            .LayoutParams.MATCH_PARENT,LinearLayout
            .LayoutParams.WRAP_CONTENT));
        view.setScaleType(ImageView.ScaleType.CENTER_CROP);

    }
    final String url=data.get(position).poster;
    if (!TextUtis.isEmpty(url) {
        Log.v("IMAGE_ADAPTER","SetView= " + url);
        Picasso.with(mContext).load(url).into(((ImageView)view));
    } else {
        Picasso.with(mContext).load(android.R.color.transparent).into(((ImageView)view);
    }
    return view;
}

Upvotes: 0

Related Questions