How to fix "NullPointerException" on ViewHolder? - java

I'm trying to do the clickable RecyclerView, I watched a tutorial on how to do it, but it gave me interface NullPointerException when I click the RecyclerView.
public class ViewHolder extends RecyclerView.ViewHolder {
View mView;
private ViewHolder.ClickListener mClickListener;
public interface ClickListener{
void onItemClick(View view, int position);
}
public void setOnClickListener(ViewHolder.ClickListener clickListener){
mClickListener = clickListener;
}
public ViewHolder(View itemView){
super(itemView);
mView = itemView;
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mClickListener.onItemClick(v, getAdapterPosition()); //Line that called the Exception
}
});
}
}
Is it something that has to do with the interface? Do I need to pass the interface to adapter like this one suggests?
Here's the exception
java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.jobseeker.ViewHolder$ClickListener.onItemClick(android.view.View, int)' on a null object reference
at com.example.jobseeker.ViewHolder$1.onClick(ViewHolder.java:32)
...etc.
Here's the adapter
adapter = new FirebaseRecyclerAdapter<Model, ViewHolder>(options) {
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull final ViewGroup viewGroup, int i) {
final View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.row, viewGroup, false);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView mCompNameTv = view.findViewById(R.id.rCompNameTv);
TextView mDescTv = view.findViewById(R.id.rDescTv);
TextView mPosTv = view.findViewById(R.id.rPosTv);
ImageView mImageTv = view.findViewById(R.id.rImageIv);
String mCompName = mCompNameTv.getText().toString();
String mDesc = mDescTv.getText().toString();
String mPos = mPosTv.getText().toString();
Drawable mDrawable = mImageTv.getDrawable();
Bitmap mBitmap = ((BitmapDrawable)mDrawable).getBitmap();
Intent intent = new Intent(viewGroup.getContext(), DetailActivity.class);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
mBitmap.compress(Bitmap.CompressFormat.PNG, 80, stream);
byte[] bytes = stream.toByteArray();
intent.putExtra("image", bytes);
intent.putExtra("companyName", mCompName);
intent.putExtra("description", mDesc);
intent.putExtra("position", mPos);
startActivity(intent);
}
});
return new ViewHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull ViewHolder holder, int position, #NonNull Model model) {
holder.setDetails(model.getCompanyName(), model.getDescription(), model.getPosition(), model.getImage());
}
#Override
public int getItemCount() {
return super.getItemCount();
}
};

Most likely you have not initialized your ViewHolder class with the listener attribute. Use the setOnClickListener function to initialize the ViewHolder.
Hence your return statement from onCreateViewHolder might look like the following.
ViewHolder vh = new ViewHolder(view);
vh.setOnClickListener(clickListener); // Initialize a click listener in your Activity maybe and pass it here.
return vh;
I would like to suggest checking my answer here for a better understanding of how you can achieve the click listening in your RecyclerView.

Related

Open 1 different activity for each cardview

I'm trying to figure how to go to another activity when I click on one item of a cardview (with recyclerview).
I searched on internet and this website and tried several options, but it isn't working (I tried only for position 0, the others will be done the same). Here, with the card 0 i try to go to "selection_niveau".
1st try : inside OnClick:
public class adapter_categorie_solo extends RecyclerView.Adapter<adapter_categorie_solo.ViewHolder> {
List<String> titles;
List<Integer> images;
LayoutInflater inflater;
public adapter_categorie_solo(Context ctx, List<String> titles, List<Integer> images){
this.titles = titles;
this.images = images;
this.inflater = LayoutInflater.from(ctx);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.cardview_solo_categorie,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.title.setText(titles.get(position));
holder.gridIcon.setImageResource(images.get(position));
}
#Override
public int getItemCount() {
return titles.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView title;
ImageView gridIcon;
public ViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.textView2);
gridIcon = itemView.findViewById(R.id.imageView2);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Clicked -> " + getAdapterPosition(), Toast.LENGTH_SHORT).show();
if (getAdapterPosition()==0)
Intent intent = new Intent (this,selection_niveau.class);
}
});
}
}
}
I get this error :
error: variable declaration not allowed here
Intent intent = new Intent (this,selection_niveau.class);
2nd try : inside onBindViewHolder
public class adapter_categorie_solo extends RecyclerView.Adapter<adapter_categorie_solo.ViewHolder> {
List<String> titles;
List<Integer> images;
LayoutInflater inflater;
public adapter_categorie_solo(Context ctx, List<String> titles, List<Integer> images){
this.titles = titles;
this.images = images;
this.inflater = LayoutInflater.from(ctx);
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.cardview_solo_categorie,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.title.setText(titles.get(position));
holder.gridIcon.setImageResource(images.get(position));
Intent intent;
if(position == 0)
intent = new Intent(this, selection_niveau.class);
holder.itemView.setOnClickListener(new View.OnClickListener(){
#Override public void onClick(View v){
Context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return titles.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView title;
ImageView gridIcon;
public ViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.textView2);
gridIcon = itemView.findViewById(R.id.imageView2);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Clicked -> " + getAdapterPosition(), Toast.LENGTH_SHORT).show();
}
});
}
}
}
And I get this error :
error: no suitable constructor found for Intent(adapter_categorie_solo,Class<selection_niveau>)
intent = new Intent(this, selection_niveau.class);
^
constructor Intent.Intent(String,Uri) is not applicable
(argument mismatch; adapter_categorie_solo cannot be converted to String)
constructor Intent.Intent(Context,Class<?>) is not applicable
(argument mismatch; adapter_categorie_solo cannot be converted to Context)
I tried to change "this" to only "context", but it doesn't work either.
These are some of the solutions I found on internet and here, but none is working and I can't really understand why as being a beginner.
Inside the onClick() callback this keyword will points to the anonymous inner class of the View.OnClickListener; so you need to change to a context by using v.getContext() instead.
Apply this by replacing itemView.setOnClickListener with:
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Clicked -> " + getAdapterPosition(), Toast.LENGTH_SHORT).show();
if (getAdapterPosition()==0) {
Intent intent = new Intent (v.getContext(), selection_niveau.class);
}
}
});
Intent constructor requires a context as the first argument, but this in the ViewHolder refers to the instance of ViewHolder, So you need to get context from the view or use the context reference you got in the constructor of the adapter_categorie_solo.
public class ViewHolder extends RecyclerView.ViewHolder{
TextView title;
ImageView gridIcon;
public ViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.textView2);
gridIcon = itemView.findViewById(R.id.imageView2);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Clicked -> " + getAdapterPosition(), Toast.LENGTH_SHORT).show();
if (getAdapterPosition()==0)
{
Intent intent = new Intent (ctx,selection_niveau.class);
ctx.startActivity(intent);
}
}
});
}
}
Using switch instead of if will be an optimal approach if you want to start different activity based on click position.
use context in Intent instead of this
if(position == 0)
intent = new Intent(context, selection_niveau.class);

Context on Adapter for RecyclerView

I am trying to send the context from my java class to my adapter of a RecyclerView, so I can click on the elements and be redirected to an intent. Apparently, the context isn't right. I've commented its line and the app works, but as soon as i try to prove it, it starts crashing again.
I assume its a context thing, but it would be pretty helpful if there's another way to set a click method to my elements.
The exception it throw its a null pointer exception:
java.lang.RuntimeException: Unable to start activity:
java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.widget.Button.setOnClickListener(android.view.View$OnClickListener)'
on a null object reference
Here is the adapter code:
public class SubsidioAdapter extends RecyclerView.Adapter<SubsidioAdapter.SubsidioViewHolder> {
private Context context;
private List<Producto> productoList;
private LayoutInflater inflater;
public SubsidioAdapter(Context context, List<Producto> productoList) {
this.context = context;
this.productoList = productoList;
this.inflater = LayoutInflater.from(context);
}
#Override
public SubsidioViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = this.inflater.inflate(R.layout.producto_item, null);
return new SubsidioViewHolder(view);
}
#Override
public void onBindViewHolder(SubsidioViewHolder subsidioViewHolder, int position) {
subsidioViewHolder.imageView.setImageResource(this.productoList.get(position).getFoto());
subsidioViewHolder.textView1.setText(this.productoList.get(position).getNombre());
subsidioViewHolder.textView2.setText(this.productoList.get(position).getDescripcion());
subsidioViewHolder.textView3.setText(this.productoList.get(position).getPrecio());
subsidioViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
context.startActivity(new
Intent(context, ModificarProductoActivity.class));
}
});
}
#Override
public int getItemCount() {
return this.productoList.size();
}
public class SubsidioViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView textView1;
TextView textView2;
TextView textView3;
public SubsidioViewHolder(View itemView) {
super(itemView);
this.imageView = itemView.findViewById(R.id.fotoSub);
this.textView1 = itemView.findViewById(R.id.descSub);
this.textView2 = itemView.findViewById(R.id.nombreSub);
this.textView3 = itemView.findViewById(R.id.precioSub);
}
}
}
set an id to root element of adapter item layout, then find that view by id and use it instead of itemView ,
instead of :
subsidioViewHolder.itemView.setOnClickListener(new View.OnClickListener(){
public void onClick(View view) {
...
}
});
use:
subsidioViewHolder.rootLayout.setOnClickListener(new View.OnClickListener(){
public void onClick(View view) {
...
}
});
. and remember you can get context in recyclerView adapter inside onCreateViewHolder() without using constructor like this:
#Override
public SubsidioViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
context=viewGroup.getContext();
View view = this.inflater.inflate(R.layout.producto_item, null);
return new SubsidioViewHolder(view);
}
although it seems like that your problem is with

How to use Strings in RecyclerView?

I'm trying to use a different String, for each item in the RecyclerView, but my program always takes the first String I've put in the ArrayList, no matter which item I click on:
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, final int viewType) {
final View v = LayoutInflater.from(mContext).inflate(R.layout.cardview, null);
final ContentMain contentMain = mData.get(viewType);
v.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(v.getContext(), contentMain.getMyContent(),
Toast.LENGTH_LONG).show();
}
}
ViewHolder vHolder = new ViewHolder(v);
return vHolder;
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final ContentMain contentMain = mData.get(position);
holder.myContent= mData.get(position).getMyContent();
}
You shouldn't bind the onClickListener in onCreateViewHolder. Purpose of onCreateViewHolder is to construct the ViewHolder and not binding information to it.
Following code in onCreateViewHolder:
final ContentMain contentMain = mData.get(viewType);
Will always return first element because your viewType is 0. It doesn't describe the position.
Move this code in onBindViewHolder as follows:
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final ContentMain contentMain = mData.get(position);
holder.myContent= mData.get(position).getMyContent();
holder.itemView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(v.getContext(), contentMain.getMyContent(),Toast.LENGTH_LONG).show();
}
});
}
Get you data inside onClick().
You are getting data using position while creating ViewHolder. It will give wrong position since recyclerview must be calculating layout measurements -
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
final View v = LayoutInflater.from(mContext).inflate(R.layout.cardview, parent, false); //Avoid inflate layout with null parent
v.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
final ContentMain contentMain = mData.get(getAdapterPosition());
Toast.makeText(v.getContext(), contentMain.getMyContent(), Toast.LENGTH_LONG).show();
}
}
ViewHolder vHolder = new ViewHolder(v);
return vHolder;
}
EDIT:
Probably you should define click listeners inside ViewHolder -
public static ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View v) {
v.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Do you stuffs here...
}
}
}
}

How to create an Intent within a fragment class

I have managed to create a tabbed activity using recycle view and have made a list-view in each fragment, now I have the lists I need to make the cards in the fragment clickable and create an intent to start a new activity. I have tried on-click method and onListItemClick even though I knew that would not work.
Basically I need to create one more layer of lists for each fragment which will lead to a list of actions (webviews, Maps, and information).
Example Code is below
ublic class LOneFragment extends Fragment {
public LOneFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_blank, container, false);
RecyclerView rv = (RecyclerView) rootView.findViewById(R.id.rv_recycler_view);
rv.setHasFixedSize(true);
MyAdapter adapter = new MyAdapter(new String[]{"League One News", "AFC Wimbledon", "Blackburn Rovers", "Blackpool", "Bradford City", "Bristol Rovers", "Bury", "Charlton Athletic", "Doncaster Rovers", "Fleetwood Town",
"Gillingham", "Milton Keynes Dons", "Northampton Town", "Oldham Athletic", "Oxford United", "Peterborough United", "Plymouth Argyle", "Portsmouth", "Rochdale", "Rotherham United",
"Scunthorpe United", "Shrewsbury Town", "Southend United", "Walsall", "Wigan Athletic"});
rv.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
rv.setLayoutManager(llm);
return rootView;
}
public void onListItemClick(ListView l, View v, int position, long id) {
if (position == 3) {
Intent intent = new Intent(getContext(), Bradford.class);
startActivity(intent);
}
My adaptor class
public class MyAdapter extends RecyclerView.Adapter {
private String[] mDataset;
public static class MyViewHolder extends RecyclerView.ViewHolder{
public CardView mCardView;
public TextView mTextView;
public MyViewHolder(View v){
super(v);
mCardView = (CardView) v.findViewById(R.id.card_view);
mTextView = (TextView) v.findViewById(R.id.tv_text);
}
}
public MyAdapter(String[] myDataset){
mDataset = myDataset;
}
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_item, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position){
holder.mTextView.setText(mDataset[position]);
}
#Override
public int getItemCount() { return mDataset.length; }
}
This works perfect for me.
1) Create method in viewholder class which will take position, get array item from position and use the way you like.
2) Access it in onBindViewHolder method with itemView instance which is the full row at given position,
public class MyAdapter extends RecyclerView.Adapter {
private String[] mDataset;
public MyAdapter(String[] myDataset){
mDataset = myDataset;
}
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_item, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position){
holder.mTextView.setText(mDataset[position]);
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.bindCliks(groupItem);
}
});
#Override
public int getItemCount() { return mDataset.length; }
}
public static class MyViewHolder extends RecyclerView.ViewHolder{
public CardView mCardView;
public TextView mTextView;
public MyViewHolder(View v){
super(v);
mCardView = (CardView) v.findViewById(R.id.card_view);
mTextView = (TextView) v.findViewById(R.id.tv_text);
}
public void bindCliks(int position) {
Intent intent = new Intent(context,
OtherActivity.class);
//use number here to get your string item and use it to other activity.
context.startActivity(intent);
}
}

How to pass data from Recyclerview onClickLister to other Activity

I want to pass data from onclick recycler view main activity to other activity(exoplayer).
I created onimageclicklistener class and then I declared my onimageclicklistener in the adapter class. Then I implement that listener in main activity and implemented on image click method, now I am blank and do not know what do.
I will appreciate if someone helps.
interface class:
public interface OnImageClickListener
{
void onImageClick(String imageData);
}
adapter:
public class SongAdapter extends RecyclerView.Adapter {
private Context context;
private List<Datum> datums;
//imagelistener define
private OnImageClickListener onImageClickListener;
//3rd argument is listener
public SongAdapter(Context context, List<Datum> datums, OnImageClickListener onImageClickListener) {
this.context = context;
this.datums = datums;
this.onImageClickListener = onImageClickListener;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Datum datum = datums.get(position);
Glide.with(context).load(datums.get(position).getThumbnail()).into(holder.thumbnail);
holder.title.setText(datum.getTitle());
holder.artist.setText(datum.getArtist());
// holder.duration.setText(datum.getDuration());
holder.duration.setText(Integer.toString(datum.getDuration()));
//imageview interface pass
holder.thumbnail.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onImageClickListener.onImageClick(datum.getUrl());
}
});
}
#Override
public int getItemCount() {
return datums.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView thumbnail;
TextView title;
TextView artist;
TextView duration;
public ViewHolder(#NonNull View itemView) {
super(itemView);
thumbnail = (ImageView) itemView.findViewById(R.id.thumbnail);
title = (TextView) itemView.findViewById(R.id.title);
artist = (TextView) itemView.findViewById(R.id.artist);
duration = (TextView) itemView.findViewById(R.id.duration);
}
}
}
Mainactivity:
//imageclick listener
#Override
public void onImageClick(String imageData) {
Intent i = new Intent(MainActivity.this,exoplayer.class);
startActivity(i);
}
I want to send data to exoplayer class.
Using Intent Extras
#Override
public void onImageClick(String imageData) {
Intent i = new Intent(MainActivity.this,exoplayer.class);
intent.putExtra("YOUR_KEY_STRING", imageDate);
startActivity(i);
}
And then from exoplayer Activity, you retrieve it in the onCreate() method as follows :
String imageData = getIntent().getStringExtra("YOUR_KEY_STRING");
Using bundle
#Override
public void onImageClick(String imageData) {
Intent i = new Intent(MainActivity.this,exoplayer.class);
Bundle bundle = new Bundle();
bundle.putString("imageDataKey", imageData);
intent.putExtra("YOUR_BUNDLE_KEY", bundle);
startActivity(i);
}
And then from your exoactivity you retrieve it as follows :
if (getIntent().getExtras() != null) {
Bundle bundle = getIntent().getStringExtra("YOUR_BUNDLE_KEY");
String imageData = bundle.getString("imageDataKey");
}
Using intent we can send data
//imageclick listener
#Override
public void onImageClick(String imageData) {
Intent i = new Intent(MainActivity.this,exoplayer.class);
i.putExtra("data",imageData);
startActivity(i);
}

Categories

Resources