Button functionality in Fragment representing a list - android

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 **/
}
});

Related

Is this a wrong way to implement a RecyclerView in Android?

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.

How to add/remove child items in a RecyclerView, suggestions for an Order Restaurant App?

Actually i'm trying to make an app that will permise to take orders in a restoraunt.
As probably you know when a waiter take's an order he can add any item from a menu to record's (something like a notebook) and even additionally he can add a variant to added item.
Example, i have a pizza menu in my app when i press some items from my menu it's added to the notebook (Menu and Notebook are 2 RecyclerView) after i've added the type of pizza's i can "modify" them by adding a subitem or better a child to the main item.
Or better i have 3 different pizza's on my notebook by clicking on one of them i'll be able to add a variant chosen from another recyclerView as "PIZZA" and variant "WITH PEPERONI" or "WITHOUT PEPERONI" or "WITH MORE MOZZARELLA".
For now i think i'm doing fine with the app like i have yet all what i need, as you can see on this GIF i'm adding from recyclerView "FOOD" to the "NOTEBOOK" recyclerView some items and by pressing VARIANTI i'm able to add Child items to last added FOOD.
But the issue comes when i have to delete a single VARIANT added to a food i would have some suggestion on how can i like click on a single VARIANT and would be able to delete the single one.
Here is my Adapter code:
public class AdapterPTERM extends RecyclerView.Adapter<AdapterPTERM.ExampleViewHolder> {
private List<ItemPTERM> mExampleList;
private final LayoutInflater mInflater;
private OnItemClickListener mListener;
private List<Variant> variants;
private ManageAddRemoveCallbacks mManageAddRemoveCallbacks ;
private View.OnClickListener varientClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if(v.getTag()!= null){
int position = (int) v.getTag();
}
}
};
public interface OnItemClickListener{
void onItemClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
mListener = listener;
}
#NonNull
#Override
public ExampleViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_pterm,parent,false);
return new ExampleViewHolder(v,mListener);
}
AdapterPTERM(Context context, List<ItemPTERM> exampleList){
mExampleList = exampleList;
mInflater = LayoutInflater.from(context);
}
#Override
public void onBindViewHolder(#NonNull final ExampleViewHolder holder, final int position) {
final ItemPTERM item = mExampleList.get(position);
holder.desc.setText(item.getBtnName());
holder.qta.setText(String.valueOf(item.getQuant()));
holder.variantsContainer.removeAllViews();
// ADDING CHILD HERE
variants = item.getVariants();
if (variants != null && variants.size() > 0){
for(Variant v : variants){
View vView = mInflater.inflate(R.layout.variant_layout,holder.variantsContainer,false);
TextView nameTV = vView.findViewById(R.id.variant_name);
nameTV.setText(v.getName());
vView.setTag(position);
vView.setOnClickListener(varientClickListener);
holder.variantsContainer.addView(vView);
}
}
tvAddItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
void onAddItem(true);
}
});
tvRemoveItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
void onBtnClickRemoveItem(true);
}
});
// NOT IMPORTANT (CHANGING FIELDS COLOR)
if(position % 2 == 0 ){
holder.itemView.setBackgroundColor(Color.parseColor("#C0C0C0"));
}else if(position % 2 == 1){
holder.itemView.setBackgroundColor(Color.parseColor("#D3D3D3"));
}
}
#Override
public int getItemCount() {
return mExampleList.size();
}
public class ExampleViewHolder extends RecyclerView.ViewHolder {
public TextView desc;
public TextView qta;
private LinearLayout variantsContainer;
ExampleViewHolder(View itemView, final OnItemClickListener listener) {
super(itemView);
desc = itemView.findViewById(R.id.Desc);
qta = itemView.findViewById(R.id.Qta);
variantsContainer = itemView.findViewById(R.id.ll_child_items);
try {
mManageAddRemoveCallbacks = ((ManageAddRemoveCallbacks) this);
} catch (ClassCastException e) {
throw new ClassCastException("ManageAddRemoveCallbacks must implement ManageAddRemoveCallbacksCallback.");
}
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(listener != null){
int position = getAdapterPosition();
if(position != RecyclerView.NO_POSITION){
listener.onItemClick(position);
}
}
}
});
}
}
public void removeItem(int position) {
mExampleList.remove(position);
notifyItemRemoved(position);
}
public interface ManageAddRemoveCallbacks {
void onAddItem(boolean isAdded);
void onBtnClickRemoveItem(boolean isRemoved);
}
}
You can implement interfaces on their respective click listeners();First go to your adapter and declare an interface like this:
private ManageAddRemoveCallbacks mManageAddRemoveCallbacks ;
public interface ManageAddRemoveCallbacks {
void onAddItem(boolean isAdded);
void onBtnClickRemoveItem(boolean isRemoved);
}
Now in your viewHolder initialize your interface:
try {
this.mManageAddRemoveCallbacks = ((ManageAddRemoveCallbacks) currentFragment);
} catch (ClassCastException e) {
throw new ClassCastException("ManageAddRemoveCallbacks must implement ManageAddRemoveCallbacksCallback.");
}
Now in your bind method where you will add click listeners to your views assign values to your interfaces
tvAddItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
void onAddItem(ture or false depending on your requirements);
}
});
tvRemoveItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
void onBtnClickRemoveItem(ture or false depending on your requirements);
}
});
Now after doing that you need to implement these interfaces in your activity/fragment something like this:
Your Activity extends AppCompatActivity implements YourAdapter.InterfaceName
so that you can use these. In this way you will get the values in your activity.

how to bind a RecyclerView item and its containing button clicked event together

I have a recyclerView and its items contain a button. And I wanna get the recyclerview item position when I click on the button. Here is the problem, when the button clicked the button clicked event is triggered but the recyclerview item under neath is not, the clicked event can not go through the button on the top to recyclerview item.
How can I get this click-through event or how can I bind them together? Please someone tell me if know the answer.
update:
I could not find any similar post online. usually recyclerview contains button is register to different OnclickedListener. In my case, they need to handle the same listener, particularly the recyclerview handle it. But the button just block the clicked event of recyclerview. here is the viewholder codeļ¼š
`public class GridViewAdapter extends RecyclerView.Adapter {
private final LinkedList<Device> mValues;
private final OnListFragmentInteractionListener mListener;
private OnLongClickListener mOnLongClickListener = null;
public GridViewAdapter(LinkedList<Device> items, OnListFragmentInteractionListener listener) {
mValues = items;
mListener = listener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.device, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
// holder.mIconView.setImageResource(R.drawable.ic_switch);
holder.mNameView.setText(holder.mItem.device_name());
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (null != mListener) {
mListener.onDeviceClicked(holder.mItem);
}
}
});
}
public void deleteByDeviceCode(int deviceCode) {
for(int i=0;i<mValues.size();i++) {
if (mValues.get(i).device_code() == deviceCode) {
mValues.remove(i);
}
}
}
public void deleteByPosition(int pos) {
mValues.remove(pos);
}
public int getDeviceCodeByPosition(int pos) {
return mValues.get(pos).device_code();
}
#Override
public int getItemCount() {
return mValues.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final View mView;
public final Button mButton;
public final TextView mNameView;
public Device mItem;
public ViewHolder(View view) {
super(view);
mView = view;
mButton = (Button) view.findViewById(R.id.dev_button);
mNameView = (TextView) view.findViewById(R.id.dev_lable);
}
#Override
public String toString() {
return super.toString() + " '" + mNameView.getText() + "'";
}
}
}`
When creating view holders, try passing them index of element represented by them, or even reference to whole element. Now, when you've receiving click event in view holder, with event itself you should pass the (index of) element that is represented by that holder/row.

Item manages his click or call listener ? (recyclerView)

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

RecyclerVIew OnItemClick return -1

I want to setup click handlers for views within the RecyclerView row. I want to handle item click from a fragment. I tried to code it by this tutorial. But when I try to identify which view was clicked, it returns -1.
I have an inteface within my adapter.
public interface OnItemClickListener {
void onItemClick(View itemView, int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.mOnItemClickListener = listener;
}
Then inside the ViewHolder constructor i call this:
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(v, getLayoutPosition());
}
}
});
And by last here is the part when i set the listener from my fragment:
mFollowingAdapter.setOnItemClickListener(new FollowingAdapter.OnItemClickListener() {
#Override
public void onItemClick(View itemView, int position) {
Log.d(Constants.TAG, itemView.getId() + " " + position);
}
});
Full VIewHolder code:
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView request_author_profile;
private TextView request_name;
private ImageView request_accept;
private ImageView request_decline;
public ViewHolder(final View itemView) {
super(itemView);
request_author_profile = (ImageView) itemView.findViewById(R.id.profile_image);
request_name = (TextView) itemView.findViewById(R.id.request_name);
request_decline = (ImageView) itemView.findViewById(R.id.btn_request_decline);
request_accept = (ImageView) itemView.findViewById(R.id.btn_request_accept);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(itemView, getLayoutPosition());
}
}
});
}
}
Thanks for help.
Try setting the anonymous intermediate listener in the onBindViewHolder() method instead of the ViewHolder constructor, passing the item's position in the data set to onItemClick() instead of getLayoutPosition().
The purpose of a ViewHolder is to represent a single list item visible on the screen, initially unrelated to any underlying piece of data. onBindBiewHolder() method binds it to an item in a data set for as long as the item should be displayed. Setting the intermediate listener there will assure that the position of the item in the data set will be passed to the client listener.
Here's a simplified example:
Adapter:
public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
public interface OnItemClickListener {
void onItemClick(int position);
}
/* this is the public listener that you set from the outside of the adapter */
private OnItemClickListener mOnItemClickListener;
#Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
/* update the intermediate listener so that it passes
* the correct position to the public listener */
holder.setOnClickListener(new OnClickListener() {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(position);
}
}
/* ... */
}
/* ... */
}
ViewHolder:
public class MyViewHolder extends RecyclerView.ViewHolder {
private View mView;
public MyViewHolder(final View itemView) {
mView = itemView;
}
/* delegate the setter to the (root) view */
public setOnClickListener(OnClickListener listener) {
mView.setOnClickListener(listener);
}
}
Note that this is note a very good implementation performance-wise, because it creates a new listener object every time a ViewHolder is bound, but I think it conveys the idea well. A better solution would be to keep a field in the ViewHolder describing a position in the data set, use it in the intermediate listener and update its value in onBindViewHolder().

Categories

Resources