I saw many methods to write Recyclerview. But I do not know what is the best practice way in terms of performance.
I have two methods to write a Recyclerview, are they the same or is there in difference?
First method is to write it in separate Adapter class
Adapter.java
public class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder> {
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return null;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder myViewHolder, int i) {
}
#Override
public int getItemCount() {
return 0;
}
class MyViewHolder extends RecyclerView.ViewHolder{
public MyViewHolder(#NonNull View itemView) {
super(itemView);
}
}
}
Second method is to write it like this inside the MainActivity or Fragment
recyclerView.setAdapter(new RecyclerView.Adapter() {
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return null;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder viewHolder, int i) {
}
#Override
public int getItemCount() {
return 0;
}
});
}
public class MyViewHolder extends RecyclerView.ViewHolder{
public MyViewHolder(#NonNull View itemView) {
super(itemView);
}
}
The adapter should be implemented as a separated class, because it makes re-using it easier:
public class YourAdapter extends RecyclerView.Adapter<Adapter.MyViewHolder> {
private ArrayList<YourModel> list = new ArrayList();
public YourAdapter(ArrayList<YourModel> list){
this.list = list;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
//return the viewholder
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder myViewHolder, int i) {
//deal with data
}
#Override
public int getItemCount() {
//return the list number
}
class MyViewHolder extends RecyclerView.ViewHolder{
public MyViewHolder(#NonNull View itemView) {
super(itemView);
//bind the views
}
}
}
Than in your Activity/Fragment you can use it like this :
//after you have initialized recyclerview and added the layoutmanager
//prepare the list for the adapter
recyclerView.setAdapter(new YourAdapter(yourList));
This way you can reuse it in more than one Activity/Fragment. This pattern of reusable code should be preferred because you don't have to create the classes multiple times as anonymous classes - if needed.
the best way is to set adapter class outside main class as your explain just use your first class
Related
I want to retrieve the data from a SQLite database and show it in the RecyclerView but it shows an error
((Adapter class))
public class myAdapert extends RecyclerView.Adapter<myAdapert.myViewHolder> {
ArrayList<Model_class> modelClassArrayList;
public myAdapert(ArrayList<Model_class> modelClassArrayList) {
this.modelClassArrayList = modelClassArrayList;
}
#NonNull
#Override
public myViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View customView = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.custom_row,viewGroup,false);
myViewHolder myviewHolder = new myViewHolder(customView);
return myviewHolder;
}
#Override
public void onBindViewHolder(#NonNull myViewHolder myViewHolder, int i) {
Model_class model_class = modelClassArrayList.get(i);
myViewHolder.author.setText(model_class.getAuthor());
myViewHolder.title.setText(model_class.getTitle());
myViewHolder.cost.setText(model_class.getCost());
myViewHolder.quantity.setText(model_class.getQuantity());
}
#Override
public int getItemCount() {
return modelClassArrayList.size();
}
public class myViewHolder extends RecyclerView.ViewHolder {
TextView author,title;
TextView cost,quantity;
public myViewHolder(#NonNull View itemView) {
super(itemView);
author = itemView.findViewById(R.id.authortxt);
title = itemView.findViewById(R.id.titleTxt);
cost = itemView.findViewById(R.id.costTxt);
quantity = itemView.findViewById(R.id.quantityTxt);
}
}
As cost and quantity are numbers, I suppose, you should convert it to String explicitly, to prevent resourse manager from loading drawables:
myViewHolder.cost.setText(String.valueOf(model_class.getCost()));
myViewHolder.quantity.setText(String.valueOf(model_class.getQuantity()));
I'm trying to develop a project in accordance with MVP standarts. In the guide i followed, author created separate ViewHolder class. I tried to do the same, but Adapter refuses to work with separate ViewHolder.
There is 2 errors
Cannot resolve symbol 'LessonCardView'
'onCreateViewHolder(ViewGroup, int)' in RVAdapter clashes with 'onCreateViewHolder(ViewGroup, int)' in 'android.support.v7.widget.RecyclerView.Adapter'; attempting to use incompatible return type
RVAdapter.java
public class RVAdapter extends RecyclerView.Adapter<RVAdapter.LessonCardViewHolder> {
private String[] mDataset;
public RVAdapter(String[] dataset) {
mDataset = dataset;
}
#Override
public LessonCardViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new LessonCardViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.lessons_item_card, parent, false));
}
#Override
public void onBindViewHolder(LessonCardViewHolder holder, int position) {
}
#Override
public int getItemCount() {
return mDataset.length;
}
}
LessonCardViewHolcer.java
public class LessonCardViewHolder extends RecyclerView.ViewHolder implements LessonCardView {
private final TextView lessonCardText;
public LessonCardViewHolder(View itemView) {
super(itemView);
lessonCardText = (TextView) itemView.findViewById(R.id.lesson_card_view);
}
#Override
public void setLessonCardText(String text) {
lessonCardText.setText(text);
}
}
I created subclass ViewHolder that inherited from LessonCardView in RVAdapter. Errors disappeared. But i'm not sure if this the right way. If it works for someone else, then i'm doing something wrong.
Change your adapter declaration from this
public class RVAdapter extends RecyclerView.Adapter<RVAdapter.LessonCardViewHolder> {
to this
// import here your view holder
public class RVAdapter extends RecyclerView.Adapter<LessonCardViewHolder> {
It looks like from how you're providing the snippets that these classes are in different files, or not correctly nested within the same file.
Have you tried using RecyclerView.Adapter<LessonCardViewHolder> rather than RecyclerView.Adapter<RVAdapter.LessonCardViewHolder>?
LessonCardViewHolder should be a nested class in RVAdapter
Here is the complete solution - Recyclerview Adapter class example
public class IAdapter extends RecyclerView.Adapter<IAdapter.ViewHolder> {
Context context;
ArrayList<Model> modelList;
public ImagesAdapter(Context context,ArrayList<Model> modelList) {
this.context=context;
this.modelList=modelList;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_items, parent, false);
return new ViewHolderImages(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Model model=photo.get(position);
userViewHolder.textView.setText(model.getTitle());
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView textView;
public ViewHolderImages(View itemView) {
super(itemView);
textView=(TextView)itemView.findViewById(R.id.textView);
}
}
#Override
public int getItemCount() {
return modelList.size();
}
}
Currently, I work on custom multi-view types for RecycleView.
I found many solutions for this problem, but I will share my way.
The different is use enum for define ViewType.
See my answer for more detail.
(Just want to share).
UPDATE
I recommend read this article: Writing Better Adapter
Here is the way I custom multi-view types for RecyclewView.
I will show the code first, then explain bellow.
#SuppressWarnings("unchecked")
public class BaseAdapter extends RecyclerView.Adapter<BaseAdapter.ViewHolder> {
private final List<Wrapper<?>> items;
public BaseAdapter() {
this.items = new ArrayList<>();
}
#Override
public int getItemViewType(int position) {
return items.get(position).viewType;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return ViewType.values()[viewType].onCreateViewHolder(parent);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.onBind(items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
public void simulateAddItems() {
items.add(new Wrapper<>(ViewType.USER.viewTYpe(), new User()));
items.add(new Wrapper<>(ViewType.USER.viewTYpe(), new User()));
items.add(new Wrapper<>(ViewType.USER.viewTYpe(), new User()));
items.add(new Wrapper<>(ViewType.BOTTOM.viewTYpe(), new BottomData()));
notifyDataSetChanged();
}
public enum ViewType {
USER {
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
return new UserViewHolder(itemView);
}
},
BOTTOM {
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
return new BottomViewHolder(itemView);
}
};
public abstract ViewHolder onCreateViewHolder(ViewGroup parent);
public int viewTYpe() {
return ordinal();
}
}
static class Wrapper<T> {
final int viewType;
final T data;
public Wrapper(int viewType, T data) {
this.viewType = viewType;
this.data = data;
}
}
public static class ViewHolder<T> extends RecyclerView.ViewHolder {
private T item;
public ViewHolder(View itemView) {
super(itemView);
}
public void onBind(T item) {
}
}
public static class UserViewHolder extends ViewHolder<User> {
public UserViewHolder(View itemView) {
super(itemView);
}
#Override
public void onBind(User item) {
super.onBind(item);
}
}
public static class BottomViewHolder extends ViewHolder<BottomData> {
public BottomViewHolder(View itemView) {
super(itemView);
}
#Override
public void onBind(BottomData item) {
super.onBind(item);
}
}
static class User {
// user fields & method
}
static class BottomData {
// bottom data fields & method
}
}
You can see the adapter have something special:
The data of Adapter is generic & extends from Wrapper class. The Wrapper class simply POJO class contain two fields viewType & item data. The viewType will be passing via getItemViewType(int position) and the item is data for each view holder.
The generic ViewHolder with T is kind of data for the view holder. You can see two custom view holder UserViewHolder and BottomViewHolder extends from ViewHolder with this data. (So in onBind of each view holder will have exact data they want to use -> no need to cast. Because we already in cast).
The enum ViewType contain all view type of adapter. Also, in view type, we have method ViewHolder onCreateViewHolder(ViewGroup parent) so we can easier make viewHolder from ViewType. I think it is easier for our eyes to see the Holder relative to the ViewType.
Also, we have method viewType() it simply return ordinal() of enum.
As a reference in java docs that method may not use. But in this case, I think it is fair enough to use (because it is unique).
So in the method onCreateViewHolder of Adapter we can simple call:
return ViewType.values()[viewType].onCreateViewHolder(parent);
and in onBindViewHolder we will call holder.onBind(items.get(position));.
Finally, I made simulateAddItems for demo how we add item to the adapter.
Here is some experiences while working with custom view type for RecycleView. I hope it useful & help other people.
Is it possible to call same adapter in two activity for different different work
Here is my RecyclerAdapter:
Only difference when calling from two activity is in this line: From Activity1.java it is:
holder.Name.setText(arrayList.get(position).getName());
And from Activity2.java
holder.Name.setText(arrayList.get(position).getVehicle());
All other code is same how can I use same adapter for different different work.
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private ArrayList<Contact> arrayList= new ArrayList<>();
public RecyclerAdapter(ArrayList<Contact> arrayList){
this.arrayList= arrayList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view,parent,false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder (MyViewHolder holder, int position) {
holder.Name.setText(arrayList.get(position).getName());
int sync_status = arrayList.get(position).getSync_status();
if(sync_status== DbContact.SYNC_STATUS_OK){
holder.Sync_Status.setImageResource(R.drawable.success);
}
else {
holder.Sync_Status.setImageResource(R.drawable.stopwatch);
}
}
#Override
public int getItemCount() {
return arrayList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder{
ImageView Sync_Status;
TextView Name;
public MyViewHolder(View itemView) {
super(itemView);
Sync_Status=(ImageView)itemView.findViewById(R.id.imgSync);
Name=(TextView)itemView.findViewById(R.id.txtName);
}
}
}
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private ArrayList<Contact> arrayList= new ArrayList<>();
private int whichActivity;
public RecyclerAdapter(ArrayList<Contact> arrayList, int activity){
whichActivity = activity;
this.arrayList= arrayList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view,parent,false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder (MyViewHolder holder, int position) {
if(whichActivity == 0) {
holder.Name.setText(arrayList.get(position).getName());
}
else {
holder.Name.setText(arrayList.get(position).getVehicle());
}
int sync_status = arrayList.get(position).getSync_status();
if(sync_status== DbContact.SYNC_STATUS_OK){
holder.Sync_Status.setImageResource(R.drawable.success);
}
else {
holder.Sync_Status.setImageResource(R.drawable.stopwatch);
}
}
#Override
public int getItemCount() {
return arrayList.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder{
ImageView Sync_Status;
TextView Name;
public MyViewHolder(View itemView) {
super(itemView);
Sync_Status=(ImageView)itemView.findViewById(R.id.imgSync);
Name=(TextView)itemView.findViewById(R.id.txtName);
}
}
}
Try this code. Now when you create your RecyclerAdapter in Activity1 call new RecyclerAdapter(arrayList, 0) and when you create your RecyclerAdapter in Activity2 call new RecyclerAdapter(arrayList, 1). You are just passing a variable into the constructor so your adapter knows which activity it is in and can run through different logic depending on the activity.
I use DataBindings and RecyclerView in my project.
I have a base adapter for RecyclerView. It looks like this
public abstract class BaseAdapter<T extends ViewDataBinding> extends RecyclerView.Adapter<BaseAdapter.ViewHolder> {
public BaseAdapter() {}
public class ViewHolder extends RecyclerView.ViewHolder {
public T binding;
public ViewHolder(View view) {
super(view);
binding = DataBindingUtil.bind(view);
}
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
... code ...
}
}
ViewHolder extended classes differs only binding field type.
After the implementation of the extended BaseAdapter class:
public class BaseAdapterExtended extends BaseAdapter<BaseAdapterExtendedBinding> {
public BaseAdapterExtended(ArrayList<ItemModel> itemModels) {
super();
mData = itemModels;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new ViewHolder(BaseAdapterExtendedBinding.inflate(inflater, parent, false).getRoot());
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
}
#Override
protected View getItemRootView(ViewHolder holder) {
return holder.binding.item;
}
#Override
public int getItemCount() {
return mData.size();
}
}
i am got next compilation error:
error: BaseAdapterExtended is not abstract and does not override abstract method onBindViewHolder(BaseAdapter.ViewHolder,int) in Adapter
Seems like BaseAdapterExtended hasn't this method, but he exists.
If I change
public void onBindViewHolder(ViewHolder holder, int position)
to
public void onBindViewHolder(BaseAdapter.ViewHolder holder, int position)
Projections compiled fine, but type binding will be ViewDataBinding instead BaseAdapterExtendedBinding. Why is this happening? Any ideas?
In my case I had forgotten to parameterize my superclass. When creating the class I hadn't yet created the ViewHolder, and it was very permissive except for the error on that method. I.e.:
public class FooAdapter extends RecyclerView.Adapter {
Had to be changed to
public class FooAdapter extends RecyclerView.Adapter<FooAdapter.FooViewHolder> {
This needed to be done anyways, but the only compiler error I got was in onBindViewHolder.
in this line:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
}
replace ViewHolder with BaseAdapterExtended.ViewHolder
I just found out the reason to this weird issue. This is happening in my project too but only for adapters that are parametrized, like yours is. All other adapters saw the BindViewHolder() method without needing to explicitly specify the ViewHolder in the class. The solution is to parametrize the ViewHolder class with any random param type, this looks like it's a bug.
public abstract class BaseAdapter<T extends ViewDataBinding> extends RecyclerView.Adapter<BaseAdapter.ViewHolder> {
public BaseAdapter() {}
public class ViewHolder<RandomTypeNotUsed> extends RecyclerView.ViewHolder {
public T binding;
public ViewHolder(View view) {
super(view);
binding = DataBindingUtil.bind(view);
}
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
... code ...
}
}
Works fine for me!
//like is my imageview
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int i) {
MyViewHolder myViewHolder1 = (MyViewHolder)viewHolder;
myViewHolder1.like.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, "yay" + i, Toast.LENGTH_SHORT ).show();
}
});
}