Not able to use ViewHolder right in RecyclerView - java

After a long time i am trying to use a recylcerview and when i am generating the onBindViewHolder methode, it is using RecyclerView.ViewHolder as a holder. but to use the holder on the Textview mFach, i need to use my ViewHolder, that i created below.
private ArrayList<DatenItem> mDatenList;
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.daten_item, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
DatenItem datenItem = mDatenList.get(position);
holder.mFach.setText(datenItem.getmFach()); //both not possible because mfach and mDatum doesnt exist (it is red)
holder.mDatum.setText(datenItem.getmDate());
}
#Override
public int getItemCount() {
return mDatenList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView mFach;
private final TextView mDatum;
public ViewHolder(#NonNull View itemView) {
super(itemView);
mFach = itemView.findViewById(R.id.fachView);
mDatum = itemView.findViewById(R.id.datumView);
}
}
}```
btw i deleted the unnecessary parts of the code in the code above

The problem is that you use incorrect generic type. You need to change your adapter declaration and corresponding functions to use your ViewHolder instead of base one:
class YourAdapter() : RecyclerView.Adapter<YourAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): YourAdapter.ViewHolder
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
}
}

i think because 'mFach' and 'mDatum' are private you can't do that.
also you can use this sample code in onBindViewHolder :
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position)
{
DatenItem datenItem = mDatenList.get(position);
holder.fill(datenItem);
}
and change you'r view holder like this :
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView mFach;
private final TextView mDatum;
public ViewHolder(#NonNull View itemView)
{
super(itemView);
mFach = itemView.findViewById(R.id.fachView);
mDatum = itemView.findViewById(R.id.datumView);
}
public void fill(DatenItem datenItem)
{
holder.mFach.setText(datenItem.getmFach());
holder.mDatum.setText(datenItem.getmDate());
}}

Related

What is the right way of Android View Binding in the RecyclerView adapter class?

Here is the code I used in my RecycleView adapter class. I don't know this is the right way or not to use View Binding. If you have a better solution answer me. Thank you.
#Override
public CategoryAdapter.MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.common_circle_image, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull CategoryAdapter.MyViewHolder holder, final int position) {
holder.binding.img.setBackgroundResource(addAdapterData.get(position).getItemUrl());
holder.binding.txt.setText(addAdapterData.get(position).getItemName());
}
#Override
public int getItemCount() {
return addAdapterData.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
CommonCircleImageBinding binding;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
binding = CommonCircleImageBinding.bind(itemView);
binding.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
commonItemClick.onItemClick(getAdapterPosition(),"");
}
});
}
}
Also, I want to know is it right to use R.layout.layout_name and ViewBinding in the same class.
What you need to do is pass the generated binding class object to the holder class constructor. In your example, You have common_circle_image XML file for RecyclerView item and the generated class is CommonCircleImageBinding so like this you use the onCreateViewHolder to pass the generated binding class to the ViewHolder class
#NonNull
#Override
public CategoryAdapter.MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
CommonCircleImageBinding itemBinding = CommonCircleImageBinding .inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new MyViewHolder(itemBinding);
}
and use the holder class like this so you can use these fields in onBindViewHolder
static class MyViewHolder extends RecyclerView.ViewHolder {
private TextView txt;
private ImageView img;
MyViewHolder(CommonCircleImageBinding itemBinding) {
super(itemBinding.getRoot());
img = itemBinding.img ;
txt = itemBinding.txt ;
}
}
Here is full view binding recycler view code in java, you can do as like:
package com.jbws.myviewbindingdemo.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.jbws.myviewbindingdemo.databinding.RowXmlViewBinding;
import com.jbws.myviewbindingdemo.pojo.ModelObject;
import java.util.ArrayList;
public class RecyclerViewListAdapter extends RecyclerView.Adapter<RecyclerViewListAdapter.ViewHolder> {
public ArrayList<ModelObject> modelObjectArrayList;
public RecyclerViewListAdapter(ArrayList<ModelObject> modelObjectArrayList) {
this.modelObjectArrayList = modelObjectArrayList;
}
#NonNull
#Override
public RecyclerViewListAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ViewHolder(RowXmlViewBinding.inflate(LayoutInflater.from(parent.getContext()),
parent, false));
}
#Override
public void onBindViewHolder(#NonNull RecyclerViewListAdapter.ViewHolder holder, final int position) {
ModelObject modelObject = modelObjectArrayList.get(position);
holder.rowXmlViewBinding.txtObjectName.setText(modelObject.getFullName());
holder.rowXmlViewBinding.btnUpdateName.setOnClickListener(view -> {
Log.i("LOG_TAG", "Full Name: " + modelObject.getFullName);
});
}
#Override
public int getItemCount() {
return modelObjectArrayList == null ? 0 :
modelObjectArrayList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private RowXmlViewBinding rowXmlViewBinding;
public ViewHolder(RowXmlViewBinding rowXmlViewBinding) {
super(rowXmlViewBinding.getRoot());
this.rowXmlViewBinding = rowXmlViewBinding;
}
}
}
For the folks looking for a solution in Kotlin, here it is:
It's a minimal example, where the adapter gets an array of Strings and displays each of the them in a layout called recyclerview_item in a TextView called itemTextView.
It's based on #SomeshKumar's answer and answers #Vijay Villiers question on how to get rid of the private TextView txt;
Edit: New Version:
I noticed the generated ...Binding has a .bind() function, so let's use it. (I guess it might be less resource-heavy?)
class SampleAdapter(private val context: Context, private val content: Array<String>) :
RecyclerView.Adapter<SampleAdapter.CustomViewHolder>()
{
class CustomViewHolder(view: View) : RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int) =
CustomViewHolder(
// Alternatively inflate like usual, if you don't need binding
RecyclerviewItemBinding
.inflate(LayoutInflater.from(context), viewGroup, false)
.root
)
override fun getItemCount() = content.size
override fun onBindViewHolder(viewHolder: CustomViewHolder, position: Int)
{
RecyclerviewItemBinding.bind(viewHolder.itemView).apply{
itemTextView.text = content[position]
}
}
}
Edit: Old Version:
class SampleAdapter(private val context: Context, private val content: Array<String>) :
RecyclerView.Adapter<SampleAdapter.CustomViewHolder>()
{
class CustomViewHolder(var viewBinding: RecyclerviewItemBinding) :
RecyclerView.ViewHolder(viewBinding.root)
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int) =
CustomViewHolder(
RecyclerviewItemBinding
.inflate(LayoutInflater.from(context), viewGroup, false)
)
override fun getItemCount() = content.size
override fun onBindViewHolder(viewHolder: CustomViewHolder, position: Int)
{
viewHolder.viewBinding.apply {
itemTextView.text = content[position]
}
}
}
Here is full recycler view adapter class in java :
public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.MyViewHolder> {
private List<Note> notes;
private ItemNotesBinding notesBinding;
public NotesAdapter(List<Note> notes) {
this.notes = notes;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
notesBinding = ItemNotesBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new MyViewHolder(notesBinding);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
Note note = notes.get(position);
notesBinding.tvTitle.setText(note.getNote());
}
#Override
public int getItemCount() {
return notes.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
ItemNotesBinding notesBinding;
public MyViewHolder(#NonNull ItemNotesBinding binding) {
super(binding.getRoot());
notesBinding = binding;
}
}
}
You can create CommonCircleImageBinding directly in onCreateViewHolder by CommonCircleImageBinding.inflate(LayoutInflater.from(parent.getContext()))
Then pass it to MyViewHolder

What is RecyclerView.Adapter<MyAdapter.MyViewHolder> and how it is different from RecyclerView.Adapter in Android?

I'm learning RecyclerView and in developer's site the Adapter class extends RecyclerView.Adapter<MyAdapter.MyViewHolder>. The implementation shows:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private String[] mDataset;
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView textView;
public MyViewHolder(TextView v) {
super(v);
textView = v;
}
}
public MyAdapter(String[] myDataset) {
mDataset = myDataset;
}
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
TextView v = (TextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.my_text_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.textView.setText(mDataset[position]);
}
#Override
public int getItemCount() {
return mDataset.length;
}
}
And in this tutorial, the adapter class extends RecyclerView.Adapter. The implementation is:
public class SimpleAdapter extends RecyclerView.Adapter {
private List<SimpleViewModel> models = new ArrayList<>();
public SimpleAdapter(final List<SimpleViewModel> viewModels) {
if (viewModels != null) {
this.models.addAll(viewModels);
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
return new SimpleViewHolder(view);
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
((SimpleViewHolder) holder).bindData(models.get(position));
}
#Override
public int getItemCount() {
return models.size();
}
#Override
public int getItemViewType(final int position) {
return R.layout.item_simple_itemview;
}
}
So what is the difference between RecyclerView.Adapter<MyAdapter.MyViewHolder> and RecyclerView.Adapter and what is the syntax<> in RecyclerView.Adapter<MyAdapter.MyViewHolder> means in this case? I know it stands for generics. When should I use this?
So what is the difference between RecyclerView.Adapter< MyAdapter.MyViewHolder> and RecyclerView.Adapter and what is the syntax<> in RecyclerView.Adapter means in this case? I know it stands for generics. When should I use this?
First, let's review the definition of the Adapter class. It's defined within RecyclerView as so:
public abstract static class Adapter<VH extends ViewHolder>
So what does the "generics" part of this mean? It means the Adapter class operates on some class type (VH) that must be a descendant of the class ViewHolder. This is known as a Bounded Type and ensures that the type of of class the adapter is working with is guaranteed to be a type of ViewHolder.
As you see with the example on the developer's site you posted, when you create your own adapter that specifies the type of the ViewHolder as MyViewHolder, then the methods you have to override in the adapter explicitly declare that type in their signatures. For example, the return type for onCreateViewHolder is explicitly set to MyViewHolder.
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
TextView v = (TextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.my_text_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
Alternatively, the tutorial you posted does not specify a type.
public class SimpleAdapter extends RecyclerView.Adapter
When you extend a class that expects a generic type and don't specify one, the compiler defaults to the least common denominator - that is, the class type lowest in the inheritance hierarchy that will guarantee the class can be used as designed. Because the Adapter's generic type is defined as VH extends ViewHolder, the compiler knows that the class type must be at least a ViewHolder and defaults to that. Hence, the same method overriden for this example returns RecyclerView.ViewHolder instead of MyViewHolder:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
return new SimpleViewHolder(view);
}
If RecyclerView.Adapter was defined as Adapter<VH> (no bounded type), and you extended it without specifying the type, the type would default to Object (the root class of all Java classes):
#Override
public Object onCreateViewHolder(final ViewGroup parent, final int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
return new SimpleViewHolder(view);
}
So, finally, why use one instead of the other? Generally speaking: you should always specify the type for a generic class. This guarantees you type-safety. In the tutorial example, you could return the wrong type of view holder which would compile but crash at runtime:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
// Returning wrong view holder, but because it still extends ViewHolder and
// that is all this method requires, this compiles
return new SomeOtherViewHolder(view);
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
// Above we created a SomeOtherViewHolder but here we're expecting a SimpleViewHolder
// This will crash trying to cast to the wrong type
((SimpleViewHolder) holder).bindData(models.get(position));
}
It's impossible to make this mistake in the first example though because the ViewHolder type is declared explicitly:
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
TextView v = (TextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.my_text_view, parent, false);
// Trying to return SomeOtherViewHolder when method expects MyViewHolder
// Compiler's like (*waves finger*) "nuh-uh, not on my watch" and fails
// to compile
SomeOtherViewHolder vh = new SomeOtherViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
// Notice here that holder is explicitly of MyViewHolder type and casting
// is not necessary! That's TYPE SAFETY y'all.
holder.textView.setText(mDataset[position]);
}
Because you specify the explicit, concrete type you expect, you cannot accidentally return the wrong type, you do not need to cast, and you can leverage all of the public methods defined in your custom ViewHolder class.
Hope that helps!

onbindViewHolder is not accepting view holder variables

For some reason the on bindviewholder is not accepting the variables created by Viewholder(holder.name and holder.dexription). I have tried everything to fix it but nothing has worked Here is the code if you could help I would be grateful.
public class CityAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<City> cities;
public CityAdapter(ArrayList<City> cities) {
this.cities =cities;
}
#Override
public int getItemCount() {
if (cities != null) {
return cities.size();
} else {
return 0;
}
}
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent,int viewType){
View v =(View) LayoutInflater.from(parent.getContext()).inflate(R.layout.items,parent,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
City city = cities.get(position);
holder.name.setText(city.getName());
holder.description.setText(city.getDescription());
}
static class ViewHolder extends RecyclerView.ViewHolder {
public View view;
public TextView name;
public TextView description;
public ImageView image;
public ViewHolder(#NonNull View itemView) {
super(itemView);
this.name=(TextView) itemView.findViewById(R.id.name);
this.description=(TextView) itemView.findViewById(R.id.description);
this.image=(ImageView) itemView.findViewById(R.id.image);
}
}
}
You are extending from wrong class
change CityAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
to
CityAdapter extends RecyclerView.Adapter<CityAdapter.ViewHolder>
And change your "onBindViewHolder" method's first argument from
RecyclerView.ViewHolder
to
ViewHolder

Unable to declare variable

Why I'm unable to declare variable in MessageViewHolder class? I want to findviewById for my xml file. BTW I'm running latest version of Android Studio in latest 64 bit Ubuntu. But, surely there is no problem with version. I tried with some other variable. Same problem!
#NonNull
#Override
public MessageViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.message_single_layout, parent, false);
return new MessageViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MessageViewHolder holder, int position) {
Messages messages = messagesList.get(position);
holder.chatText.setText(messages.getMessage());
}
#Override
public int getItemCount() {
return messagesList.size();
}
public class MessageViewHolder extends RecyclerView.ViewHolder {
public TextView chatText;
public CircleImageView chatImage;
public MessageViewHolder(View itemView) {
super(itemView);
}
chatText =
chatImage
}
}
You need to do findViewById inside MessageViewHolder() Using itemView
Try this
public class MessageViewHolder extends RecyclerView.ViewHolder {
public TextView chatText;
public CircleImageView chatImage;
public MessageViewHolder(View itemView) {
super(itemView);
chatText =itemView.findViewById(R.id.chatText);
chatImage=itemView.findViewById(R.id.chatImage);
}
}
In view holder you take id's from view
So it is -
public MessageViewHolder(View itemView) {
super(itemView);
chatText = itemView.findViewById(R.id.*);
chatImage = itemView.findViewById(R.id.*);
}

ButterKnife onClick listener inside ViewHolder method [duplicate]

This question already has answers here:
butterknife #OnClick inside ViewHolder in CustomAdapter
(4 answers)
Closed 5 years ago.
What would be the proper way of adding Butter Knife's #OnClick to the view in this method?
private List<Foo> foos;
private RecyclerView recyclerView;
private MapActivity mapView;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_foo, parent,
false);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int selectedPosition = recyclerView.getChildAdapterPosition(v);
mapView.onFooSelected(foos.get(selectedPosition));
}
});
return new FooInfoViewHolder(view);
}
The way this question is different is that I want to bind #OnClick to the FooInfoViewHolder itself.
Use ViewHolder class and inside of that class we can handle clicks for butter knife.
Below is the just example for understanding.
public class YourAdapter extends RecyclerView.Adapter<YourAdapter.ViewHolder> {
private List<Data> list;
public YourAdapter(List<Data> list) {
this.list = list;
}
#Override
public YourAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new ViewHolder(inflater.inflate(R.layout.list_item_layout, parent, false));
}
#Override
public void onBindViewHolder(YourAdapter.ViewHolder holder, int position) {
Data item = list.get(position);
holder.tvTitle1.setText(item.getTitle1());
}
#Override
public int getItemCount() {
int count = 0;
if (null != list) {
count = list.size();
}
return count;
}
public class ViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.tvVRValue1)
TextView tvTitle1;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
#OnClick(R.id.tvVRValue1)
public void onClickYourFunction(){
//Here your onclick method goes
}
}
}
Hope it will help you. Cheers !!!
I figured out what was wrong. Actually I think it's a slightly different answer than the possible duplicates.
The key was to implement the #OnClick on the ViewHolder ITSELF (not, like in the other question, on children of the view):
private List<Foo> foos;
private RecyclerView recyclerView;
private MapActivity mapView;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_foo, parent,
false);
return new FooInfoViewHolder(view);
}
And then the FooInfoViewHolder:
public class FooInfoViewHolder extends RecyclerView.ViewHolder {
#OnClick
public void onClick(View v) {
int selectedPosition = recyclerView.getChildAdapterPosition(v);
mapView.onFooSelected(foos.get(selectedPosition));
}
public FooInfoViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}

Categories