问题
I've been using Retrofit2.0 (just learned how to use it) to retrieve Google Books API info, I have finally succeeded. But now I don't know know how to pass it to RecyclerView since now my models have gotten revised and now deal with nested models (I think that is the correct term) compared to what I had before to (old Stack Overflow question here).
Currently, I'm getting this error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android_myneighborsbookshelf, PID: 8806
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.List.get(int)' on a null object reference
at com.example.android_myneighborsbookshelf.adapters.SearchViewRecyclerViewAdapter.onBindViewHolder(SearchViewRecyclerViewAdapter.java:90)
at com.example.android_myneighborsbookshelf.adapters.SearchViewRecyclerViewAdapter.onBindViewHolder(SearchViewRecyclerViewAdapter.java:27)
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7033)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7075)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:5991)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6258)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6097)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6093)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4115)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3832)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4385)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1083)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1812)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1656)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1565)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at androidx.appcompat.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:446)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1812)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1656)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1565)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:753)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2792)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2319)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1460)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7183)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:949)
at android.view.Choreographer.doCallbacks(Choreographer.java:761)
at android.view.Choreographer.doFrame(Choreographer.java:696)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Here is my model Book2.java
public class Book2 {
// @SerializedName("title")
private String mTitle;
@SerializedName("items")
private List<Items> items;
public Book2() {
// no arg constructor needed
}
public Book2(Items items) { // <-- not too sure if this is correct
this.mTitle = mTitle; // <-- not too sure if this is correct
}
public List<Items> getItems() {
return items;
}
public void setItems(List<Items> items) {
this.items = items;
}
public class Items {
@SerializedName("volumeInfo")
private VolumeInfo volumeInfo;
public VolumeInfo getVolumeInfo() {
return volumeInfo;
}
public void setVolumeInfo(VolumeInfo volumeInfo) {
this.volumeInfo = volumeInfo;
}
}
public static class VolumeInfo extends Book2 {
@SerializedName("title")
private String mTitle;
@SerializedName("authors")
private ArrayList<String> mAuthors;
@SerializedName("publisher")
private String mPublisher;
@SerializedName("publishedDate")
private String mPublishedDate;
@SerializedName("description")
private String mDescription;
// @SerializedName("industryIdentifiers")
// private ArrayList<String> mIndustryIdentifiers;
@SerializedName("categories")
private ArrayList<String> mCategories;
@SerializedName("imageLinks")
private ImageLinks mImageLinks;
public VolumeInfo(String title) {
this.mTitle = title;
}
public String getmTitle() {
return mTitle;
}
public void setmTitle(String mTitle) {
this.mTitle = mTitle;
}
public ArrayList<String> getmAuthors() {
return mAuthors;
}
public void setmAuthors(ArrayList<String> mAuthors) {
this.mAuthors = mAuthors;
}
public String getmPublisher() {
return mPublisher;
}
public void setmPublisher(String mPublisher) {
this.mPublisher = mPublisher;
}
public String getmPublishedDate() {
return mPublishedDate;
}
public void setmPublishedDate(String mPublishedDate) {
this.mPublishedDate = mPublishedDate;
}
public String getmDescription() {
return mDescription;
}
public void setmDescription(String mDescription) {
this.mDescription = mDescription;
}
/*
public ArrayList<String> getmIndustryIdentifiers() {
return mIndustryIdentifiers;
}
public void setmIndustryIdentifiers(ArrayList<String> mIndustryIdentifiers) {
this.mIndustryIdentifiers = mIndustryIdentifiers;
}
*/
public ArrayList<String> getmCategories() {
return mCategories;
}
public void setmCategories(ArrayList<String> mCategories) {
this.mCategories = mCategories;
}
public ImageLinks getmImageLinks() {
return mImageLinks;
}
public void setmImageLinks(ImageLinks mImageLinks) {
this.mImageLinks = mImageLinks;
}
}
public class ImageLinks {
@SerializedName("thumbnail")
private String mThumbnail;
public String getmThumbnail() {
return mThumbnail;
}
public void setmThumbnail(String mThumbnail) {
this.mThumbnail = mThumbnail;
}
}
}
MainActivity2.java
public class MainActivity2 extends AppCompatActivity {
private RecyclerView mRecyclerView;
private ArrayList<Book2> mBooks;
// private ArrayList<Book2> mBooks;
private SearchViewRecyclerViewAdapter mSearchViewRecyclerViewAdapter;
private RequestQueue mRequestQueue;
private static final String BASE_URL2="https://www.googleapis.com/books/v1/";
// private static final String BASE_URL_TYPICODE="https://jsonplaceholder.typicode.com/";
// private static final String BASE_URL="https://www.googleapis.com/books/v1/volumes?q=";
private EditText search_edit_text;
Button search_button;
ProgressBar loading_indicator;
private TextView error_message;
// private static final String LOG_TAG = MainActivity2.class.getSimpleName();
private static final String TAG = "MainActivity2";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_main2);
search_edit_text=findViewById(R.id.search_box);
search_button= findViewById(R.id.search_buttton);
loading_indicator=findViewById(R.id.loading_indicator);
error_message= findViewById(R.id.message_display);
mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mBooks = new ArrayList<>();
mRequestQueue = Volley.newRequestQueue(this);
search_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBooks.clear();
search();
}
});
}
private void parseJson3(String key){
// source = https://medium.com/@wkrzywiec/making-use-of-open-rest-api-with-retrofit-dac6094f0522
// youtube tutorial here: https://www.youtube.com/watch?v=4JGvDUlfk7Y
JsonPlaceHolderApi serviceEndPoint = ServiceGenerator.createService(JsonPlaceHolderApi.class);
Call<Book2> call = serviceEndPoint.getQueryItems2("volumes?q=enders+game");
call.enqueue(new Callback<Book2>() {
@Override
public void onResponse(Call<Book2> call, retrofit2.Response<Book2> response) {
if (response.isSuccessful()) {
String title ="";
// List<String> author = "";
String author =" ";
String publishedDate = "Not Available";
String publisher = "Not Available";
String description = "No Description";
int pageCount = 1000;
String category = "No categories Available ";
String buy ="";
// String volumeInfoString = "";
String bookJSONString = null;
String thumbnail = "";
String previewLink = "";
String price = "";
String url = "";
// for (int i = 0; i < grantResults.length; i++) {
for (int i = 0; i < response.body().getItems().size(); i++) {
Log.d(TAG, "onResponse: success getmTitle: " + response.body().getItems().get(i).getVolumeInfo().getmTitle());
title = response.body().getItems().get(i).getVolumeInfo().getmTitle();
Log.d(TAG, "onResponse: success getmAuthors: " + response.body().getItems().get(i).getVolumeInfo().getmAuthors());
// author = response.body().getItems().get(i).getVolumeInfo().getmAuthors();
Log.d(TAG, "onResponse: success getmCategories: " + response.body().getItems().get(i).getVolumeInfo().getmCategories());
// category = response.body().getItems().get(i).getVolumeInfo().getmCategories();
Log.d(TAG, "onResponse: success getmDescription: " + response.body().getItems().get(i).getVolumeInfo().getmDescription());
description = response.body().getItems().get(i).getVolumeInfo().getmDescription();
Log.d(TAG, "onResponse: success getmPublisher: " + response.body().getItems().get(i).getVolumeInfo().getmPublisher());
publisher = response.body().getItems().get(i).getVolumeInfo().getmPublisher();
Log.d(TAG, "onResponse: success getmImageLinks: " + response.body().getItems().get(i).getVolumeInfo().getmImageLinks().getmThumbnail());
thumbnail = response.body().getItems().get(i).getVolumeInfo().getmImageLinks().getmThumbnail();
}
Log.d(TAG, "onResponse: title = " + title);
// mBooks.add(new Book2(title, author, publishedDate, description ,category
// , thumbnail, buy, previewLink, price, pageCount, url));
mBooks.add(new Book2.VolumeInfo(title));
mSearchViewRecyclerViewAdapter = new SearchViewRecyclerViewAdapter(MainActivity2.this , mBooks);
mRecyclerView.setAdapter(mSearchViewRecyclerViewAdapter);
} else {
Log.d(TAG, "onResponse: call in MainActivity2 was not successful");
}
}
@Override
public void onFailure(Call<Book2> call, Throwable t) {
Log.d(TAG, "onFailure: didnt work" + t);
}
});
}
private boolean Read_network_state(Context context)
{ boolean is_connected;
ConnectivityManager cm =(ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
is_connected=info!=null&&info.isConnectedOrConnecting();
return is_connected;
}
private void search()
{
String search_query = search_edit_text.getText().toString();
boolean is_connected = Read_network_state(this);
if(!is_connected)
{
error_message.setText(R.string.Failed_to_Load_data);
mRecyclerView.setVisibility(View.INVISIBLE);
error_message.setVisibility(View.VISIBLE);
return;
}
// Log.d("QUERY",search_query);
if(search_query.equals(""))
{
Toast.makeText(this,"Please enter your query",Toast.LENGTH_SHORT).show();
return;
}
String final_query=search_query.replace(" ","+");
Uri uri=Uri.parse(BASE_URL2+final_query);
// Uri uri=Uri.parse(BASE_URL+final_query);
Uri.Builder buider = uri.buildUpon();
parseJson3(buider.toString());
}
}
SearchViewRecyclerViewAdapter.java
public class SearchViewRecyclerViewAdapter extends RecyclerView.Adapter<SearchViewRecyclerViewAdapter.MyViewHolder> {
private Context mContext;
private List<Book2> mData;
private RequestOptions options;
private static final String LOG_TAG = SearchViewRecyclerViewAdapter.class.getSimpleName();
public SearchViewRecyclerViewAdapter(Context mContext, List<Book2> mData) {
this.mContext = mContext;
this.mData = mData;
//Request option for Glide
options = new RequestOptions().centerCrop().placeholder(R.drawable.loading_shape).error(R.drawable.loading_shape);
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int i) {
View view;
LayoutInflater inflater = LayoutInflater.from(mContext);
view = inflater.inflate(R.layout.book_raw_item2 , parent , false);
final MyViewHolder viewHolder = new MyViewHolder(view);
viewHolder.container.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(mContext , BookProfileActivity2.class);
int pos = viewHolder.getAdapterPosition();
// i.putExtra("book_title" ,mData.get(pos).getTitle());
i.putExtra("book_title" ,mData.get(pos).getItems().get(pos).getVolumeInfo().getmTitle());
// i.putExtra("book_author" ,mData.get(pos).getAuthors());
i.putExtra("book_title" ,mData.get(pos).getItems().get(pos).getVolumeInfo().getmAuthors());
// i.putExtra("book_desc" ,mData.get(pos).getDescription());
i.putExtra("book_title" ,mData.get(pos).getItems().get(pos).getVolumeInfo().getmDescription());
// i.putExtra("book_categories" ,mData.get(pos).getCategories());
i.putExtra("book_title" ,mData.get(pos).getItems().get(pos).getVolumeInfo().getmCategories());
// i.putExtra("book_publish_date" ,mData.get(pos).getPublishedDate());
i.putExtra("book_title" ,mData.get(pos).getItems().get(pos).getVolumeInfo().getmPublishedDate());
// i.putExtra("book_info" ,mData.get(pos).getmUrl());
// i.putExtra("book_buy" ,mData.get(pos).getBuy());
// i.putExtra("book_preview" ,mData.get(pos).getPerview());
// i.putExtra("book_thumbnail" ,mData.get(pos).getThumbnail());
i.putExtra("book_title" ,mData.get(pos).getItems().get(pos).getVolumeInfo().getmImageLinks().getmThumbnail());
// i.putExtra("volumeInfo" ,mData.get(pos).getVolumeInfo());
// Log.d(LOG_TAG, "onClick: " + mData.get(pos).getVolumeInfo());
mContext.startActivity(i);
}
});
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int i) {
Book2 book = mData.get(i); // <-- Tried this.
Book2.VolumeInfo book = mData.get(i).getItems().get(i).getVolumeInfo(); // <-- Tried this
holder.tvTitle.setText(book.getItems().get(i).getVolumeInfo().getmTitle()); // <-- Error occurs here
}
@Override
public int getItemCount() {
return mData.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder{
ImageView ivThumbnail;
TextView tvTitle, tvCategory, tvPrice, tvAuthor;
LinearLayout container;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
ivThumbnail = itemView.findViewById(R.id.thumbnail);
tvTitle = itemView.findViewById(R.id.title);
tvAuthor = itemView.findViewById(R.id.author);
tvCategory = itemView.findViewById(R.id.category);
tvPrice = itemView.findViewById(R.id.price);
container = itemView.findViewById(R.id.container);
}
}
}
来源:https://stackoverflow.com/questions/58406028/how-to-pass-retrofit-response-to-recyclerviewadapter-in-android-google-books-api