在从JSON获取数据后通知适配器数据更改时,RecyclerView中的每个位置都设置了相同的数据

时间:2016-11-25 17:18:58

标签: android android-recyclerview notifydatasetchanged

我从数据库中获取数据并正确获取JSON。此外,当我在RecyclerView上设置适配器时,除了在每个位置设置相同的数据外,一切似乎都能正常工作,但是我得到了具有20个不同值的完整ArrayList。我不知道我在哪里搞乱了notifyDatasetChanged,因为这是我第一次使用RecyclerView。

我在活动中使用片段,在代码中,我使用了Log语句(我使用了“############”而不是LOG_TAG,虽然< / em>)告诉我在调用onBindViewHolder时可以获得20个电影细节的完整arraylist。

以下是代码:

DefaultMovieFragment.java

public class DefaultMovieFragment extends Fragment implements LoaderManager.LoaderCallbacks<ArrayList<Movie>> {

private static final int DEFAULT_MOVIE_LOADER_ID = 1;
ArrayList<Movie> movies;
DefaultMovieAdapter mAdapter;
RecyclerView mRecyclerView;

public DefaultMovieFragment() {
    // Required empty public constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    Log.v("############", "onCreateView called");
    // Inflate the layout for this fragment
    View rootView = inflater.inflate(R.layout.fragment_default_movie, container, false);
    Movie movie = new Movie("ram", 2, "path");
    if (savedInstanceState==null){
        movies = new ArrayList<>();
    }

    //First of all check if network is connected or not then only start the loader
    ConnectivityManager connMgr = (ConnectivityManager)
            getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnected()) {

      /* fetch data. Get a reference to the LoaderManager, in order to interact with loaders. */
        startLoaderManager();
        Log.v("############", "startLoaderManager called");
    }

    // Lookup the recyclerview in activity layout
    mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerViewMovies);

    // Create mAdapter passing in the sample user data
    mAdapter = new DefaultMovieAdapter(getActivity(), movies);
    // Attach the mAdapter to the recyclerview to populate items
    mRecyclerView.setAdapter(mAdapter);

    // First param is number of columns and second param is orientation i.e Vertical or Horizontal
    final StaggeredGridLayoutManager gridLayoutManager =
            new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
    // Attach the layout manager to the recycler view
    mRecyclerView.setLayoutManager(gridLayoutManager);
    // That's all!

    return rootView;
}

private void startLoaderManager() {
    LoaderManager loaderManager = getLoaderManager();
    loaderManager.initLoader(DEFAULT_MOVIE_LOADER_ID, null, this);
    Log.v("############", "startLoaderManager finished");
}


@Override
public Loader<ArrayList<Movie>> onCreateLoader(int id, Bundle args) {
    Log.v("############", "onCreateLoader called");
    Uri baseUri = Uri.parse(UrlsAndConstants.DefaultQuery.DEFAULT_URL);
    Log.v("############", "baseUri is "+baseUri.toString());
    Uri.Builder uriBuilder = baseUri.buildUpon();
    Log.v("############", "uriBuilder is "+uriBuilder.toString());
    uriBuilder.appendQueryParameter(API_KEY_PARAM, API_KEY_PARAM_VALUE);
    Log.v("############", "uriBuilder.toString() is "+uriBuilder.toString());
    String urls = "https://api.themoviedb.org/3/discover/movie?api_key=4182aa25bab27d06344e404f65c4ae76";
    return new DefaultMovieLoader(getActivity().getApplicationContext(), urls);
}

@Override
public void onLoadFinished(Loader<ArrayList<Movie>> loader, ArrayList<Movie> movie) {
    Log.v("############", "startLoaderManager finished");
    if (movie.isEmpty()) {
        Log.v("******************", "movies isEmpty");
        return;
    } else {
        Log.v("############", "movies are"+movie);
        // Attach the mAdapter to the recyclerview to populate items

        mAdapter.setMovieData(movie);
        mRecyclerView.setAdapter(mAdapter);
    }
}

@Override
public void onLoaderReset(Loader<ArrayList<Movie>> loader) {
    Log.v("############", "onLoaderReset called");
}
}

DefaultMovieAdapter.java

public class DefaultMovieAdapter extends  RecyclerView.Adapter<DefaultMovieAdapter.ViewHolder>{

// Store a member variable for the movies
private ArrayList<Movie> mDefaultMovie;
// Store the context for easy access
private Context mContext;
private Movie currentMovie;

// Pass in the movies array into the constructor
public DefaultMovieAdapter(Context context, ArrayList<Movie> movies) {
    mDefaultMovie = movies;
    mContext = context;
}

// Easy access to the context object in the recyclerview
private Context getContext() {
    return mContext;
}
/*
 Provide a direct reference to each of the views within a data item
 Used to cache the views within the item layout for fast access
 */
public static class ViewHolder extends RecyclerView.ViewHolder {
    /*
    Your holder should contain a member variable
    for any view that will be set as you render a row
    */
    public final TextView movieTitleTextView;
    public final ImageView movieTitleImageView;
    /*
    We also create a constructor that accepts the entire item row
    and does the view lookups to find each subview
    */
    public ViewHolder(View itemView) {
        /*
        Stores the itemView in a public final member variable that can be used
        to access the context from any ViewHolder instance.
        */
        super(itemView);
        movieTitleTextView = (TextView) itemView.findViewById(R.id.grid_item_movie_title);
        movieTitleImageView = (ImageView) itemView.findViewById(R.id.grid_item_movie_image);
    }
}

@Override
public DefaultMovieAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View v = LayoutInflater.from(parent.getContext()).
            inflate(R.layout.item_movies, parent, false);
    return new ViewHolder(v);
}

@Override
public void onBindViewHolder(DefaultMovieAdapter.ViewHolder viewHolder, int position) {
    Log.v("############", "onBindViewHolder called");
    // Get the data model based on position
    currentMovie = mDefaultMovie.get(position);
    Log.v("############", "currentMovie called is "+currentMovie.toString());
    Log.v("############", "currentMovie's title is "+currentMovie.getMovieTitle().toString());
    /*
    Set item views based on your views and data model
    TextView textView = viewHolder.movieTitleTextView;
    */
    viewHolder.movieTitleTextView.setText(currentMovie.getMovieTitle());
    Log.v("############", "title is :>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+currentMovie.getMovieTitle());
    //ImageView button = viewHolder.movieTitleImageView;
    //viewHolder.movieTitleImageView.setImageResource(R.mipmap.ic_launcher);
    String url = "https://image.tmdb.org/t/p/w500/"+currentMovie.getMoviePosterPath().toString();
    Log.v("############", "poster path is :>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+currentMovie.getMoviePosterPath().toString());
    Picasso.with(getContext())
            .load(url)
            .placeholder(R.mipmap.ic_launcher)
            .into(viewHolder.movieTitleImageView);
}

@Override
public int getItemCount() {
    Log.v("############", "getItemCount called with size "+ mDefaultMovie.size());
    return mDefaultMovie.size();
}
public void setMovieData(ArrayList<Movie> weatherData) {
    Log.v("############", "setMovieData Called");
    mDefaultMovie = weatherData;
    Log.v("############", "mDefaultMovie is "+mDefaultMovie);
    notifyDataSetChanged();
    Log.v("############", "notifyDataSetChanged Finished");
}
}

DefaultMovieLoader.java

public class DefaultMovieLoader extends AsyncTaskLoader {

/**
 * Query URL
 */
private String mUrl;

/**
 * Constructs a new {@link DefaultMovieLoader}.
 *
 * @param context of the activity
 * @param url     to load data from
 */
public DefaultMovieLoader(Context context, String url) {
    super(context);
    mUrl = url;
    Log.v("############", "url is "+mUrl);
}

@Override
protected void onStartLoading() {
    forceLoad();
    Log.v("############", "onStartLoading called");
}

/**
 * This is on a background thread.
 */
@Override
public ArrayList<Movie> loadInBackground() {
    if (mUrl == null) {
        return null;
    }

    // Perform the network request, parse the response, and extract a list of news.
    ArrayList<Movie> movies = QueryUtils.fetchMovieData(mUrl);
    Log.v("############", "loadInBackground called");
    return movies;
}
}

注意:这里的代码也可以在Gist中找到:https://gist.github.com/rajtheinnovator/4ae0ab873129eff84db68d5645ac64d8

修改:

public class QueryUtils {
private static String movieTitle;
private static int movieId;
private static String moviePosterPath;

/**
 * Create a private constructor because no one should ever create a {@link QueryUtils} object.
 * This class is only meant to hold static variables and methods, which can be accessed
 * directly from the class name QueryUtils (and an object instance of QueryUtils is not needed).
 */
private QueryUtils() {
}

/**
 * Query the GUARDIAN dataset and return an {@link Movie} ArrayList to represent a single Movie.
 */
public static ArrayList<Movie> fetchMovieData(String requestUrl) {
    Log.v("############", "fetchMovieData called");
    // Create URL object
    URL url = createUrl(requestUrl);

    // Perform HTTP request to the URL and receive a JSON response back
    String jsonResponse = null;
    try {
        jsonResponse = makeHttpRequest(url);
    } catch (IOException e) {
        //handle exception
    }

    // Extract relevant fields from the JSON response and create an {@link Event} object
    ArrayList<Movie> movies = extractFeatureFromJson(jsonResponse);

    // Return the {@link Event}
    return movies;
}

/**
 * Returns new URL object from the given string URL.
 */
private static URL createUrl(String stringUrl) {
    URL url = null;
    try {
        url = new URL(stringUrl);
    } catch (MalformedURLException e) {
        //handle exception
    }
    return url;
}

/**
 * Make an HTTP request to the given URL and return a String as the response.
 */
private static String makeHttpRequest(URL url) throws IOException {
    Log.v("############", "makeHttpRequest called");
    String jsonResponse = "";

    // If the URL is null, then return early.
    if (url == null) {
        return jsonResponse;
    }

    HttpURLConnection urlConnection = null;
    InputStream inputStream = null;
    try {
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setReadTimeout(10000 /* milliseconds */);
        urlConnection.setConnectTimeout(15000 /* milliseconds */);
        urlConnection.setRequestMethod("GET");
        urlConnection.connect();

        /*
        If the request was successful (response code 200),
        then read the input stream and parse the response.
        */
        if (urlConnection.getResponseCode() == 200) {
            inputStream = urlConnection.getInputStream();
            jsonResponse = readFromStream(inputStream);
        } else {
            //handle exception
        }
    } catch (IOException e) {
        //handle exception
    } finally {
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
        if (inputStream != null) {
            inputStream.close();
        }
    }
    return jsonResponse;
}

/**
 * Convert the {@link InputStream} into a String which contains the
 * whole JSON response from the server.
 */
private static String readFromStream(InputStream inputStream) throws IOException {
    StringBuilder output = new StringBuilder();
    if (inputStream != null) {
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
        BufferedReader reader = new BufferedReader(inputStreamReader);
        String line = reader.readLine();
        while (line != null) {
            output.append(line);
            line = reader.readLine();
        }
    }
    return output.toString();
}

/**
 * Return a list of {@link Movie} objects that has been built up from
 * parsing a JSON response.
 */
public static ArrayList<Movie> extractFeatureFromJson(String jsonResponse) {
    Log.v("############", "extractFeatureFromJson called");
    Log.v("############", "jsonResponse"+jsonResponse);

    // Create an empty ArrayList that we can start adding movies to
    ArrayList<Movie> movies = new ArrayList<Movie>();

    /*
    Try to parse the received jsonResponse. If there's a problem with the way the JSON
    is formatted, a JSONException exception object will be thrown. Catch the exception
    so the app doesn't crash, and handle exception.
    */
    try {
        // Parse the jsonResponse string
        JSONObject movie_json_response = new JSONObject(jsonResponse);
        Log.v("############", "JSONObject is: " + movie_json_response.toString());
        if (movie_json_response.has("results")) {
            JSONArray resultsArray = movie_json_response.getJSONArray("results");
            if (resultsArray.length() > 0) {
                for (int i = 0; i < resultsArray.length(); i++) {
                    JSONObject movieDetail = resultsArray.getJSONObject(0);
                    if (movieDetail.has("title")) {
                        movieTitle = movieDetail.getString("title");
                    }
                    if (movieDetail.has("id")) {
                        movieId = movieDetail.getInt("id");
                    }
                    if (movieDetail.has("poster_path")) {
                        moviePosterPath = movieDetail.getString("poster_path");
                    }
                    Log.v("############", " title is "+movies + "############ id is"+movieId+" ############ poster path is "+moviePosterPath);
                    movies.add(new Movie(movieTitle, movieId, moviePosterPath));
                }
            }
        }
    } catch (JSONException e) {
        //handle exception
    }
    Log.v("############", "Movies returned is: " + movies.toString());
    // Return the list of movies
    return movies;
}
}

更新:

StackTrace在这里的要点:https://gist.github.com/rajtheinnovator/4ae0ab873129eff84db68d5645ac64d8#file-stacktrace-xml

3 个答案:

答案 0 :(得分:1)

更新 QueryUtils 中存在的解析器逻辑。

for (int i = 0; i < resultsArray.length(); i++) {
 Change zeroth index to i
//JSONObject movieDetail =  resultsArray.getJSONObject(0);

我建议你创建一个局部变量 currentMovie 而不是全局变量。

@Override
public void onBindViewHolder(DefaultMovieAdapter.ViewHolder viewHolder, int position) {
// Use Local variable
Movie currentMovie = mDefaultMovie.get(position);
}

答案 1 :(得分:1)

问题出在您的QueryUtils类中。您始终在索引0处检索元素。 改变自:

JSONObject movieDetail = resultsArray.getJSONObject(0);

JSONObject movieDetail = resultsArray.getJSONObject(i);

答案 2 :(得分:0)

上面代码中的所有内容都可以,除非我搞砸了处理JSON。正如@rahul和@ marcelo建议的那样,问题出现在我编码的类 QueryUtils.java 的方法 extractFeatureFromJson 中的for循环中:

for (int i = 0; i < resultsArray.length(); i++) {
                JSONObject movieDetail = resultsArray.getJSONObject(0);
//other code
}

但实际的行应该是:

for (int i = 0; i < resultsArray.length(); i++) {
                JSONObject movieDetail = resultsArray.getJSONObject(i);
//other code
}

上面的其余代码非常好,工作正常。