I am having an issue with a NPE on a clickListener on a RecyclerView which is in a fragment. Initially it works fine with it displaying a toast message, but when I rotate the device and touch any icon I get:
java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.android.newswiz.OnItemClickListener.onItemClick(java.lang.String)' on a null object reference
at com.example.android.newswiz.Fragments.PublishersSlidePageFragment$PublishersAdapter$PublishersAdapterViewHolder$1.onClick(PublishersSlidePageFragment.java:86)
Here is my Fragment class. I have commented the part where there is the NPE in the logs:
package com.example.android.newswiz.Fragments;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.android.newswiz.OnItemClickListener;
import com.example.android.newswiz.R;
public class PublishersSlidePageFragment extends Fragment {
public OnItemClickListener listener;
public PublishersSlidePageFragment(){}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle
savedInstanceState) {
ViewGroup rootView = (ViewGroup)
inflater.inflate(R.layout.fragment_publishers_slide_page,
container, false);
RecyclerView mRecyclerView =
rootView.findViewById(R.id.recyclerView_publishers);
int numberOfColumns =
Integer.parseInt(getContext().getResources().getString(R.string.no_of_cols));
mRecyclerView.setLayoutManager(new GridLayoutManager(getContext(),
numberOfColumns));
mRecyclerView.setHasFixedSize(true);
PublishersAdapter mAdapter = new PublishersAdapter();
mRecyclerView.setAdapter(mAdapter);
return rootView;
}
public void setListener(OnItemClickListener onItemClickListener) {
this.listener = onItemClickListener;
}
public class PublishersAdapter extends RecyclerView.Adapter<PublishersAdapter.PublishersAdapterViewHolder>{
@NonNull
@Override
public PublishersAdapter.PublishersAdapterViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.publisher_item, parent, false);
return new PublishersAdapterViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull PublishersAdapter.PublishersAdapterViewHolder holder, int position) {
holder.mPublisherImageView.setImageResource(mThumbIds[position]);
holder.mPublisherTextView.setText(mPublishers[position]);
holder.bind(mPublishers[position], listener);
}
@Override
public int getItemCount() {
return mThumbIds.length;
}
public class PublishersAdapterViewHolder extends RecyclerView.ViewHolder {
private final ImageView mPublisherImageView;
private final TextView mPublisherTextView;
public PublishersAdapterViewHolder(View itemView) {
super(itemView);
mPublisherImageView = itemView.findViewById(R.id.publisher_icon);
mPublisherTextView = itemView.findViewById(R.id.publisher_desc);
}
public void bind(final String mPublisher, final OnItemClickListener clickListener) {
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
clickListener.onItemClick(mPublisher); //NPE on this line
}
});
}
}
}
// references to the images
private Integer[] mThumbIds = {
R.drawable.abc_news, R.drawable.abc_news_au,
R.drawable.aftenposten, R.drawable.aljazeera_english,
R.drawable.bbc, R.drawable.cbs_news,
R.drawable.cnn_news, R.drawable.entertainment_weekly,
R.drawable.espn, R.drawable.financial_post,
R.drawable.financial_times, R.drawable.fox_news,
R.drawable.fox_sports, R.drawable.ign,
R.drawable.independent, R.drawable.lequipe,
R.drawable.metro, R.drawable.msnbc,
R.drawable.mtvnews, R.drawable.nat_geo,
R.drawable.nbc_news, R.drawable.new_scientist,
R.drawable.new_york_magazine, R.drawable.talk_sport,
R.drawable.techradar, R.drawable.the_guardian,
R.drawable.the_nyt, R.drawable.wsj
};
private String[] mPublishers = {"ABC News", "ABC News (AU)", "Aftenposten", "AlJazeera (ENG)"
, "BBC", "CBS News", "CNN News", "Entertainment Weekly", "ESPN", "Financial Post",
"Financial Times", "Fox News", "Fox Sports", "IGN", "Independent", "L'Equipe",
"Metro", "MSNBC", "MTV News", "Nat. Geo.", "NBC News", "New Scientist",
"NY Magazine", "Talk Sport", "TechRadar", "The Guardian", "NYT",
"Wall Street Journal"};
}
The Fragment class and listener are initialized in the Main Activity:
public class MainActivity extends AppCompatActivity {
private ViewPager mPager;
private SectionsPageAdapter mSectionsPageAdapter;
private Context context;
private TabLayout tabLayout;
private OnItemClickListener publisherClickListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
// Login authentication code ....
//Set up the ViewPager and PagerAdapter
mSectionsPageAdapter = new
SectionsPageAdapter(getSupportFragmentManager());
mPager = findViewById(R.id.sources_container);
setupViewPager(mPager);
tabLayout = findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mPager);
}
//FIXME NPE on listener object if you rotate screen and touch icon
private void setupViewPager(ViewPager mPager) {
SectionsPageAdapter adapter = new SectionsPageAdapter(getSupportFragmentManager());
PublishersSlidePageFragment publishersSlidePageFragment = new PublishersSlidePageFragment();
publisherClickListener = new OnItemClickListener() {
@Override
public void onItemClick(String item) {
Toast.makeText(context, "You have selected " + item, Toast.LENGTH_SHORT).show();
}
};
publishersSlidePageFragment.setListener(publisherClickListener);
adapter.addFragment(publishersSlidePageFragment, "Publishers");
adapter.addFragment(new CategoriesSlidePageFragment(), "Categories");
adapter.addFragment(new CountriesSlidePageFragment(), "Countries");
mPager.setAdapter(adapter);
}
I find it very weird that it is fine to recreate the fragments when the screen is rotated but is unable to make a new clickListener to the fragment. I have searched StackOverflow and found Save interface (Listener) in onSaveInstanceState to be of use but I don't think in my situation it is good practice to save the listener. I want to make a new listener and just set it on the Recycler View within the fragment. I might be missing something trivial but can anyone advise?