I have created generic adapter for RecyclerView by using DataBinding. Here is small code snippet
public class RecyclerAdapter<T, VM extends ViewDataBinding> extends RecyclerView.Adapter<RecyclerAdapter.RecyclerViewHolder> {
private final Context context;
private ArrayList<T> items;
private int layoutId;
private RecyclerCallback<VM, T> bindingInterface;
public RecyclerAdapter(Context context, ArrayList<T> items, int layoutId, RecyclerCallback<VM, T> bindingInterface) {
this.items = items;
this.context = context;
this.layoutId = layoutId;
this.bindingInterface = bindingInterface;
}
public class RecyclerViewHolder extends RecyclerView.ViewHolder {
VM binding;
public RecyclerViewHolder(View view) {
super(view);
binding = DataBindingUtil.bind(view);
}
public void bindData(T model) {
bindingInterface.bindData(binding, model);
}
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(layoutId, parent, false);
return new RecyclerViewHolder(v);
}
#Override
public void onBindViewHolder(RecyclerAdapter.RecyclerViewHolder holder, int position) {
T item = items.get(position);
holder.bindData(item);
}
#Override
public int getItemCount() {
if (items == null) {
return 0;
}
return items.size();
}
}
You can find full code in Github repo : Recyclerview-Generic-Adapter
The problem i am facing is after using generic adapter RecyclerView loading time increase and for a second it shows design time layout and than loads original data.
The piece you're missing is the binding.executePendingBindings() in bindData:
public void bindData(T model) {
bindingInterface.bindData(binding, model);
binding.executePendingBindings();
}
Related
I have an adapter that is showing limited Items on Fragment.
#Override
public int getItemCount() {
int limit = 7;
return Math.min(latestProductModelList.size(),limit);
}
and I want to show all the items of list in recyclerview when I click on ViewAll button using the same adapter on another acitivity.
this is my adapter.
`
public class LatestProductAdapter extends RecyclerView.Adapter<LatestProductAdapter.ViewHolder> {
List<LatestProductModel> latestProductModelList = new ArrayList<>();
Context context;
LatestProductClickInterface latestProductClickInterface;
public LatestProductAdapter(Context context, LatestProductClickInterface latestProductClickInterface) {
this.context = context;
this.latestProductClickInterface = latestProductClickInterface;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_layout,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
LatestProductModel latestProductModel = latestProductModelList.get(position);
Glide.with(context).load(latestProductModel.getImage()).into(holder.itemImage);
holder.itemTitle.setText(latestProductModel.getTitle());
holder.itemView.setOnClickListener(v -> {
latestProductClickInterface.OnLatestProductClicked(latestProductModelList.get(position));
});
}
#Override
public int getItemCount() {
int limit = 7;
return Math.min(latestProductModelList.size(),limit);
}
#SuppressLint("NotifyDataSetChanged")
public void updateList(List<LatestProductModel> latestProductModels){
latestProductModelList.clear();
latestProductModelList.addAll(latestProductModels);
Collections.reverse(latestProductModelList);
notifyDataSetChanged();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
ImageView itemImage;
TextView itemTitle;
public ViewHolder(#NonNull View itemView) {
super(itemView);
itemImage = itemView.findViewById(R.id.item_img);
itemTitle = itemView.findViewById(R.id.item_title);
}
}
}
`
How to achieve this?
Or is there another way to achieve this?
kindly help me.
You can use bellow codes for your adapter:
public class LatestProductAdapter extends RecyclerView.Adapter<LatestProductAdapter.ViewHolder> {
List<LatestProductModel> latestProductModelList = new ArrayList<>();
Context context;
LatestProductClickInterface latestProductClickInterface;
private boolean shouldShowAllItems;
public LatestProductAdapter(Context context, LatestProductClickInterface latestProductClickInterface , boolean shouldShowAllItems) {
this.context = context;
this.latestProductClickInterface = latestProductClickInterface;
this.shouldShowAllItems = shouldShowAllItems;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_layout,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
LatestProductModel latestProductModel = latestProductModelList.get(position);
Glide.with(context).load(latestProductModel.getImage()).into(holder.itemImage);
holder.itemTitle.setText(latestProductModel.getTitle());
holder.itemView.setOnClickListener(v -> {
latestProductClickInterface.OnLatestProductClicked(latestProductModelList.get(position));
});
}
#Override
public int getItemCount() {
if (shouldShowAllItems){
return latestProductModelList.size();
}else {
int limit = 7;
return Math.min(latestProductModelList.size(), limit);
}
}
#SuppressLint("NotifyDataSetChanged")
public void updateList(List<LatestProductModel> latestProductModels){
latestProductModelList.clear();
latestProductModelList.addAll(latestProductModels);
Collections.reverse(latestProductModelList);
notifyDataSetChanged();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
ImageView itemImage;
TextView itemTitle;
public ViewHolder(#NonNull View itemView) {
super(itemView);
itemImage = itemView.findViewById(R.id.item_img);
itemTitle = itemView.findViewById(R.id.item_title);
}
}
}
and create adapter object accourding your need:
LatestProductAdapter latestProductAdapter = LatestProductAdapter(context , this ,//true or false);
In my SectionsListAdapter adapter i have nested Adapter named MonthLessonsList, after get date from web service and set new data and call notifyDataSetChanged method for adapter, main adapter as SectionsListAdapter can be refresh, but nested adapter dont refresh and after calling notifyDataSetChanged for that, doesn't work correctly and don't show new data
call and set new data from Fragment:
monthSectionsItems = SQLite.select().from(MonthSections.class).queryList();
adapter.setData(monthSectionsItems);
and my Adapter with nested adapter:
public class SectionsListAdapter extends RecyclerView.Adapter<SectionsListAdapter.MyViewHolder> {
private final OnItemSelected listener;
private List<MonthSections> list;
private Context context;
private MonthLessonsList lessonsListAdapter;
public SectionsListAdapter(List<MonthSections> followingsList, Context mContext, OnItemSelected listener) {
this.list = followingsList;
this.context = mContext;
this.listener = listener;
}
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.sections_list_row, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, final int position) {
holder.indicatorIcon.setText(list.get(position).getSection_month_name());
...
List<SectionLesson> lessonsList = list.get(position).getLessons();
lessonsListAdapter = new MonthLessonsList(lessonsList);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(context);
holder.userChildCategories.setLayoutManager(mLayoutManager);
holder.userChildCategories.setAdapter(lessonsListAdapter);
...
}
#Override
public int getItemCount() {
return list.size();
}
public void setData(List<MonthSections> data) {
list.clear();
list.addAll(data);
notifyDataSetChanged();
lessonsListAdapter.notifyDataSetChanged();
}
public List<MonthSections> getData() {
return list;
}
class MyViewHolder extends RecyclerView.ViewHolder {
...
public MyViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
section_title.setGravity(Gravity.RIGHT);
}
}
public class MonthLessonsList extends RecyclerView.Adapter<MonthLessonsList.LessonsViewHolder> {
private List<SectionLesson> lessonItem;
public class LessonsViewHolder extends RecyclerView.ViewHolder {
public TextView title;
public LessonsViewHolder(View view) {
super(view);
title = view.findViewById(R.id.title);
}
}
public MonthLessonsList(List<SectionLesson> lists) {
this.lessonItem = lists;
}
#Override
public LessonsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.child_list_row, parent, false);
return new LessonsViewHolder(itemView);
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(#NonNull LessonsViewHolder holder, int position) {
SectionLesson lesson = lessonItem.get(position);
holder.title.setText("درس: " + (position + 1) + ") " + lesson.getTitle());
}
#Override
public int getItemCount() {
return lessonItem.size();
}
}
}
Declare child adapter inside onBindViewHolder, and remove from global level.
And no need to notify child adapter. Because that will be automatically filled when parent onBindViewHolder is called.
Like
MonthLessonsList lessonsListAdapter = new MonthLessonsList(lessonsList);
holder.userChildCategories.setAdapter(lessonsListAdapter);
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.
Prompt, faced with scrollToPosition speed. I RecyclerView 900 positions and need to quickly establish a position as scrollToPosition very slow, c ListView such problems were not setSelection working out perfectly. It is possible to accelerate the scrollToPosition?
public class EventAdapter extends RecyclerView.Adapter<EventAdapter.Holder> {
final static public String TAG = "EventAdapter";
Context context;
List<Event> items;
public EventAdapter(List<Event> items) {
this.items = items;
}
#Override
public EventAdapter.Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_event, parent, false);
context = parent.getContext();
return new EventAdapter.Holder(v);
}
#Override
public void onBindViewHolder(EventAdapter.Holder holder, int position) {
holder.setModel(context, items.get(position), position);
}
#Override
public int getItemCount() {
return items.size();
}
public class Holder extends RecyclerView.ViewHolder {
public View block;
Holder(View view) {
super(view);
block = (View) view.findViewById(R.id.cv);
}
public void setModel(Context context, Event model, int position) {
}
}
}
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
rv.setLayoutManager(linearLayoutManager);
rv.setHasFixedSize(true);
linearLayoutManager.scrollToPosition(index);
There are couple of ways to do it:
1) Try smoothScrollToPosition as : [will certainly work]
Toast.makeText(this, "Scroll...", Toast.LENGTH_SHORT).show();
myList.smoothScrollToPosition(0);
2) Also read about scrollToPositionWithOffset as this may help too
Edit
As It is a genericAdapter not simple one and I know the methods to add click listener. And it is not a good practice to do this in onCreateViewHolder. So that's why I need a better suggestion
I have created a Generic Adapter for RecyclerView in android. Now I want some suggestion to improve it. And how could I add clickListener to it.
GenericAdapter.java
public abstract class GenericAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private ArrayList<T> items;
private OnRecyclerItemClicked onRecyclerItemClicked;
public abstract RecyclerView.ViewHolder setViewHolder(ViewGroup parent);
public abstract void onBindData(RecyclerView.ViewHolder holder, T val);
public GenericAdapter(Context context, ArrayList<T> items){
this.context = context;
this.items = items;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder holder = setViewHolder(parent);
return holder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
onBindData(holder,items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
public void addItems( ArrayList<T> savedCardItemz){
items = savedCardItemz;
this.notifyDataSetChanged();
}
public T getItem(int position){
return items.get(position);
}
public void setOnRecyclerItemClicked(OnRecyclerItemClicked onRecyclerItemClicked){
this.onRecyclerItemClicked = onRecyclerItemClicked;
}
public interface OnRecyclerItemClicked{
void onItemClicked(View view,int position);
}
}
And Call it like
adapter = new GenericAdapter<MyModelClass>(context,listOfModelClass) {
#Override
public RecyclerView.ViewHolder setViewHolder(ViewGroup parent) {
final View view = LayoutInflater.from(context).inflate(R.layout.item_recycler_view, parent, false);
AViewHolder viewHolder = new AViewHolder(context, view);
return viewHolder;
}
#Override
public void onBindData(RecyclerView.ViewHolder holder1, MyModelClass val) {
MyModelClass currentCard = val;
AViewHolder holder = (AViewHolder)holder1;
holder.cardNumber.setText(currentCard.getDisplayNumber());
holder.cardHolderName.setText(currentCard.getCardHolderName());
}
};
mRecyclerView.setAdapter(adapter);
Now how and where could I add a click listener. As adding click listener to onBindData is an overhead. Need suggestion.
Have you tried adding a ViewHolder and add the clicklistener to it
Now GenericAdapter.java.
public abstract class GenericAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private List<T> items;
private OnRecyclerItemClicked onRecyclerItemClicked;
public abstract RecyclerView.ViewHolder setViewHolder(ViewGroup parent , OnRecyclerItemClicked onRecyclerItemClicked);
public abstract void onBindData(RecyclerView.ViewHolder holder, T val);
public abstract OnRecyclerItemClicked onGetRecyclerItemClickListener();
public GenericAdapter(Context context, List<T> items){
this.context = context;
this.items = items;
onRecyclerItemClicked = onGetRecyclerItemClickListener();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder holder = setViewHolder(parent , onRecyclerItemClicked);
return holder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
onBindData(holder,items.get(position));
}
#Override
public int getItemCount() {
return items.size();
}
public void setItems( ArrayList<T> savedCardItemz){
items = savedCardItemz;
this.notifyDataSetChanged();
}
public T getItem(int position){
return items.get(position);
}
public interface OnRecyclerItemClicked{
void onItemClicked(View view,int position);
}
}
And calling it like this
GenericAdapter<CreditCardItemBO> adaptering = new GenericAdapter<CreditCardItemBO>(mContext,new ArrayList<CreditCardItemBO>()) {
#Override
public RecyclerView.ViewHolder setViewHolder(ViewGroup parent, OnRecyclerItemClicked onRecyclerItemClicked) {
final View view = LayoutInflater.from(mContext).inflate(R.layout.item_save_credit_card, parent, false);
CreditCardViewHolder viewHolder = new CreditCardViewHolder(mContext, view,onRecyclerItemClicked);
return viewHolder;
}
#Override
public void onBindData(RecyclerView.ViewHolder holder, CreditCardItemBO val) {
}
#Override
public OnRecyclerItemClicked onGetRecyclerItemClickListener() {
return new OnRecyclerItemClicked() {
#Override
public void onItemClicked(View view, int position) {
}
};
}
};
You can add the listener in the activity you have declared the RecyclerView:
recyclerView.addOnItemTouchListener(new RecyclerTouchListener(this, recyclerView,
new RecyclerTouchListener.ClickListener() {
#Override
public void onClick(View view, int position) {
//Handle the action
}
}
#Override
public void onLongClick(View view, int position) {
}
}));
I have implemented ViewHolder in Santa solution like this:
public class EmployeeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView employeeName;
private BaseRecyclerAdapter.OnRecyclerItemClicked mOnRecyclerItemClicked;
public EmployeeViewHolder(View view, BaseRecyclerAdapter.OnRecyclerItemClicked onRecyclerItemClicked) {
super(view);
employeeName = (TextView) view.findViewById(R.id.employee_name);
mOnRecyclerItemClicked = onRecyclerItemClicked;
view.setOnClickListener(this);
}
#Override
public void onClick(View view) {
mOnRecyclerItemClicked.onItemClicked(view, getAdapterPosition());
}
public TextView getEmployeeName() {
return employeeName;
}
}
If you need the context ad it in to constructor as a parameter.
I have created a generic adapter, the aar and the sources are at this url:
it's easy usage:
RecyclerAdapter<Customer> mAdapter = new RecyclerAdapter(customerList, CustomerViewHolder.class);
add attach listener (lambda sample)
RecyclerItemClickListener.affectOnItemClick(mRecycler, (position, view1) -> {
//action
});
see Url link more details