自定义适配器不更新gridview

时间:2015-11-11 16:15:30

标签: android gridview custom-adapter

我正在编写一个应用程序,用于从MovieDB api中提取电影数据,并在GridView中显示信息。

当应用程序首次加载时,我会期望视图最初填充,但事实并非如此。我在菜单栏中有一个排序选项,当第一次设置排序选项时,GridView按照流行度顺序填充,就像它最初应该一样,但不管实际选择了什么排序标准。

我已经使用日志来确定从API检索正确的数据并正确处理,因此我必须假设适配器没有正确更新视图。

为什么视图最初没有显示或更新应该如何?

FilmFragment.java:

public class FilmFragment extends Fragment {

private ArrayList<FilmParcelable> filmParcels = new ArrayList<FilmParcelable>();

private ImageAdaptor mFilmAdaptor;

protected String[] sortOptions = {
        "popularity.desc",
        "vote_average.desc"
};

protected String sortBy = sortOptions[0];

private final String LOG_TAG = FilmFragment.class.getSimpleName();

public FilmFragment() {
}

@Override
public void onCreate(Bundle savedInstanceState){

    super.onCreate(savedInstanceState);

    if (savedInstanceState == null || !savedInstanceState.containsKey("films")){
        updateFilms();
        mFilmAdaptor = new ImageAdaptor(getActivity(),filmParcels);
    } else {
        filmParcels = savedInstanceState.getParcelableArrayList("films");
        mFilmAdaptor = new ImageAdaptor(getActivity(),filmParcels);
    }
    // allow fragment to handle menu events
    setHasOptionsMenu(true);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
    inflater.inflate(R.menu.filmfragment, menu);
}

public boolean onOptionsItemSelected(MenuItem item){
    //Handle action bar item clicks. The action bar will
    //automatically handle clicks on the Home/Up button, so long
    //as you specify a parent activity in AndroidManifest.xml
    int id = item.getItemId();
    if (id == R.id.action_sort){
        showSortDialog();
        return true;
    }

    return super.onOptionsItemSelected(item);
}

@Override
public void onSaveInstanceState(Bundle outState){
    outState.putParcelableArrayList("films", filmParcels);
    super.onSaveInstanceState(outState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View rootView = inflater.inflate(R.layout.fragment_main, container, false);

    // Find GridView to populate with poster images
    GridView gridView = (GridView) rootView.findViewById(R.id.gridView);

    // Set the adaptor of the GridView to my ImageAdaptor
    gridView.setAdapter(mFilmAdaptor);
    updateAdaptor();

    return rootView;
}

// Update movie data in case there is a change in the "sort by" option
// Or the fragment is started with no saved data

public void updateFilms(){
    new FetchFilmTask().execute();
}

public void updateAdaptor(){
    mFilmAdaptor.clear();
    mFilmAdaptor.addAll(filmParcels);
    mFilmAdaptor.notifyDataSetChanged();
}

// Show dialog sort pop up
public void showSortDialog(){
    DialogFragment dialog = new SortDialog();
    dialog.setTargetFragment(this, 0);
    dialog.show(getActivity().getSupportFragmentManager(), "SortDialog");
}

// If a fragment or activity called by this fragment returns to this fragment,
// Get the information returned via the intent
public void onActivityResult(int requestCode, int resultCode, Intent data){

   if (requestCode == 0){
       int mSelected = data.getIntExtra("Selected Option", -1);
       if (mSelected != -1){
           sortBy = sortOptions[mSelected];
           updateFilms();
           updateAdaptor();
       }
   }
}


// Class to get JSON data from The Movie Database API
public class FetchFilmTask extends AsyncTask<Void, Void, FilmParcelable[]> {

    private final String LOG_TAG = FetchFilmTask.class.getSimpleName();

    private final String MOVIE_DB_API_KEY = "e1968ef8ba074d7d5bf07a59de8b2310";

    protected FilmParcelable[] doInBackground(Void... params){
        // 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 raw JSON response as a string
        String movieDBStr = null;

        try {
            // Construct URL for Movie DB query
            Uri.Builder builder = new Uri.Builder();
            builder.scheme("http")
                    .authority("api.themoviedb.org")
                    .appendPath("3")
                    .appendPath("discover")
                    .appendPath("movie")
                    .appendQueryParameter("api_key", MOVIE_DB_API_KEY)
                    .appendQueryParameter("sort_by", sortBy);


            String myUrl = builder.build().toString();
            Log.d(LOG_TAG, myUrl);

            URL url = new URL(myUrl);

            // Create the request to The Movie DB, 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){
                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) {
                return null;
            }
            movieDBStr = buffer.toString();
        } catch (IOException e){
            Log.e(LOG_TAG, "Error: ", e);
            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 getFilmDataFromJson(movieDBStr);
        } catch (JSONException e){
            Log.e(LOG_TAG, e.getMessage(), e);
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Take the String representing the complete forecast in JSON Format and
     * pull out the data we need to construct the Strings needed for the wireframes.
     *
     * Fortunately parsing is easy:  constructor takes the JSON string and converts it
     * into an Object hierarchy for us.
     */
    private FilmParcelable[] getFilmDataFromJson(String movieDBStr)
        throws JSONException {

        // JSON objects that need to be extracted
        final String MDB_RESULTS = "results";
        final String MDB_ID = "id";
        final String MDB_SYNOPSIS = "overview";
        final String MDB_RELEASE = "release_date";
        final String MDB_POSTER = "poster_path";
        final String MDB_TITLE = "title";
        final String MDB_RATING = "vote_average";

        JSONObject filmJson = new JSONObject(movieDBStr);
        JSONArray filmArray = filmJson.getJSONArray(MDB_RESULTS);

        FilmParcelable[] resultFilms = new FilmParcelable[filmArray.length()];
        for (int i = 0; i < filmArray.length(); i++){
            // Data needed by the FilmParcelable
            int id;
            String title;
            String releaseDate;
            String posterUrl;
            Double voteAverage;
            String plotSynopsis;

            JSONObject film = filmArray.getJSONObject(i);

            id = film.getInt(MDB_ID);
            plotSynopsis = film.getString(MDB_SYNOPSIS);
            releaseDate = film.getString(MDB_RELEASE);
            posterUrl = "http://image.tmdb.org/t/p/w300" + film.getString(MDB_POSTER);
            title = film.getString(MDB_TITLE);
            voteAverage = film.getDouble(MDB_RATING);
            Log.d(LOG_TAG, title);
            Log.d(LOG_TAG, posterUrl);

            resultFilms[i] = new FilmParcelable(id, title, releaseDate, posterUrl, voteAverage, plotSynopsis);
        }

        return resultFilms;
    }

    @Override
    protected void onPostExecute(FilmParcelable[] result){
        if (result != null){
            filmParcels = new ArrayList<>(Arrays.asList(result));
        }
    }

}

}

ImageAdaptor.java:

public class ImageAdaptor extends ArrayAdapter<FilmParcelable> {

public ImageAdaptor(Activity context, ArrayList<FilmParcelable> filmParcels){
    super(context, 0, filmParcels);
}

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

    Context context= getContext();
    View gridView;

    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    String mUrl = getItem(position).getUrl();

    if (convertView == null) {

        gridView = inflater.inflate(R.layout.gridview_film_layout, parent, false);

        // Find the image view from the gridview_film_layout
        ImageView posterView = (ImageView) gridView.findViewById(R.id.grid_item_image);

        // Set the image view to contain image located at mUrl
        Picasso.with(getContext()).load(mUrl).into(posterView);
    } else {
        gridView = convertView;
    }

    return gridView;
}

}

2 个答案:

答案 0 :(得分:1)

相应于您发布的代码,您将在GridView的同一单元格中返回。你应该有这两行

ImageView posterView = (ImageView) gridView.findViewById(R.id.grid_item_image);
Picasso.with(getContext()).load(mUrl).into(posterView);
来自if / else后卫的

 if (convertView == null) {
    // inflate
 } else {
   // gridView = convertView;
 }

  ImageView posterView = (ImageView) gridView.findViewById(R.id.grid_item_image);
  Picasso.with(getContext()).load(mUrl).into(posterView);
  return gridView;

答案 1 :(得分:0)

我在填充用于适配器的arraylist后尝试更新适配器。然而,arraylist在后台填充和更新,所以代码:

updateFilms();
updateAdaptor();

导致适配器在数据在后台加载完成之前更新。

确定Blackbelt的解决方案是正确的。