I have a RecyclerView, which has a Button and an EditText.
I have a Button outside of the RecyclerView. On click the outside Button, I want the Button in the RecyclerView toggles between hide and show.
How to do that?
I have tried to send a boolean parameter mEditFlag to the recyclerAdapter, toogle mEditFlag, and notify the adapter the data has changed. but it doesn't work.
mCurrentOrderRecyclerAdapter = new CurrentOrderRecyclerAdapter(this, mEditFlag);
rvOrder.setAdapter(mCurrentOrderRecyclerAdapter);
rlEditOrderList.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mEditFlag = !mEditFlag;
mCurrentOrderRecyclerAdapter.notifyDataSetChanged();
if (mEditFlag) {
ivEditOrderList.setImageResource(R.drawable.order_edit_true);
tvEditOrderList.setText(R.string.order_edit_flag_true);
} else {
ivEditOrderList.setImageResource(R.drawable.order_edit_false);
tvEditOrderList.setText(R.string.order_edit_flag_false);
}
}
});
#Override
public void onBindViewHolder(OrderRecyclerViewHolder holder, final int position) {
if(editFlag) {
holder.rlLeftItemRvOrder.setVisibility(View.VISIBLE);
} else {
holder.rlLeftItemRvOrder.setVisibility(View.GONE);
}
holder.tvNumItemRvOrder.setText(position + "x");
holder.tvDesItemRvOrder.setText("holder.ivFoodItemRvOrder.setImageResource(R.drawable.drawer_menu)");
holder.ivFoodItemRvOrder.setImageResource(R.drawable.drawer_menu);
}
You have to achieve it with your adapter layer.
The Button outside the RecyclerView has to update the item inside the adapter (for example a boolean).
Then notify the change to update (for example with the notifyItemChanged method) the RecyclerView
Somenthing like:
rlEditOrderList.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Update the item inside the adapter
MyObject obj = mAdapter.getItem(position);
obj.myBoolean= true;
mAdapter.notifyItemChanged(position);
}
});
with an adapter like:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<MyObject> mDataset;
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
//Get the item in the adapter
MyObject obj = getItem(position);
if(obj.myBoolean)
holder.mButtonView.setVisibile(View.VISIBLE);
else
holder.mButtonView.setVisibile(View.GONE);
}
}
Related
I have an item list which are placed under a RecyclerView. On any item, there a quantity(add or subtract). When add is selected, the quantity should increase one.
But when i clicked on '+' for the first time, the value of quantity is setting to 1 and soon after resetting to 0, but when i click on second time the value is becoming '1'. Similarly when i click on next time the value is setting to 2 and after a fraction of second it is still getting to 1 and i click on another time the value is becoming 2. Why by first click is not getting recognized? Can someone please help me here?
public class ItemListAdapter extends RecyclerView.Adapter<ItemListAdapter.ItemViewHolder>{
private Context mContext;
private List<Items> itemsList;
private OnItemListListner onClick;
public ItemListAdapter(Context mContext,List<Items> itemsList)
{
this.mContext=mContext;
this.itemsList=itemsList;
}
#NonNull
#Override
public ItemViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater=LayoutInflater.from(mContext);
View view=inflater.inflate(R.layout.item_details_layout,null);
return new ItemListAdapter.ItemViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final ItemViewHolder holder, final int position) {
Items items=itemsList.get(position);
Log.d("onBindViewHolder",items.getItemName());
holder.itemNameTV.setText(items.getItemName());
String strItemPrice=mContext.getResources().getString(R.string.Rs)+" "+String.valueOf(items.getItemPrice());
holder.itemPriceTV.setText(strItemPrice);
holder.itemDescTV.setText(items.getItemDescription());
Glide.with(mContext).load(items.getItemImagePath()).into(holder.imageView);
holder.itemAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int intCurrentQuantity=Integer.valueOf(holder.itemQuantity.getText().toString());
intCurrentQuantity+=1;
Log.d("myQuantity",String.valueOf(intCurrentQuantity));
holder.itemQuantity.setText(String.valueOf(intCurrentQuantity));
notifyItemChanged(holder.getAdapterPosition());
}
});
holder.itemNameTV.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onClick.onItemListClick(position);
}
});
holder.itemPriceTV.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onClick.onItemListClick(position);
}
});
holder.itemDescTV.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onClick.onItemListClick(position);
}
});
holder.imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onClick.onItemListClick(position);
}
});
}
#Override
public int getItemCount() {
return itemsList.size();
}
public void SetOnClick(OnItemListListner onClick)
{
this.onClick=onClick;
}
class ItemViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
TextView itemNameTV,itemPriceTV,itemDescTV,itemQuantity,itemSubtract,itemAdd;
public ItemViewHolder(#NonNull View itemView) {
super(itemView);
imageView=itemView.findViewById(R.id.itemImageView);
itemNameTV=itemView.findViewById(R.id.itemNameTextView);
itemPriceTV=itemView.findViewById(R.id.itemPriceTextView);
itemDescTV=itemView.findViewById(R.id.itemDescritionTextView);
itemQuantity=itemView.findViewById(R.id.itemQuantity);
itemSubtract=itemView.findViewById(R.id.subtractQuantity);
itemAdd=itemView.findViewById(R.id.addQuantity);
}
}
public interface OnItemListListner{
void onItemListClick(int position);
}
}
you should have the quantity property in Item model class and have to follow the code I added below.
holder.itemAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int quantity = item[getAdapterPosition()].quantity;
quantity = quantity +1;
item[getAdapterPosition()].setQuantity(quantity);
notifyItemChanged(getAdapterPosition());
}
});
Thanks.I am sure this will work for you.
Reviewd your code and then after i'm just agreed with Harsh's Answer
there is a two way to set your adapter. most commonly used Model for storing and retriving data.
You need to create a one model class and and set data first into the model then after get data from model and set into your adapter / notify your adapter.
You need to create one variable in model class like below snippet:
public class Item {
String mQty = "";
public String getmQty() {
return mQty;
}
public void setmQty(String mQty) {
this.mQty = mQty;
}
}
Helpful Answer: Click Me
Recycler view recycles the views on scroll and resets its content, so you have to persist the quantity qty variable. You can do it by two ways
Store quantity inside your Items model.
Persist quantity in seperate HashMap as item.getId() as key and qty as value.
then get and set from the model object or hashmap.
Check updated code
holder.itemAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int intCurrentQuantity=Integer.valueOf(holder.itemQuantity.getText().toString());
++intCurrentQuantity; //use this
Log.d("myQuantity",String.valueOf(intCurrentQuantity));
holder.itemQuantity.setText(String.valueOf(intCurrentQuantity));
notifyItemChanged(holder.getAdapterPosition());
}
});
Im trying to implement a RecyclerView in my app. This is my Adapter class:
public class AdapterItemsList extends RecyclerView.Adapter<AdapterItemsList.ViewHolderItems>
{
private ArrayList<CItem> items;
public AdapterItemsList(ArrayList<CItem> items)
{
this.items = items;
}
#NonNull
#Override
public ViewHolderItems onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_counter, null, false);
return new ViewHolderItems(view);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolderItems holder, final int position)
{
holder.asignarDatos(items.get(holder.getAdapterPosition()));
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
//deleteSelectedItem() method gonna be here
Toast.makeText(view.getContext(), String.valueOf(holder.getAdapterPosition())
, Toast.LENGTH_SHORT).show();
return false;
}
});
holder.counterAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
auxInteger += 1;
holder.itemNumber.setText(auxInteger.toString());
items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
}
});
holder.counterSubtract.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
if (auxInteger > 0)
{
auxInteger -= 1;
holder.itemNumber.setText(auxInteger.toString());
items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
}
}
});
}
#Override
public int getItemCount()
{
return items.size();
}
public class ViewHolderItems extends RecyclerView.ViewHolder
{
TextView itemName;
TextView itemNumber;
Button counterAdd;
Button counterSubtract;
public ViewHolderItems(View itemView)
{
super(itemView);
itemName = (TextView) itemView.findViewById(R.id.textViewItemCounterName);
itemNumber = (TextView) itemView.findViewById(R.id.textViewItemCounterNumber);
counterAdd = (Button) itemView.findViewById(R.id.buttonItemCounterAdd);
counterSubtract = (Button) itemView.findViewById(R.id.buttonItemCounterSubtract);
}
public void asignarDatos(CItem item)
{
itemName.setText(item.getObjectName());
itemNumber.setText(item.getObjectNumber().toString());
}
}
}
The code that "creates" the adapter:
private void CreateAdapter() {
recyclerViewItems.setAdapter(null);
items.clear();
//I dont post the code, but this just gets data from a SQLite database and populate "items".
LoadClientsList();
final AdapterItemsList adapter = new AdapterItemsList(items);
recyclerViewItems.setAdapter(adapter);
}
As you can see, I never use "implements View.OnLongClickListener" or something like that, but I just set the OnClickListener inside onBindViewHolder. Now there is a "Toast" inside (which appears just fine when I test the app), but later on, there should be my "delete item" dialog, that gonna give the user the change to Accept or Cancel (an usual dialog). If the user press "accept" then I gonna remove the item from the list.
I gonna admit, Im not an Android or Java Ninja, so I know there are horrendous mistakes probably. But the thing is, the app is working by now (the Toast appears correctlywhen I long-press an item), but the fact that I never use "implements", while I see everyone does it in StackOverflow questions related to RecyclerViews and Adapters, make me think Im making (several) serious mistakes.
I could admit since Im not a master at Java fundamentals (some years working with COBOL already that I remember almost NULL of OOP), I don't see where my mistakes are, but still, any help will be much appreciated.
Thanks in regard!
UPDATE:
Ok I was changing my code and now its like follows:
public class AdapterItemsList extends
RecyclerView.Adapter<AdapterItemsList.ViewHolderItems> implements
View.OnClickListener {
private ArrayList<CItem> items;
/* Added this */
private View.OnClickListener listener;
public AdapterItemsList(ArrayList<CItem> items)
{
this.items = items;
}
#NonNull
#Override
public ViewHolderItems onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_counter, null, false);
/* Added this */
view.setOnClickListener(this);
return new ViewHolderItems(view);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolderItems holder, final int position)
{
holder.asignarDatos(items.get(holder.getAdapterPosition()));
holder.counterAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
auxInteger += 1;
holder.itemNumber.setText(auxInteger.toString());
items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
}
});
holder.counterSubtract.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
if (auxInteger > 0)
{
auxInteger -= 1;
holder.itemNumber.setText(auxInteger.toString());
items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
}
}
});
}
#Override
public int getItemCount() { [...] }
/* Added this */
public void setOnClickListener(View.OnClickListener listener)
{
this.listener = listener;
}
/* Added this */
#Override
public void onClick(View view) {
if (listener != null)
{
listener.onClick(view);
}
}
public class ViewHolderItems extends RecyclerView.ViewHolder { [...] } }
Code where I create the adapter:
private void CreateAdapter() {
recyclerViewItems.setAdapter(null);
items.clear();
LoadClientsList();
final AdapterItemsList adapter = new AdapterItemsList(items);
/* Added this */
adapter.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), String.valueOf(recyclerViewItems.getChildAdapterPosition(view)), Toast.LENGTH_SHORT).show();
}
});
recyclerViewItems.setAdapter(adapter);
}
The "recyclerViewItems.getChildAdapterPosition(view)" finally solves my problem (didn't mentioned before, but it was the reason why I put the "setOnClick..." inside the onBindViewHolder method, just because I needed the position of the pressed item and I couldnt figure out how on earth to access it).
So now I set the listener when I create the adapter instead of when Binding the View holder, which I guess it's better.
Still, I didnt change the "holder.counterAdd.setOnClickListener" and "holder.counterSubtract.setOnClickListener" lines, since I think they must be inside the onBindViewHolder method, but I guess Im still wrong on that.
I disagree with most of the comments on your question, and I think that the way you've written your adapter is pretty good. It's not perfect, but there is nothing wrong with manually setting a View.OnClickListener to some (or many) views inside your ViewHolder. In fact, this is the Google-recommended way of doing things!
I would make some changes, however. The first would be to move the declaration of your click listeners into the onCreateViewHolder() method (by putting them inside the ViewHolder's constructor). The second would be to add checks for NO_POSITION to make sure you handle the case where an item has been removed from your adapter but the layout hasn't been recalculated yet.
Here is what I'd recommend:
public ViewHolderItems(View itemView)
{
super(itemView);
itemName = (TextView) itemView.findViewById(R.id.textViewItemCounterName);
itemNumber = (TextView) itemView.findViewById(R.id.textViewItemCounterNumber);
counterAdd = (Button) itemView.findViewById(R.id.buttonItemCounterAdd);
counterSubtract = (Button) itemView.findViewById(R.id.buttonItemCounterSubtract);
itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
deleteItem();
return true;
}
});
counterAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
itemAdd();
}
});
counterSubtract.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
itemSubtract();
}
});
}
private void deleteItem() {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
items.remove(position);
adapter.notifyItemRemoved(position);
}
}
private void itemAdd() {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
CItem item = items.get(position);
item.setObjectNumber(item.getObjectNumber() + 1);
adapter.notifyItemChanged(position);
}
}
private void itemSubtract() {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
CItem item = items.get(position);
if (item.getObjectNumber() > 0) {
item.setObjectNumber(item.getObjectNumber() - 1);
adapter.notifyItemChanged(position);
}
}
}
And then your onBindViewHolder() is extremely simple:
#Override
public void onBindViewHolder(#NonNull ViewHolderItems holder, int position)
{
holder.asignarDatos(items.get(position));
}
So, let's talk about what I'm doing here.
First, I pushed all of the listener setup into the constructor of your ViewHolder. This means that the listeners are all assigned exactly once. In order to make that still function in a world where ViewHolders are recycled and attached to different items, we have to use getAdapterPosition() to make sure that we're always interacting with the correct item in your list.
We have to check for NO_POSITION because RecyclerView layout operations are asynchronous: when you delete an item from the list, there is a gap where the item doesn't exist in your adapter any more but still is displayed on screen. In this gap, the user might tap a button! In these cases, we just ignore the user input.
I also changed how you update the values of your items, and how you display those updates. Rather than getting the text and parsing it and then re-setting it, I always go through the adapter. Remember that the goal of your RecyclerView implementation is to be able to display any element in your list correctly. The best way to do this is to update the element in your data set, and then notify the RecyclerView that that element has changed, and let onBindViewHolder() take care of the required updates.
I want to show and hide some buttons within the recycler view in recycler item clicked. For example I have a recyclerw view with two items like this
On click of the 1st item, the buttons Edit and delete should be displayed. My adapter code is here:
using Android.Support.V7.Widget;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
namespace RecyclerViewTest
{
internal class BuyerAdapter : RecyclerView.Adapter
{
public List<Buyer> mlivestock;
public event EventHandler<int> ItemClick;
public BuyerAdapter(List<Buyer> photoAlbum)
{
mlivestock = photoAlbum;
}
public override RecyclerView.ViewHolder
OnCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.From(parent.Context).
Inflate(Resource.Layout.PersonCardView, parent, false);
PhotoViewHolder vh = new PhotoViewHolder(itemView, ItemOnClick);
return vh;
}
void ItemOnClick(int position)
{
if (ItemClick != null)
ItemClick(this, position);
}
public override void
OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
PhotoViewHolder vh = holder as PhotoViewHolder;
vh.Agent.Text = mlivestock[position].Agent;
vh.Pen.Text = mlivestock[position].Pen.ToString();
}
public override int ItemCount
{
get { return mlivestock.Count; }
}
}
public class PhotoViewHolder : RecyclerView.ViewHolder
{
public TextView Agent { get; private set; }
public TextView Pen { get; private set; }
public PhotoViewHolder(View itemView, Action<int> listener)
: base(itemView)
{
Agent = itemView.FindViewById<TextView>(Resource.Id.agent);
Pen = itemView.FindViewById<TextView>(Resource.Id.pen);
itemView.Click += (sender, e) => listener(base.AdapterPosition);
}
}
}
I have added a "ItemClick" event which should show/hide the buttons. I would also want to have click events for edit and delete buttons. How can I achieve this? Any help would be appreciated.
Set an adapter variable:
private int currentSelectedPosition = RecyclerView.NO_POSITION
Change your personCardView adapter layout to have both the buttons in them, and set their visibility to GONE. Reference them in your ViewHolder (e.g. Button editButton, Button deleteButton)
In your item onClickListener, set the currentPosition and call notifyDataSetChanged() - this is necessary to re-hide the buttons in previous selections and show the buttons in this selection. Then in onBindViewHolder, apply the VISIBLE or GONE logic as below. I personally set the itemClickListener inside onBindViewHolder too, so the whole method would look like this:
public void OnBindViewHolder(RecyclerView.ViewHolder holder, int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
currentSelectedPosition = position;
notifyDataSetChanged();
}
});
if (currentSelectedPosition == position) {
holder.editButton.setVisibility(View.VISIBLE);
holder.editButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// your edit button click event here
}
});
holder.deleteButton.setVisibility(View.VISIBLE);
holder.deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// your delete button click event here
}
});
} else {
holder.editButton.setVisibility(View.GONE);
holder.deleteButton.setVisibility(View.GONE);
}
//..... the rest of your code for onBindViewHolder (updating your text views and so on)
}
In my app I have a Fragment representing a list of items.
This is done using a RecyclerViewAdapter. I have implemented an OnListFragmentInteractionListener and therefore if I click an item in the list my app does something.
That works correctly. However, I decided I want to also add a button inside that item. So if I click anywhere other than the button but inside that item's borders, it will proceed with functionality as before, but if I click on the button, it will do something else.
My question is, where do I implement this functionality?
The XML file representing an item in the list is just a couple of text views and a FloatingActionButton.
I thought I have to implement this in my adapter, but where exactly? My adapter looks like this:
public class ContractRecyclerViewAdapter extends RecyclerView.Adapter<ContractRecyclerViewAdapter.ViewHolder> {
private final List<Contract> mValues;
private final OnListFragmentInteractionListener mListener;
public ContractRecyclerViewAdapter(List<Contract> items, OnListFragmentInteractionListener listener) {
mValues = items;
mListener = listener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fragment_contract_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
holder.mSell.setText(mValues.get(position).getSell());
if (holder.mDescription != null) {
holder.mDescription.setText(mValues.get(position).getDescription());
}
// TODO: Get the price from parameters?
holder.mPrice.setText(((Double) new Random().nextDouble()).toString());
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (null != mListener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mListener.onListFragmentInteraction(holder.mItem);
}
}
});
holder.mBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
}
#Override
public int getItemCount() {
return mValues.size();
}
public void addContract(Contract contract) {
mValues.add(contract);
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final View mView;
public final TextView mSell;
public final TextView mDescription;
public final TextView mPrice;
public final FloatingActionButton mBtn;
public Contract mItem;
public ViewHolder(View view) {
super(view);
mView = view;
mSell = (TextView) view.findViewById(R.id.company_text);
mDescription = (TextView) view.findViewById(R.id.contract_description);
mPrice = (TextView) view.findViewById(R.id.price_text);
mBtn = (FloatingActionButton) view.findViewById(R.id.buy_button_from_list);
}
#Override
public String toString() {
return super.toString() + " '" + mSell.getText() + ", " +
mDescription.getText() + "'";
}
}
}
The problem is that wherever I implement the onClickListener for my FloatingActionButton I'll need to have access to my SharedPreferences, so I think I will have to implement it in an Activity class (or Fragment class). Is that true? If I go this way then I'll have to implement it in the Fragment whose content is this view, but then how will I know the position of the item selected?
Thanks.
You can create another method inside your interface,
private interface OnListFragmentInteractionListener {
void onListFragmentInteraction(Contract mItem);
void onButtonClicked(Contract mItem); // You may want to edit the arguments of the method
}
Implement the onButtonClicked in your activity and do your stuff there.
Call the method inside your onClick of the Button as follows,
holder.mBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (null != mListener) {
mListener.onButtonClicked(holder.mItem);
}
}
});
how will I know the position of the item selected?
To get the position of item selected you can utilize the getAdapterPosition() method of RecyclerView.ViewHolder's class inside your View.OnClickListener.
holder.btnXyz.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int position = holder.getAdapterPosition();
Model item = items.get(position);
/** your code **/
}
});
I have a RecyclerView which contains multiple item types (4 or 5 at least).
Some items can be clicked, sometimes they have two differents clickListener (for example two imageView in one item).
For now, the item manages the clicks himself like this :
item.imageView1.setOnClickListener(....){
startActivity(Activity2);
}
item.imageView2.setOnClickListener(....){
startActivity(Activity1);
}
But I have a problem : I need to put some variables in the activity which will be started, so what is the best way to do this :
1) My item need to know these variables and continues to manage his own click ?
2) My item has a listener which call startActivity with the variables (like the fragment or parent activity or an object dedicated to this) ?
If you need more precisions, ask me.
Thx.
Make an interface for passing those values.
public interface MyRecyclerCallback {
void onItemClicked(Integer o); //insert whatever you want to pass further, possibly translated to form packable to intents
}
Then add it to your adapter from the activity with recycler like you would any listener. Either constructor or by separate method.
Pass it further down to every children upon their creation.
Call it when onClick gets detected with appropriate argument.
The actual argument may be some abstract thing
depending on your logic. It's more of a general idea. That's the way I do it with my recyclers.
In your activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecycler = (RecyclerView) findViewById(R.id.recycler);
MyAdapter adapter = new MyAdapter(this,new MyRecyclerCallback() {
#Override
public void onItemClicked(Integer o) { //any argument you like, might be an abstract
Intent i = new Intent(this,ActivityTwo.class);
i.putExtra(EXTRA_VALUE,o);
startActivity(i);
}
});
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(adapter);
}
Adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.Child>{
private final Context mContext;
private final MyRecyclerCallback mCallback;
private List<Integer> mChildren;
public MyAdapter(Context ctx, MyRecyclerCallback myRecyclerCallback) {
mContext = ctx;
mCallback = myRecyclerCallback;
mChildren = new ArrayList<>();
}
public void populateList(List<Integer> list ) { //this can be a network call or whatever you like
mChildren.addAll(list);
}
#Override
public Child onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(mContext).inflate(R.layout.item, parent, false);
return new Child(v,mCallback);
}
#Override
public void onBindViewHolder(Child holder, int position) {
holder.setValue1(mChildren.get(position)*3);
holder.setValue2(mChildren.get(position));
}
#Override
public int getItemCount() {
return mChildren.size();
}
public class Child extends RecyclerView.ViewHolder{
View mView1;
View mView2;
private int mValue1;
private int mValue2;
public Child(View itemView, final MyRecyclerCallback mCallback) {
super(itemView);
mView1 = itemView.findViewById(R.id.view1);
mView2 = itemView.findViewById(R.id.view2);
mView1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mCallback.onItemClicked(mValue1);
}
});
mView2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mCallback.onItemClicked(mValue2);
}
});
}
public void setValue1(int position) {
mValue1 = position;
}
public void setValue2(int position) {
mValue2=position;
}
}
}
this a good alternative to handle onclick and onlongclick
ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do it
}
});
http://www.littlerobots.nl/blog/Handle-Android-RecyclerView-Clicks/
You can have a single OnClickListener on your ViewHolder class and use it with an interface to determine the type of item which has been clicked.
For example, say you have a model class called Item :
public class Item {
private int itemType;
private String itemDescription;
private String optionalExtra;
public static final int ITEM_TYPE_1 = 1;
public static final int ITEM_TYPE_2 = 2;
public Item(int itemType, String itemDescription) {
this.itemType = itemType;
this.itemDescription = itemDescription;
}
public Item(int itemType, String itemDescription, String optionalExtra) {
this.itemType = itemType;
this.itemDescription = itemDescription;
this.optionalExtra = optionalExtra;
}
}
You have a custom interface defined to intercept click on items in recyclerview :
public interface CustomClickListener {
void onClickOfItemType1( int position );
void onClickOfItemType2( int position );
}
Inside your adapter for recyclerview, in the viewholder class :
//Similar implementation for other ViewHolders too.
public class ViewHolderType1 extends RecyclerView.ViewHolder implements View.OnClickListener {
ImageView imageView;
TextView textView;
View itemView;
public ViewHolderType1(View view) {
super(view);
this.itemView = view;
//initialize other views here
//Set item click listener on your view
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
int itemPosition = getAdapterPosition();
if( itemPosition != RecyclerView.NO_POSITION ) {
customClickListener.onClickOfItemType1(itemPosition);
//You can call onClickOfItemType2(itemPosition) in your other type of view holder class
}
}
}
In your Activity or Fragment :
Pass the customClickListener as a parameter to your adapeter :
CustomAdapter customAdapter = new CustomAdapter(List<Item> itemList, new CustomClickListener {
#Override
void onClickOfItemType1(int position) {
Item item = itemList.get(position);
//This item is of type 1
//You can implement serializable / parcelable in your item class and use it to directly pass across item to your activity
Intent intent = new Intent(Activity.this, CustomActivity1.class);
intent.putExtra("item", item);
startActivity(intent);
}
#Override
void onClickOfItemType2(int position) {
Item item = itemList.get(position);
//This item is of type 2
//You can implement serializable / parcelable in your item class and use it to directly pass across item to your activity
Intent intent = new Intent(Activity.this, CustomActivity2.class);
intent.putExtra("item", item);
startActivity(intent);
}
});
In case you are trying to trigger different activities on click of different views; inside your viewclicklistener implementation check the view id and trigger the corresponding activity.
#Override
public void onClick(View v) {
int itemPosition = getAdapterPosition();
if( itemPosition != RecyclerView.NO_POSITION ) {
switch(v.getId()) {
case R.id.imageView :
customClickListener.onClickOfItemType1(itemPosition);
break;
case R.id.textView :
customClickListener.onClickOfItemType2(itemPosition);
break;
}
}
}
This is a guide for usage of RecyclerView :
https://guides.codepath.com/android/using-the-recyclerview