我想使用带有改型和视图模型的API。
数据未显示在屏幕中,错误是“映射器函数返回了空值”。
这是我的片段,我在这里连接适配器:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_movies, container, false);
rv_grid_movies = rootView.findViewById(R.id.rv_grid_movies);
RecyclerView.LayoutManager manager = new GridLayoutManager(getActivity(), 2);
rv_grid_movies.addItemDecoration(new GridSpacingItemDecoration(2, dpToPx(3), true));
rv_grid_movies.setItemAnimator(new DefaultItemAnimator());
rv_grid_movies.setLayoutManager(manager);
rv_grid_movies.setHasFixedSize(true);
Model moviesModel = ViewModelProviders.of(this).get(Model.class);
moviesModel.getMovies().observe(this, new Observer<List<MoviesItems>>() {
@Override
public void onChanged(@Nullable List<MoviesItems> moviesItems) {
moviesAdapter = new MoviesAdapter(getActivity(), moviesItems);
rv_grid_movies.setAdapter(moviesAdapter);
}
});
return rootView;
}
这是我的适配器和我的回收站视图适配器:
public MoviesAdapter(Context context, List<MoviesItems> moviesList){
this.context = context;
this.moviesList = moviesList;
}
@NonNull
@Override
public MoviesViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate(R.layout.grid_movies, viewGroup, false);
return new MoviesViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MoviesViewHolder moviesViewHolder, int i) {
MoviesItems items = moviesList.get(i);
String url = "https://image.tmdb.org/t/p/original";
moviesViewHolder.txt_title_movies.setText(items.getTitle());
moviesViewHolder.txt_date_movies.setText(items.getRelease_date());
moviesViewHolder.txt_description_movies.setText(items.getOverview());
Glide.with(context)
.load(url + items.getPoster_path())
.into(moviesViewHolder.img_movies);
}
@Override
public int getItemCount() {
return (moviesList != null) ? moviesList.size() : 0;
}
public class MoviesViewHolder extends RecyclerView.ViewHolder {
TextView txt_title_movies, txt_date_movies, txt_description_movies;
ImageView img_movies;
public MoviesViewHolder(@NonNull View itemView) {
super(itemView);
txt_title_movies = itemView.findViewById(R.id.txt_titlemovie);
txt_date_movies = itemView.findViewById(R.id.txt_datemovie);
txt_description_movies = itemView.findViewById(R.id.txt_descriptionmovie);
img_movies = itemView.findViewById(R.id.img_movie);
}
}
}
这是接口API,我使用Retrofit:
public interface Api {
String BASE_URL = "https://api.themoviedb.org/3/discover/";
String API_KEY = "myapikey";
String LANGUAGE = "&language=en-US";
@GET("movie?api_key="+API_KEY+LANGUAGE)
Call<List<MoviesItems>> getMovies();
}
这是我的视图模型:
private MutableLiveData<List<MoviesItems>> moviesList;
public LiveData<List<MoviesItems>> getMovies() {
if (moviesList == null){
moviesList = new MutableLiveData<List<MoviesItems>>();
loadMovies();
}
return moviesList;
}
private void loadMovies() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Api.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Api api = retrofit.create(Api.class);
Call<List<MoviesItems>> call = api.getMovies();
call.enqueue(new Callback<List<MoviesItems>>() {
@Override
public void onResponse(Call<List<MoviesItems>> call, Response<List<MoviesItems>> response) {
moviesList.setValue(response.body());
}
@Override
public void onFailure(Call<List<MoviesItems>> call, Throwable t) {
}
});
}
这是logcat中的错误消息:
java.lang.NullPointerException: The mapper function returned a null value.
at io.reactivex.d.b.b.a(ObjectHelper.java:39)
at io.reactivex.d.e.b.g$a.onNext(ObservableMap.java:59)
at io.reactivex.d.e.b.i$a.run(ObservableScalarXMap.java:248)
at io.reactivex.d.e.b.f.b(ObservableJust.java:35)
at io.reactivex.h.a(Observable.java:11442)
at io.reactivex.d.e.b.g.b(ObservableMap.java:33)
at io.reactivex.h.a(Observable.java:11442)
at io.reactivex.d.e.b.l$b.run(ObservableSubscribeOn.java:96)
at io.reactivex.a.b.b$b.run(HandlerScheduler.java:109)
at android.os.Handler.handleCallback(Handler.java:754)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:165)
at android.app.ActivityThread.main(ActivityThread.java:6375)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:912)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:802)
答案 0 :(得分:0)
mapper函数返回一个空值,因此请像这样修改您的代码,下面是完整的示例代码:
完整示例代码:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragment"/>
</RelativeLayout>
MainActivity.java:
public class MainActivity extends AppCompatActivity {
FrameLayout frameLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
frameLayout=findViewById(R.id.fragment);
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,new MyFragment(),"").commit();
}
}
fragment_movies.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_grid_movies"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
MyFragment.java:
import android.support.v4.app.Fragment;
public class MyFragment extends Fragment {
RecyclerView rv_grid_movies;
MoviesAdapter moviesAdapter;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_movies, container, false);
rv_grid_movies = rootView.findViewById(R.id.rv_grid_movies);
RecyclerView.LayoutManager manager = new GridLayoutManager(getActivity(), 2);
// rv_grid_movies.addItemDecoration(new GridSpacingItemDecoration(2, dpToPx(3), true));
rv_grid_movies.setItemAnimator(new DefaultItemAnimator());
rv_grid_movies.setLayoutManager(manager);
rv_grid_movies.setHasFixedSize(true);
Model model = ViewModelProviders.of(this).get(Model.class);
model.getMovies().observe(this, new Observer<List<MovieList>>() {
@Override
public void onChanged(@Nullable List<MovieList> movieLists) {
moviesAdapter=new MoviesAdapter(getActivity(),movieLists);
rv_grid_movies.setAdapter(moviesAdapter);
}
});
return rootView;
}
}
MovieItems.java(Model):
public class MovieItems {
@SerializedName("page")
@Expose
private Integer page;
@SerializedName("total_results")
@Expose
private Integer totalResults;
@SerializedName("total_pages")
@Expose
private Integer totalPages;
@SerializedName("results")
@Expose
private List<MovieList> movieLists = null;
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getTotalResults() {
return totalResults;
}
public void setTotalResults(Integer totalResults) {
this.totalResults = totalResults;
}
public Integer getTotalPages() {
return totalPages;
}
public void setTotalPages(Integer totalPages) {
this.totalPages = totalPages;
}
public List<MovieList> getMovieLists() {
return movieLists;
}
public void setMovieLists(List<MovieList> movieLists) {
this.movieLists = movieLists;
}
}
MovieList.java(模型):
public class MovieList {
@SerializedName("popularity")
@Expose
private Double popularity;
@SerializedName("vote_count")
@Expose
private Integer voteCount;
@SerializedName("video")
@Expose
private Boolean video;
@SerializedName("poster_path")
@Expose
private String posterPath;
@SerializedName("id")
@Expose
private Integer id;
@SerializedName("adult")
@Expose
private Boolean adult;
@SerializedName("backdrop_path")
@Expose
private String backdropPath;
@SerializedName("original_language")
@Expose
private String originalLanguage;
@SerializedName("original_title")
@Expose
private String originalTitle;
@SerializedName("genre_ids")
@Expose
private List<Integer> genreIds = null;
@SerializedName("title")
@Expose
private String title;
@SerializedName("vote_average")
@Expose
private Double voteAverage;
@SerializedName("overview")
@Expose
private String overview;
@SerializedName("release_date")
@Expose
private String releaseDate;
public Double getPopularity() {
return popularity;
}
public void setPopularity(Double popularity) {
this.popularity = popularity;
}
public Integer getVoteCount() {
return voteCount;
}
public void setVoteCount(Integer voteCount) {
this.voteCount = voteCount;
}
public Boolean getVideo() {
return video;
}
public void setVideo(Boolean video) {
this.video = video;
}
public String getPosterPath() {
return posterPath;
}
public void setPosterPath(String posterPath) {
this.posterPath = posterPath;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Boolean getAdult() {
return adult;
}
public void setAdult(Boolean adult) {
this.adult = adult;
}
public String getBackdropPath() {
return backdropPath;
}
public void setBackdropPath(String backdropPath) {
this.backdropPath = backdropPath;
}
public String getOriginalLanguage() {
return originalLanguage;
}
public void setOriginalLanguage(String originalLanguage) {
this.originalLanguage = originalLanguage;
}
public String getOriginalTitle() {
return originalTitle;
}
public void setOriginalTitle(String originalTitle) {
this.originalTitle = originalTitle;
}
public List<Integer> getGenreIds() {
return genreIds;
}
public void setGenreIds(List<Integer> genreIds) {
this.genreIds = genreIds;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Double getVoteAverage() {
return voteAverage;
}
public void setVoteAverage(Double voteAverage) {
this.voteAverage = voteAverage;
}
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;
}
}
MoviesAdapter.java:
public class MoviesAdapter extends
RecyclerView.Adapter<MoviesAdapter.MoviesViewHolder>{
List<MovieList> moviesList;
Context context;
public MoviesAdapter(Context context, List<MovieList> moviesList){
this.context = context;
this.moviesList = moviesList;
}
@NonNull
@Override
public MoviesViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(context).inflate(R.layout.grid_movies, viewGroup, false);
return new MoviesViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MoviesViewHolder moviesViewHolder, int i) {
MovieList items = moviesList.get(i);
String url = "https://image.tmdb.org/t/p/original";
moviesViewHolder.txt_title_movies.setText(items.getTitle());
moviesViewHolder.txt_date_movies.setText(items.getReleaseDate());
moviesViewHolder.txt_description_movies.setText(items.getOverview());
Glide.with(context)
.load(url + items.getPosterPath())
.into(moviesViewHolder.img_movies);
}
@Override
public int getItemCount() {
return (moviesList != null) ? moviesList.size() : 0;
}
public class MoviesViewHolder extends RecyclerView.ViewHolder {
TextView txt_title_movies, txt_date_movies, txt_description_movies;
ImageView img_movies;
public MoviesViewHolder(@NonNull View itemView) {
super(itemView);
txt_title_movies = itemView.findViewById(R.id.txt_titlemovie);
txt_date_movies = itemView.findViewById(R.id.txt_datemovie);
txt_description_movies = itemView.findViewById(R.id.txt_descriptionmovie);
img_movies = itemView.findViewById(R.id.img_movie);
}
}
}
Api(interface):
public interface Api {
String BASE_URL = "https://api.themoviedb.org/3/discover/";
@GET("movie")
Call<MovieItems> getMovies(@Query("api_key") String apiKey, @Query("language")String language);
}
Model.java:
public class Model extends ViewModel {
private MutableLiveData<List<MovieList>> moviesList;
public LiveData<List<MovieList>> getMovies() {
if (moviesList == null){
moviesList = new MutableLiveData<List<MovieList>>();
loadMovies();
}
return moviesList;
}
private void loadMovies() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Api.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Api api = retrofit.create(Api.class);
/*Add your api key*/ Call<MovieItems> call = api.getMovies("Your api key","en-US");
call.enqueue(new Callback<MovieItems>() {
@Override
public void onResponse(Call<MovieItems> call, Response<MovieItems> response) {
moviesList.setValue(response.body().getMovieLists());
}
@Override
public void onFailure(Call<MovieItems> call, Throwable t) {
}
});
}
}
grid_movies.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/txt_titlemovie"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/txt_datemovie"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/txt_descriptionmovie"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/img_movie"/>
</LinearLayout>