How do you use DiffCallback to load a newList in RecyclerView when DiffUtil ItemCallback is being used.
I would like to give the user the option to return different size lists from the database when the user selects a different size I want the RecyclerView to update.
RecyclerViewAdatper
RecyclerViewAdapter extends ListAdapter<WordEntity, RecyclerViewAdapter.ViewHolder> {
private RecyclerViewAdapter() {
super(DIFF_CALLBACK);
}
private static final DiffUtil.ItemCallback<WordEntity> DIFF_CALLBACK = new DiffUtil.ItemCallback<WordEntiti>() {
#Override
public boolean areItemsTheSame...
#Override
public boolean areContentsTheSame...
};
#Override
public viewHolder onCreateViewHolder...
#Override
public void onVindViewHolder ...
class ViewHolder extends RecyclerView.ViewHolder ...
public void updateWordList(List<WordEntity> words) {
final WordDiffCallBack diffCallBack = new WordDiffCallBack(list???, words);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallBack);
this.list???.clear();
this.addAll(words);
diffResult.dispatcheUpdatesTo(this);
}
WordsDiffCallBack
private final List<WordEntity> mOldList;
private final List<WordEntity> mNewList;
public WordsDiffCallBack(List<WordEntity> oldList, List<WordEntity> newList) {
this.mOldList = oldList;
this.mNewList = newList;
}
#Override
public int getOldListSize() {
return mOldList.size();
}
#Override
public int getNewListSize() {
return mNewList.size();
}
#Override
public boolean areItemsTheSame(int OldItemPostion, int newItemPosition) ...
#Override boolean areContentsTheSame(int oldItemPosition, int newItemPosition)...
#Override getChangePayload(int oldItemPosition, int newItemPosition) ...
}
I want the RecycelView to update automatically when the size of the list gets changed by the user. How do I call the old list from the ListAdapter and will that even update the RecyclerView
You can create a template just like shown in this video in youTube:
https://www.youtube.com/watch?v=y31fzLe2Ajw
Here is an example of adapter.
public class CartFragAdapter extends RecyclerView.Adapter<CartFragAdapter.CartFragViewHolder> {
private static final String TAG = "debinf PurchaseAdap";
private static final DiffUtil.ItemCallback<ProductsObject> DIFF_CALLBACK = new DiffUtil.ItemCallback<ProductsObject>() {
#Override
public boolean areItemsTheSame(#NonNull ProductsObject oldProduct, #NonNull ProductsObject newProduct) {
Log.i(TAG, "areItemsTheSame: old is "+oldProduct.getCode()+" ; new is "+newProduct.getCode());
return oldProduct.getCode().equals(newProduct.getCode());
}
#Override
public boolean areContentsTheSame(#NonNull ProductsObject oldProduct, #NonNull ProductsObject newProduct) {
Log.i(TAG, "areContentsTheSame: old is "+oldProduct.getPrice()+" ; new is "+newProduct.getPrice());
return oldProduct.getPrice() == newProduct.getPrice();
}
};
private AsyncListDiffer<ProductsObject> differ = new AsyncListDiffer<ProductsObject>(this, DIFF_CALLBACK);
#NonNull
#Override
public CartFragViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_purchase, parent, false);
return new CartFragViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull CartFragViewHolder holder, int position) {
final ProductsObject purchaseList = differ.getCurrentList().get(position);
holder.mCode.setText(purchaseList.getCode());
holder.mPrice.setText(String.valueOf(purchaseList.getPrice()));
holder.mDescription.setText(purchaseList.getDescription());
}
#Override
public int getItemCount() {
Log.i(TAG, "getItemCount");
return differ.getCurrentList().size();
}
public void submitList(List<ProductsObject> products){
Log.i(TAG, "submitList: products.size is "+products.size());
differ.submitList(products);
}
public class CartFragViewHolder extends RecyclerView.ViewHolder {
public TextView mCode, mPrice, mDescription;
public CartFragViewHolder(#NonNull View itemView) {
super(itemView);
mCode = (TextView) itemView.findViewById(R.id.item_productCode);
mPrice = (TextView) itemView.findViewById(R.id.item_productPrice);
mDescription = (TextView) itemView.findViewById(R.id.item_productDescription);
}
}
}
In MainActivity you call adapter like this:
CartFragAdapter adapter = new CartFragAdapter();
adapter.submitList(inputData);
I hope it helps!
observe this code
here's my diffutil callback
public class MyDiffUtilCallback extends DiffUtil.Callback {
List<String> oldlist;
List<String > newlist;
public MyDiffUtilCallback(List<String> oldlist, List<String> newlist) {
this.oldlist = oldlist;
this.newlist = newlist;
}
#Override
public int getOldListSize() {
return oldlist.size();
}
#Override
public int getNewListSize() {
return newlist.size();
}
#Override
public boolean areItemsTheSame(int olditempostion, int newitemPostion) {
return olditempostion==newitemPostion;
}
#Override
public boolean areContentsTheSame(int olditempostion, int newitemPostion) {
return olditempostion==newitemPostion;
}
#Nullable
#Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return super.getChangePayload(oldItemPosition, newItemPosition);
}
}
here's the recyclerview adpater which uses diffutilcallback
public class recyclerviewAdapter extends RecyclerView.Adapter<recyclerviewAdapter.Viewholder> {
List<String> datasource;
public recyclerviewAdapter(List<String> datasource) {
this.datasource = datasource;
}
#Override
public Viewholder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.demorow,viewGroup,false);
return new Viewholder(itemView);
}
#Override
public void onBindViewHolder(#NonNull Viewholder viewholder, int i) {
viewholder.tv_demo_data_row.setText(datasource.get(i));
}
#Override
public int getItemCount() {
return datasource.size();
}
public static class Viewholder extends RecyclerView.ViewHolder {
TextView tv_demo_data_row;
public Viewholder(#NonNull View itemView) {
super(itemView);
tv_demo_data_row=itemView.findViewById(R.id.tv_demo_data_row);
}
}
//DIFF CALLBACK STATE
public void insertdata(List<String> insertList){
/**
* Insert list insert data to list
*/
MyDiffUtilCallback diffUtilCallback = new MyDiffUtilCallback(datasource,insertList);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffUtilCallback);
datasource.addAll(insertList);
diffResult.dispatchUpdatesTo(this);
}
public void updateList(List<String> newList){
/**
* update list clear old data and update new data
*/
MyDiffUtilCallback diffUtilCallback = new MyDiffUtilCallback(datasource,newList);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffUtilCallback);
datasource.clear();
datasource.addAll(newList);
diffResult.dispatchUpdatesTo(this);
}}
and here i can update list in activity on button clickevent
insert_data.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//inserting the data
List<String> insertlist = new ArrayList<>(); //asign old list
for(int i=0;i<2;i++){
insertlist.add(UUID.randomUUID().toString()); // insert new list
adapter.insertdata(insertlist);
faster_recyclerview.smoothScrollToPosition(adapter.getItemCount()-1); //auto scroll to last postion
}
}
});
replace List of string with your modelclass
And in addition to the above (using DiffUtils), if you use Clicks to change RecyclerView's content, make
ViewHolder implements View.OnClickListener
and use this to set OnClickListener in onBindViewHolder
.setOnClickListener(holder);
and get your current position by
this.getAdapterPosition()
private class HistoryRecyclerAdapter extends RecyclerView.Adapter<HistoryRecyclerAdapter.ViewHolder> {
private List<HistoryEntry> entries;
HistoryRecyclerAdapter(List<HistoryEntry> entries) {
this.entries = entries;
}
public void updateContainer(List<HistoryEntry> list){
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
#Override
public int getOldListSize() { return entries.size(); }
#Override
public int getNewListSize() { return list.size(); }
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return entries.get(oldItemPosition).getId()==list.get(newItemPosition).getId();
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
HistoryEntry old = entries.get(oldItemPosition);
HistoryEntry neww = list.get(newItemPosition);
return old.getExpression().equals(neww.getExpression())
&& old.getResult().equals(neww.getResult());
}
}, false);//detectMoves=true потом позволит вызвать анимацию если строка просто была перемещена
entries = list;
diffResult.dispatchUpdatesTo(this);
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
//some Views
final Button delBtn;
ViewHolder(View view){
super(view);
//some Views
delBtn = (Button) view.findViewById(R.id.delete_btn);
}
#Override
public void onClick(View v) {
switch (v.getId()){
//some cases
case R.id.delete_btn:
historyManager.delete(entries.get(this.getAdapterPosition()));
break;
}
}
}
#NonNull
#Override
public HistoryRecyclerAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.history_row, parent, false);
return new HistoryRecyclerAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(HistoryRecyclerAdapter.ViewHolder holder, final int position) {
//some code
holder.delBtn.setOnClickListener(holder);
}
#Override
public int getItemCount() {
return entries.size();
}
}
Early I used final int position in onBindViewHolder and don't understand what's wrong...)))
Here is snapet of my project (How to view list of my providers):
public class ProviderAdapter extends ListAdapter<Provider, RecyclerView.ViewHolder> {
private final GeneralListener<Provider> listener;
public ProviderAdapter(GeneralListener<Provider> listener) {
super(DIFF_CALLBACK);
this.listener = listener;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ViewHolder(ItemProviderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
((ViewHolder) holder).bind(getItem(position));
}
class ViewHolder extends RecyclerView.ViewHolder {
ItemProviderBinding binding;
public ViewHolder(ItemProviderBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
void bind(Provider data) {
binding.image.setImageURI(data.img_url);
binding.txtName.setText(data.name);
binding.txtMealTypes.setText(data.meal_types);
binding.txtAddress.setText(data.address);
binding.btnBack.setOnClickListener(view -> {
listener.onClick(Actions.VIEW, data);
});
}
}
private static final DiffUtil.ItemCallback<Provider> DIFF_CALLBACK = new DiffUtil.ItemCallback<Provider>() {
#Override
public boolean areItemsTheSame(#NonNull Provider oldItem, #NonNull Provider newItem) {
return oldItem.id == newItem.id;
}
#Override
public boolean areContentsTheSame(#NonNull Provider oldItem, #NonNull Provider newItem) {
return oldItem.name.equals(newItem.name)
&& oldItem.meal_types.equals(newItem.meal_types)
&& oldItem.address.equals(newItem.address);
}
};
}
To use it:
ProviderAdapter adapter = new ProviderAdapter(new GeneralListener<Provider>() {
#Override
public void onClick(Actions action, Provider provider) {
if (action == Actions.VIEW) {
// TODO: View provider details
}
}
});
recyclerView.setAdapter(adapter);
adapter.submitList(providerList);
Related
I created a recyclerview with a StaggeredGridLayoutManager as LayoutManager.
I wanted to implement a multiple selection thanks to the recyclerview-selection library but when I make a long click on a certain item, it sometimes selects 2. (I am using a DB to retrieve the data)
This is my main code:
GridRecyclerView notesRecyclerView = binding.noteRecyclerView;
notesRecyclerView.setHasFixedSize(true);
adapter = new NoteAdapterTest();
notesRecyclerView.setAdapter(adapter);
SelectionTracker<Long> tracker = new SelectionTracker.Builder<>("mySelection", notesRecyclerView,
new StableIdKeyProvider(notesRecyclerView),
new NoteDetailsLookup(notesRecyclerView),
StorageStrategy.createLongStorage())
.withSelectionPredicate(SelectionPredicates.createSelectAnything()).build();
adapter.setSelectionTracker(tracker);
My Adapter:
private SelectionTracker<Long> selectionTracker;
private static final DiffUtil.ItemCallback<Note> DIFF_CALLBACK = new DiffUtil.ItemCallback<Note>() {
#Override
public boolean areItemsTheSame(#NonNull Note oldItem, #NonNull Note newItem) {
return oldItem.getId() == newItem.getId();
}
#Override
public boolean areContentsTheSame(#NonNull Note oldItem, #NonNull Note newItem) {
return oldItem.getTitle().equals(newItem.getTitle()) && oldItem.getContent().equals(newItem.getContent());
}
};
public NoteAdapterTest() {
super(DIFF_CALLBACK);
setHasStableIds(true);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
AdapterNoteBinding binding = AdapterNoteBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new NoteHolder(binding);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Note note = getItem(position);
((NoteHolder) holder).bind(note, selectionTracker.isSelected((long) position));
}
public static class NoteHolder extends RecyclerView.ViewHolder {
private final AdapterNoteBinding binding;
public NoteHolder(#NonNull AdapterNoteBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public final void bind(Note note, boolean isActive) {
Context context = binding.getRoot().getContext();
itemView.setActivated(isActive);
if (isActive) {
binding.noteAdapterLayout.getBackground().setTint(context.getColor(R.color.blue));
} else {
binding.noteTitle.setText(note.getTitle());
}
}
public ItemDetailsLookup.ItemDetails<Long> getItemDetails() {
return new ItemDetailsLookup.ItemDetails<Long>() {
#Override
public int getPosition() {
return getAdapterPosition();
}
#Nullable
#Override
public Long getSelectionKey() {
return getItemId();
}
};
}
}
#Override
public long getItemId(int position) {
return position;
}
public void setSelectionTracker(SelectionTracker<Long> selectionTracker) {
this.selectionTracker = selectionTracker;
}
}
My lookup:
public class NoteDetailsLookup extends ItemDetailsLookup<Long> {
private final RecyclerView recyclerView;
public NoteDetailsLookup(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
}
#Nullable
#Override
public ItemDetails<Long> getItemDetails(#NonNull MotionEvent e) {
View view = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (view != null) {
RecyclerView.ViewHolder viewHolder = recyclerView.getChildViewHolder(view);
if (viewHolder instanceof NoteAdapterTest.NoteHolder) {
return ((NoteAdapterTest.NoteHolder) viewHolder).getItemDetails();
}
}
return null;
}
}
How do you know that 2 items are selected? What the selection tracker returns?
Try to set background tint in your view holder always:
if (isActive) {
binding.noteAdapterLayout.getBackground().setTint(context.getColor(R.color.blue));
} else {
binding.noteAdapterLayout.getBackground().setTint(context.getColor(R.color.white)); // your default color
}
It may be just a view state issue.
Hello everyone here's my code with recycler view, it works it charges 3 options from my database but my problem is when i try to get all the 3 values in my recycler view
First of all here's my class:
public class OpcionesPonderacionClass {
String Titulo;
public OpcionesPonderacionClass(String titulo) {
Titulo = titulo;
}
public OpcionesPonderacionClass() {
}
public String getTitulo() {
return Titulo;
}
public void setTitulo(String titulo) {
Titulo = titulo;
}
}
Here's my adapter code:
public class OpcionesPonderacionAdapter extends RecyclerView.Adapter<OpcionesPonderacionAdapter.ViewHolderOpcionesPonderacion> {
Context mCtx;
ArrayList<OpcionesPonderacionClass> opcionesPonderacionList;
public OpcionesPonderacionAdapter(Context mCtx, ArrayList<OpcionesPonderacionClass> opcionesList) {
this.mCtx = mCtx;
this.opcionesPonderacionList = opcionesList;
}
#NonNull
#Override
public ViewHolderOpcionesPonderacion onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.itemopcionponderacion,null);
return new ViewHolderOpcionesPonderacion(view);
}
#Override
public void onBindViewHolder(#NonNull ViewHolderOpcionesPonderacion holder, final int position) {
holder.tituloOpcionPonderacion.setText(opcionesPonderacionList.get(position).getTitulo());
holder.npValueOpcionPonderacion.setMinValue(0);
holder.npValueOpcionPonderacion.setMaxValue(10);
}
#Override
public int getItemCount() {
return opcionesPonderacionList.size();
}
public class ViewHolderOpcionesPonderacion extends RecyclerView.ViewHolder {
TextView tituloOpcionPonderacion;
NumberPicker npValueOpcionPonderacion;
public ViewHolderOpcionesPonderacion(#NonNull View itemView) {
super(itemView);
tituloOpcionPonderacion = itemView.findViewById(R.id.tv_TituloOpcionPonderacion);
npValueOpcionPonderacion = itemView.findViewById(R.id.np_ValorNumero);
}
}
}
I can see all the options with their numberpickers from 0 to 10 but for now i'm trying to get a Toast with all the values separated with comma",".
My main problem is how to .getValue() from all the number pickers generated.
You can set up listener to observe value changes within a NumberPicker and collect values in a some Collection.
public static class OpcionesPonderacionAdapter
extends RecyclerView.Adapter<OpcionesPonderacionAdapter.ViewHolderOpcionesPonderacion> {
Context mCtx;
ArrayList<OpcionesPonderacionClass> opcionesPonderacionList;
private final Map<Integer, Integer> pickerValues = new HashMap<>();
private OnNumberPickerValueChangeListener valueChangeListener
= new OnNumberPickerValueChangeListener() {
#Override
public void onValueChange(int position, int value) {
pickerValues.put(position, value);
}
};
public OpcionesPonderacionAdapter(Context mCtx, ArrayList<OpcionesPonderacionClass> opcionesList) {
this.mCtx = mCtx;
this.opcionesPonderacionList = opcionesList;
}
#NonNull
#Override
public ViewHolderOpcionesPonderacion onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.itemopcionponderacion, null);
return new ViewHolderOpcionesPonderacion(view, valueChangeListener);
}
#Override
public void onBindViewHolder(#NonNull ViewHolderOpcionesPonderacion holder, final int position) {
holder.tituloOpcionPonderacion.setText(opcionesPonderacionList.get(position).getTitulo());
holder.npValueOpcionPonderacion.setMinValue(0);
holder.npValueOpcionPonderacion.setMaxValue(10);
}
#Override
public int getItemCount() {
return opcionesPonderacionList.size();
}
public Map<Integer, Integer> getPickerValues() {
return pickerValues;
}
public class ViewHolderOpcionesPonderacion extends RecyclerView.ViewHolder {
TextView tituloOpcionPonderacion;
NumberPicker npValueOpcionPonderacion;
public ViewHolderOpcionesPonderacion(#NonNull View itemView,
final OnNumberPickerValueChangeListener listener) {
super(itemView);
tituloOpcionPonderacion = itemView.findViewById(R.id.tv_TituloOpcionPonderacion);
npValueOpcionPonderacion = itemView.findViewById(R.id.np_ValorNumero);
npValueOpcionPonderacion.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker,
int oldVal,
int newVal) {
listener.onValueChange(getAdapterPosition(), newVal);
}
});
}
}
interface OnNumberPickerValueChangeListener {
void onValueChange(int position, int value);
}
}
to get values:
#Override
public void onClick(View v) {
final Collection<Integer> values = adapter.getPickerValues().values();
final String result = TextUtils.join(".", values);
Toast.makeText(this, result ,Toast.LENGTH_SHORT).show();
}
My Title is kind of hard to understand but basically when I add items into my database is should display it in a RecyclerView. Now in my RecyclerView I have two layouts but the problem is the first item of my database goes behind my first item in my other layout. So if I have 3 items in my database, it shows only 2 items from the database and the first item hides behind my first item in the RecyclerView which is a different layout that does not use the database at all.
This is my code:
FirebaseRecyclerOptions<Event> firebaseRecyclerOptions = new FirebaseRecyclerOptions.Builder<Event>()
.setQuery(query1, Event.class).build();
AccAdapter = new FirebaseRecyclerAdapter<Event, RecyclerView.ViewHolder>(firebaseRecyclerOptions){
final static int TYPE_HEADER = 0;
final static int TYPE_ITEM = 1;
#Override
public int getItemViewType(int position) {
if (position == 0) return TYPE_HEADER;
return TYPE_ITEM;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER){
View view = LayoutInflater.from(getActivity()).inflate(R.layout.recycler_view_row_add_items,
parent, false);
return new ProdudctHolder3(view);
} else {
View view = LayoutInflater.from(getActivity()).inflate(R.layout.recycler_view_row_acc,
parent, false);
return new ProductHolder2(view);
}
}
#Override
protected void onBindViewHolder(final RecyclerView.ViewHolder holder, int position, final Event model) {
if (holder instanceof ProdudctHolder3){
((ProdudctHolder3) holder).addBackground.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(getActivity(), AccAddItems.class ));
}
});
} else{
final ProductHolder2 productHolder2 = (ProductHolder2) holder;
productHolder2.mName.setText(model.getName());
productHolder2.view.setBackgroundResource(getBackgroundDrawable(Integer.valueOf(model.getProductAmount())));
productHolder2.mbackground.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dialog = new Dialog(getActivity());
dialog.setContentView(R.layout.popup_edit_product);
SeekBar amountSeekBar = dialog.findViewById(R.id.amountSeekBar);
amountSeekBar.setMax(100);
amountSeekBar.setProgress(Integer.valueOf(model.getProductAmount()));
amountSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
progress = i;
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
getRef(holder.getAdapterPosition()).child("productAmount").setValue(String.valueOf(progress));
dialog.dismiss();
}
});
dialog.show();
}
});
productHolder2.mbackground.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
final PopupMenu popupMenu = new PopupMenu(getActivity(), productHolder2.mbackground);
popupMenu.inflate(R.menu.menu_acc);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()){
case R.id.deleteProduct:
getRef(productHolder2.getAdapterPosition()).removeValue();
popupMenu.dismiss();
return true;
default:
return false;
}
}
});
popupMenu.show();
return true;
}
});
}
}
};
mAccRecyclerViewRef.setAdapter(AccAdapter);
My two Product Holders
private class ProdudctHolder3 extends RecyclerView.ViewHolder{
private RelativeLayout addBackground;
public ProdudctHolder3(View itemView) {
super(itemView);
addBackground = itemView.findViewById(R.id.mBackground2);
}
}
private class ProductHolder2 extends RecyclerView.ViewHolder{
private TextView mName;
private RelativeLayout mbackground;
private View view;
public ProductHolder2(View itemView) {
super(itemView);
mName = itemView.findViewById(R.id.ItemName);
mbackground = itemView.findViewById(R.id.mBackground1);
view = itemView.findViewById(R.id.amountIndicator);
}
}
The ideal solution would have been to set two adapters on a single RecyclerView but unfortunatelly this is not possible.
However, you can make a single custom Adapter that handles two types of items. I will explain this by getting an example.
Let's assume you need to display objects of two types, humans and aliens. Your objects require completely different layouts and completely different ViewHolders. Please see the below code for the ViewHolders:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static class HumanViewHolder extends RecyclerView.ViewHolder {
public HumanViewHolder(View itemView) {
super(itemView);
//Prepare your ViewHolder
}
public void bind(Human human) {
//Display your human object
}
}
private static class AlienViewHolder extends RecyclerView.ViewHolder {
public AlienViewHolder(View itemView) {
super(itemView);
//Prepare your ViewHolder
}
public void bind(Alien alien) {
//Display your alien object
}
}
}
First you need to add two different constants to your adapter representing both type of views:
private static final int ITEM_TYPE_HUMAN;
private static final int ITEM_TYPE_ALIEN;
To keep things simple, let's also assume you store your objects in a list:
private List<Object> items = new ArrayList<>();
public MyAdapter(List<Object> items) {
this.items.addAll(items);
//Other stuff if needed
}
Now, the first you need to do, is to implement getItemViewType() method:
#Override
public int getItemViewType(int position) {
if (items.get(position) instanceof Human) {
return ITEM_TYPE_HUMAN;
} else {
return ITEM_TYPE_ALIEN;
}
}
Second, you need to use the item type inside the onCreateViewHolder() method like this:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
if (viewType == ITEM_TYPE_HUMAN) {
View view = layoutInflater.inflate(R.layout.item_human, parent, false);
return new HumanViewHolder(view);
} else {
View view = layoutInflater.inflate(R.layout.item_alien, parent, false);
return new AlienViewHolder(view);
}
}
In the end, you just need to bind the proper view holder like this:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
Object item = items.get(position);
if (viewHolder instanceof HumanViewHolder) {
((HumanViewHolder) viewHolder).bind((Human) item);
} else {
((AlienViewHolder) viewHolder).bind((Alien) item);
}
}
Why use the Firebase Recycler Adapter when you could easily make a custom one? If I understood well you want an item to be fixed at position 0 (header) while others will be added below the first one, right? If so, here is a solution I like to use:
public interface ViewType {
public int getViewType();
}
public interface ViewTypeDelegateAdapter {
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent);
public void onBindViewHolder(RecyclerView.ViewHolder holder, ViewType item);
}
public class ViewTypes {
public static int HEADER = 0
public static int ITEM = 1
}
public class ProductDelegateAdapter implements ViewTypeDelegateAdapter {
private int resID;
private Context context;
public ProductDelegateAdapter(int resID, Context context) {
this.resID = resID;
this.context = context;
}
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
return new ProductHolder(LayoutInflater.from(parent.context).inflate(resID, parent, false));
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, ViewType item) {
(holder as ProductHolder).bind("test");
}
class ProductHolder extends RecyclerView.ViewHolder {
public ProductHolder(View view) {
super(view);
}
public void bind(String test) {
}
}
}
public class HeaderDelegateAdapter implements ViewTypeDelegateAdapter {
private int resID;
private Context context;
public ProductDelegateAdapter(int resID, Context context) {
this.resID = resID;
this.context = context;
}
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
return new HeaderHolder(LayoutInflater.from(parent.context).inflate(resID, parent, false));
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, ViewType item) {
(holder as HeaderHolder).bind("test");
}
class HeaderHolder extends RecyclerView.ViewHolder {
public HeaderHolder(View view) {
super(view);
}
public void bind(String test) {
}
}
}
public class AccAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private Context context;
private List<ViewType> items;
private SparseArrayCompat<ViewTypeDelegateAdapter> delegateAdapters;
private ViewType headerItem;
public AccAdapter(Context context) {
this.context = context;
this.items = new ArrayList();
this.delegateAdapters = new SparseArrayCompat();
this.headerItem = new ViewType() {
#Override
public int getViewType() {
return ViewTypes.HEADER;
}
};
this.items.add(this.headerItem);
this.delegateAdapters.put(ViewTypes.HEADER, HeaderDelegateAdapter(R.id.test, this.context));
this.delegateAdapters.put(ViewTypes.ITEM, ProductDelegateAdapter(R.id.test, this.context));
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, Int viewType) {
return delegateAdapters.get(viewType).onCreateViewHolder(parent);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, Int position) {
delegateAdapters.get(getItemViewType(position)).onBindViewHolder(holder, items[position])
}
#Override
public Int getItemViewType(Int position) {
return items.get(position).getViewType();
}
#Override
public Int getItemCount() {
return items.getSize();
}
public void add(ViewType viewType) {
val initPosition = this.items.size - 1
this.items.add(item)
notifyItemRangeChanged(initPosition, this.items.size + 1)
}
}
public class Event implements ViewType {
private String id;
#Override
public int getViewType() {
return ViewTypes.ITEM;
}
}
Excuse me for some syntax errors, I've translated to Java from Kotlin to help you. Hope it helps!
My Title is kind of hard to understand but basically when I add items into my database is should display it in a RecyclerView. Now in my RecyclerView I have two layouts but the problem is the first item of my database goes behind my first item in my other layout. So if I have 3 items in my database, it shows only 2 items from the database and the first item hides behind my first item in the RecyclerView which is a different layout that does not use the database at all.
This is my code:
FirebaseRecyclerOptions<Event> firebaseRecyclerOptions = new FirebaseRecyclerOptions.Builder<Event>()
.setQuery(query1, Event.class).build();
AccAdapter = new FirebaseRecyclerAdapter<Event, RecyclerView.ViewHolder>(firebaseRecyclerOptions){
final static int TYPE_HEADER = 0;
final static int TYPE_ITEM = 1;
#Override
public int getItemViewType(int position) {
if (position == 0) return TYPE_HEADER;
return TYPE_ITEM;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER){
View view = LayoutInflater.from(getActivity()).inflate(R.layout.recycler_view_row_add_items,
parent, false);
return new ProdudctHolder3(view);
} else {
View view = LayoutInflater.from(getActivity()).inflate(R.layout.recycler_view_row_acc,
parent, false);
return new ProductHolder2(view);
}
}
#Override
protected void onBindViewHolder(final RecyclerView.ViewHolder holder, int position, final Event model) {
if (holder instanceof ProdudctHolder3){
((ProdudctHolder3) holder).addBackground.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(getActivity(), AccAddItems.class ));
}
});
} else{
final ProductHolder2 productHolder2 = (ProductHolder2) holder;
productHolder2.mName.setText(model.getName());
productHolder2.view.setBackgroundResource(getBackgroundDrawable(Integer.valueOf(model.getProductAmount())));
productHolder2.mbackground.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
dialog = new Dialog(getActivity());
dialog.setContentView(R.layout.popup_edit_product);
SeekBar amountSeekBar = dialog.findViewById(R.id.amountSeekBar);
amountSeekBar.setMax(100);
amountSeekBar.setProgress(Integer.valueOf(model.getProductAmount()));
amountSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
progress = i;
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
getRef(holder.getAdapterPosition()).child("productAmount").setValue(String.valueOf(progress));
dialog.dismiss();
}
});
dialog.show();
}
});
productHolder2.mbackground.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
final PopupMenu popupMenu = new PopupMenu(getActivity(), productHolder2.mbackground);
popupMenu.inflate(R.menu.menu_acc);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()){
case R.id.deleteProduct:
getRef(productHolder2.getAdapterPosition()).removeValue();
popupMenu.dismiss();
return true;
default:
return false;
}
}
});
popupMenu.show();
return true;
}
});
}
}
};
mAccRecyclerViewRef.setAdapter(AccAdapter);
My two Product Holders
private class ProdudctHolder3 extends RecyclerView.ViewHolder{
private RelativeLayout addBackground;
public ProdudctHolder3(View itemView) {
super(itemView);
addBackground = itemView.findViewById(R.id.mBackground2);
}
}
private class ProductHolder2 extends RecyclerView.ViewHolder{
private TextView mName;
private RelativeLayout mbackground;
private View view;
public ProductHolder2(View itemView) {
super(itemView);
mName = itemView.findViewById(R.id.ItemName);
mbackground = itemView.findViewById(R.id.mBackground1);
view = itemView.findViewById(R.id.amountIndicator);
}
}
The ideal solution would have been to set two adapters on a single RecyclerView but unfortunatelly this is not possible.
However, you can make a single custom Adapter that handles two types of items. I will explain this by getting an example.
Let's assume you need to display objects of two types, humans and aliens. Your objects require completely different layouts and completely different ViewHolders. Please see the below code for the ViewHolders:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static class HumanViewHolder extends RecyclerView.ViewHolder {
public HumanViewHolder(View itemView) {
super(itemView);
//Prepare your ViewHolder
}
public void bind(Human human) {
//Display your human object
}
}
private static class AlienViewHolder extends RecyclerView.ViewHolder {
public AlienViewHolder(View itemView) {
super(itemView);
//Prepare your ViewHolder
}
public void bind(Alien alien) {
//Display your alien object
}
}
}
First you need to add two different constants to your adapter representing both type of views:
private static final int ITEM_TYPE_HUMAN;
private static final int ITEM_TYPE_ALIEN;
To keep things simple, let's also assume you store your objects in a list:
private List<Object> items = new ArrayList<>();
public MyAdapter(List<Object> items) {
this.items.addAll(items);
//Other stuff if needed
}
Now, the first you need to do, is to implement getItemViewType() method:
#Override
public int getItemViewType(int position) {
if (items.get(position) instanceof Human) {
return ITEM_TYPE_HUMAN;
} else {
return ITEM_TYPE_ALIEN;
}
}
Second, you need to use the item type inside the onCreateViewHolder() method like this:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
if (viewType == ITEM_TYPE_HUMAN) {
View view = layoutInflater.inflate(R.layout.item_human, parent, false);
return new HumanViewHolder(view);
} else {
View view = layoutInflater.inflate(R.layout.item_alien, parent, false);
return new AlienViewHolder(view);
}
}
In the end, you just need to bind the proper view holder like this:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
Object item = items.get(position);
if (viewHolder instanceof HumanViewHolder) {
((HumanViewHolder) viewHolder).bind((Human) item);
} else {
((AlienViewHolder) viewHolder).bind((Alien) item);
}
}
Why use the Firebase Recycler Adapter when you could easily make a custom one? If I understood well you want an item to be fixed at position 0 (header) while others will be added below the first one, right? If so, here is a solution I like to use:
public interface ViewType {
public int getViewType();
}
public interface ViewTypeDelegateAdapter {
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent);
public void onBindViewHolder(RecyclerView.ViewHolder holder, ViewType item);
}
public class ViewTypes {
public static int HEADER = 0
public static int ITEM = 1
}
public class ProductDelegateAdapter implements ViewTypeDelegateAdapter {
private int resID;
private Context context;
public ProductDelegateAdapter(int resID, Context context) {
this.resID = resID;
this.context = context;
}
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
return new ProductHolder(LayoutInflater.from(parent.context).inflate(resID, parent, false));
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, ViewType item) {
(holder as ProductHolder).bind("test");
}
class ProductHolder extends RecyclerView.ViewHolder {
public ProductHolder(View view) {
super(view);
}
public void bind(String test) {
}
}
}
public class HeaderDelegateAdapter implements ViewTypeDelegateAdapter {
private int resID;
private Context context;
public ProductDelegateAdapter(int resID, Context context) {
this.resID = resID;
this.context = context;
}
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
return new HeaderHolder(LayoutInflater.from(parent.context).inflate(resID, parent, false));
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, ViewType item) {
(holder as HeaderHolder).bind("test");
}
class HeaderHolder extends RecyclerView.ViewHolder {
public HeaderHolder(View view) {
super(view);
}
public void bind(String test) {
}
}
}
public class AccAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private Context context;
private List<ViewType> items;
private SparseArrayCompat<ViewTypeDelegateAdapter> delegateAdapters;
private ViewType headerItem;
public AccAdapter(Context context) {
this.context = context;
this.items = new ArrayList();
this.delegateAdapters = new SparseArrayCompat();
this.headerItem = new ViewType() {
#Override
public int getViewType() {
return ViewTypes.HEADER;
}
};
this.items.add(this.headerItem);
this.delegateAdapters.put(ViewTypes.HEADER, HeaderDelegateAdapter(R.id.test, this.context));
this.delegateAdapters.put(ViewTypes.ITEM, ProductDelegateAdapter(R.id.test, this.context));
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, Int viewType) {
return delegateAdapters.get(viewType).onCreateViewHolder(parent);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, Int position) {
delegateAdapters.get(getItemViewType(position)).onBindViewHolder(holder, items[position])
}
#Override
public Int getItemViewType(Int position) {
return items.get(position).getViewType();
}
#Override
public Int getItemCount() {
return items.getSize();
}
public void add(ViewType viewType) {
val initPosition = this.items.size - 1
this.items.add(item)
notifyItemRangeChanged(initPosition, this.items.size + 1)
}
}
public class Event implements ViewType {
private String id;
#Override
public int getViewType() {
return ViewTypes.ITEM;
}
}
Excuse me for some syntax errors, I've translated to Java from Kotlin to help you. Hope it helps!
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