我正在使用Retrofit库来解析现在播放电影类别的TMDB API的Json响应,并希望在RecyclerView中显示结果。
我从API获得的原始Json是
{
"page": 1,
"results": [
{
"poster_path": "\/tWqifoYuwLETmmasnGHO7xBjEtt.jpg",
"adult": false,
"overview": "A live-action adaptation of Disney's version of the classic 'Beauty and the Beast' tale of a cursed prince and a beautiful young woman who helps him break the spell.",
"release_date": "2017-03-16",
"genre_ids": [
14,
10749
],
"id": 321612,
"original_title": "Beauty and the Beast",
"original_language": "en",
"title": "Beauty and the Beast",
"backdrop_path": "\/6aUWe0GSl69wMTSWWexsorMIvwU.jpg",
"popularity": 147.254531,
"vote_count": 1938,
"video": false,
"vote_average": 6.9
},
{
"poster_path": "\/unPB1iyEeTBcKiLg8W083rlViFH.jpg",
"adult": false,
"overview": "A story about how a new baby's arrival impacts a family, told from the point of view of a delightfully unreliable narrator, a wildly imaginative 7 year old named Tim.",
"release_date": "2017-03-23",
"genre_ids": [
16,
35,
10751
],
"id": 295693,
"original_title": "The Boss Baby",
"original_language": "en",
"title": "The Boss Baby",
"backdrop_path": "\/bTFeSwh07oX99ofpDI4O2WkiFJ.jpg",
"popularity": 138.856227,
"vote_count": 649,
"video": false,
"vote_average": 5.7
},
我创建的模型类是Movie.java
package com.example.vikas.movie.model;
/**
* Created by vikas on 22/4/17
*/
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.List;
public class Movie
{
@SerializedName("poster_path")
private String posterPath;
@SerializedName("adult")
private boolean adult;
@SerializedName("overview")
private String overview;
@SerializedName("realease_date")
private String releaseDate;
@SerializedName("genre_ids")
private List<Integer> genreIds=new ArrayList<Integer>();
@SerializedName("id")
private int id;
@SerializedName("original_title")
private String originalTitle;
@SerializedName("original_language")
private String original_language;
@SerializedName("title")
private String title;
@SerializedName("backdrop_path")
private String backdrop_path;
@SerializedName("popularity")
private Double popularity;
@SerializedName("vote_count")
private int vote_count;
@SerializedName("vedeo")
private boolean vedeo;
@SerializedName("vote_average")
private Double vote_average;
public Movie(String posterPath, boolean adult, String overview, String releaseDate, List<Integer> genreIds, Integer id,
String originalTitle, String original_language, String title, String backdrop_path, Double popularity,
Integer vote_count, Boolean vedeo, Double vote_average) {
this.posterPath = posterPath;
this.adult = adult;
this.overview = overview;
this.releaseDate = releaseDate;
this.genreIds = genreIds;
this.id = id;
this.originalTitle = originalTitle;
this.original_language = original_language;
this.title = title;
this.backdrop_path = backdrop_path;
this.popularity = popularity;
this.vote_count = vote_count;
this.vedeo = vedeo;
this.vote_average = vote_average;
}
public String getPosterPath() {
return posterPath;
}
public void setPosterPath(String posterPath) {
this.posterPath = posterPath;
}
public boolean isAdult() {
return adult;
}
public void setAdult(boolean adult) {
this.adult = adult;
}
public String getOverview() {
return overview;
}
public void setOverview(String overview) {
this.overview = overview;
}
public String getReleaseDate() {
return releaseDate;
}
public void setReleaseDate(String releaseDate) {
this.releaseDate = releaseDate;
}
public List<Integer> getGenreIds() {
return genreIds;
}
public void setGenreIds(List<Integer> genreIds) {
this.genreIds = genreIds;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOriginalTitle() {
return originalTitle;
}
public void setOriginalTitle(String originalTitle) {
this.originalTitle = originalTitle;
}
public String getOriginalLanguage() {
return original_language;
}
public void setOriginalLanguage(String original_language) {
this.original_language = original_language;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBackdropPath() {
return backdrop_path;
}
public void setBackdropPath(String backdropPath) {
this.backdrop_path = backdropPath;
}
public Double getPopularity() {
return popularity;
}
public void setPopularity(Double popularity) {
this.popularity = popularity;
}
public Integer getvote_count() {
return vote_count;
}
public void setVoteCount(Integer voteCount) {
this.vote_count = voteCount;
}
public Boolean getVedeo() {
return vedeo;
}
public void setVedeo(Boolean vedeo) {
this.vedeo = vedeo;
}
public Double getvote_average() {
return vote_average;
}
public void setVoteAverage(Double vote_average) {
this.vote_average = vote_average;
}
}
MovieResponse.java
package com.example.vikas.movie.model;
/**
* Created by vikas on 22/4/17.
*/
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class MoviesResponse
{
@SerializedName("page")
private int page;
@SerializedName("results")
private List<Movie>results;
@SerializedName("total_results")
private int totalResults;
@SerializedName("total_pages")
private int totalPages;
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public List<Movie> getResults() {
return results;
}
public void setResults(List<Movie> results) {
this.results = results;
}
public int getTotalResults() {
return totalResults;
}
public void setTotalResults(int totalResults) {
this.totalResults = totalResults;
}
public int getTotalPages() {
return totalPages;
}
public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
}
}
现在进行如下API调用是我编写的用于生成URL和创建改造实例的类。我用这些类来生成URL并调用API。
ApiClient.java 包com.example.vikas.movie.rest;
/**
* Created by vikas on 22/4/17.
*/
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient
{
public static final String URL="http://api.themoviedb.org/3/";
private static Retrofit retrofit=null;
public static Retrofit getClient()
{
if(retrofit==null)
{
retrofit= new Retrofit.Builder()//netsed class of Retrofit used to create a new instance of retrofit class
.baseUrl(URL)//loaded the base URL which will be used later for all api calls
.addConverterFactory(GsonConverterFactory.create())//added converter factory for
// serialization and desserialization of objects
.build();//created the retrofit instance with configured values
}
return retrofit;
}
}
ApiInterface.java
package com.example.vikas.movie.rest;
/**
* Created by vikas on 23/4/17.
*/
import com.example.vikas.movie.model.MoviesResponse;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;
// to define endpoints of a URL we will use special retrofit annotations. we have used Get annotation in
// to get information from the API and the parameters os this method are @Query,@Path,
public interface ApiInterface
{
@GET("movie/now-playing")
Call<MoviesResponse> getNowPlayingMovies(@Query("api_key") String apikey);
//return value is alwayays a parameterized call<T> oject whose paremeter is generic so we can have
@GET("movie/{id}")
Call<MoviesResponse> getMovieDetails(@Path("id") int id, @Query("api_key") String apiKey);
}
// so using this we will generate the following URL
//http://api.themoviedb.org/3/movie/now-playing?api_key=Xxxx
现在我在RecyclerView中显示结果,所以Dapter类就是
MovieAdapter.java
package com.example.vikas.movie.adapter;
/**
* Created by vikas on 23/4/17.
*/
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.example.vikas.movie.R;
import com.example.vikas.movie.helper.CircleTransformation;
import com.example.vikas.movie.model.Movie;
import java.util.List;
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MovieViewHolder> {
private List<Movie> movies;
private int rowLayout;
private Context context;
public static class MovieViewHolder extends RecyclerView.ViewHolder {
RelativeLayout moviesLayout;
TextView movieTitle;
TextView data;
TextView movieDescription;
TextView rating;
public MovieViewHolder(View v) {
super(v);
moviesLayout = (RelativeLayout) v.findViewById(R.id.movie_layout);
movieTitle = (TextView) v.findViewById(R.id.title);
data = (TextView) v.findViewById(R.id.subtitle);
movieDescription = (TextView) v.findViewById(R.id.description);
rating = (TextView) v.findViewById(R.id.rating);
}
}
public MovieAdapter(List<Movie> movies, int rowLayout, Context context) {
this.movies = movies;
this.rowLayout = rowLayout;
this.context = context;
}
@Override
public MovieAdapter.MovieViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(rowLayout, parent, false);
return new MovieViewHolder(view);
}
@Override
public void onBindViewHolder(MovieViewHolder holder, final int position) {
holder.movieTitle.setText(movies.get(position).getTitle());
holder.data.setText(movies.get(position).getReleaseDate());
holder.movieDescription.setText(movies.get(position).getOverview());
holder.rating.setText(movies.get(position).getvote_average().toString());
}
@Override
public int getItemCount() {
return movies.size();
}
}
现在是MainActivity类,我在其中调用API并获取结果并将结果提供给适配器
MainActivity.java
package com.example.vikas.movie;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import com.example.vikas.movie.adapter.MovieAdapter;
import com.example.vikas.movie.model.Movie;
import com.example.vikas.movie.model.MoviesResponse;
import com.example.vikas.movie.rest.ApiClient;
import com.example.vikas.movie.rest.ApiInterface;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MainActivity extends AppCompatActivity
{
private static final String TAG=MainActivity.class.getSimpleName();
//get which got from web API
private final static String API_KEY="Xxxxxx";
private CoordinatorLayout coordinatorLayout;
private MovieAdapter movieAdapter;
private RecyclerView recyclerView;
private List<Movie> movieList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
coordinatorLayout=(CoordinatorLayout)findViewById(R.id.coordinate_layout);
if(API_KEY.isEmpty())
{
Snackbar snackbar = Snackbar
.make(coordinatorLayout, "Please Enter The key", Snackbar.LENGTH_LONG);
snackbar.show();
return;
}
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext()));
ApiInterface apiInterface= ApiClient.getClient().create(ApiInterface.class);
Call<MoviesResponse> call= apiInterface.getNowPlayingMovies(API_KEY);
call.enqueue(new Callback<MoviesResponse>()
{
@Override
public void onResponse(Call<MoviesResponse> call, Response<MoviesResponse> response)
{
Log.d(TAG, "Number of movies received: " + movieList.size());
int statusCode=response.code();
movieList= response.body().getResults();
movieAdapter.notifyDataSetChanged();
}
@Override
public void onFailure(Call<MoviesResponse> call, Throwable t)
{
Snackbar snackbar = Snackbar
.make(coordinatorLayout, "Please Check your Internet Connection", Snackbar.LENGTH_LONG);
snackbar.show();
Log.e(TAG, t.toString());
}
});
}
}
但是当我运行此代码时,我收到此错误logcat
04-23 23:32:32.529 30506-30506/com.example.vikas.movie E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.vikas.movie, PID: 30506
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.vikas.movie.adapter.MovieAdapter.notifyDataSetChanged()' on a null object reference
at com.example.vikas.movie.MainActivity$1.onResponse(MainActivity.java:69)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:68)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
那么我现在该怎么做才能运行这段代码并获取结果以成功显示结果,以便它不会返回null
这个问题不同,首先我无法提供正确的网址
答案 0 :(得分:0)
首先你的网址是错误的。它应该是
http://api.themoviedb.org/3/movie/now_playing?api_key=ddfffa28b4ace50cacf67274370469a1
注意结束点 movie / now_playing 而不是电影/正在播放
这在https://developers.themoviedb.org/3/movies/get-now-playing
中有记录此外,您可以在获得响应并初始化列表
后获取列表大小首先这个
movieList= response.body().getResults();
然后
Log.d(TAG, "Number of movies received: " + movieList.size());
Edit1:
你有这个
recyclerView.setAdapter(new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext()));
您永远不会初始化movieAdapter
,当您致电notifyDataSetChanged()
时,movieAdapter
为空。
将其更改为
movieAdapter = new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext());
然后
recyclerView.setAdapter(movieAdapter);
注意在大多数与ui相关的情况下,获取getApplicationContext()可能没用。喜欢活动背景。
编辑2:您还需要将项目添加到同一列表中,然后按照以下评论中的建议调用notifyDataSetChanged()
。
答案 1 :(得分:0)
替换
recyclerView.setAdapter(new MovieAdapter(movieList, R.layout.recyclerview_item_movie, getApplicationContext()));
使用
movieAdapter = new MovieAdapter(movieList, R.layout.recyclerview_item_movie, this);
recyclerView.setAdapter(movieAdapter);
并替换
movieList= response.body().getResults();
movieAdapter.notifyDataSetChanged();
有了这个。否则notifyDataSetChanged什么都不做,因为你有一个完全不同的列表,需要重新创建适配器
movieList.clear();
movieList.addAll(response.body().getResults());
movieAdapter.notifyDataSetChanged();