It is my third day now dealing with the handling of my view clicks. I originally was using ListView, then I switched to RecyclerView. I have added android:onclick elements to every control on my row_layout and I am handling them in my MainActivity like this:
public void MyMethod(View view) {}
In my old ListView implementation, I have done setTag(position) to be able to get it in MyMethod by doing this inside it:
Integer.parseInt(view.getTag().toString())
This worked nicely without problems. Though now I am dealing with RecyclerView and being forced to use the ViewHolder, which does not offer a setTag method. After searching for 2 hours, I have found that people use setTag like this:
holder.itemView.setTag(position)
This was acceptable. Though when I try to get the value from the MyMethod function using the line:
Integer.parseInt(view.getTag().toString())
The application crashes. I have read several implementation of onclick handling inside the adapter which works but I have to use the MainActivity because I am using something that is unique to that activity.
TL;DR I want to send the position of the clicked row to my MainActivity in a simple manner.
Edit: I apologize for the confusion since my topic was not very thorough. I have a RecyclerView and an adapter. The adapter is linked to my row_layout. This row_layout xml has one root LinearLayout. Inside it there is one TextView, another LinearLayout (which has two TextViews) and one Button (for simplicity). I do not want to suffer for dealing with the clicks on RecylerView like I did with the ListView. So, I have decided to add an android:onclick for every control, then link TextView and LinearLayout to a single method and link the Button (and future Buttons) to their unique methods. What I am missing is that I want to be able to tell the position on each of the receiving methods on my MainActivity. If I must link everything that comes from the adapter and goes into the MainActivity to a single onclick handler, so be it. Although, how would I tell which control fired the click?
Edit 2: The requested layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:onClick="MyMethod"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1">
<TextView
android:id="#+id/letter"
android:onClick="MyMethod"
android:layout_width="60dp"
android:layout_height="fill_parent"
/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:onClick="MyMethod"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/firstname"
android:onClick="MyMethod"
android:layout_width="fill_parent"
android:layout_height="17dp" />
<TextView
android:id="#+id/longname"
android:onClick="MyMethod"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<Button
android:text="Test"
android:onClick="OtherMethod"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:id="#+id/process"/>
</LinearLayout>
You can achieve this by creating an interface inside your adapter for an itemclicklistener and then you can set onItemClickListener from your MainActivity.
Somewhere inside your RecyclerViewAdapter you would need the following:
private onRecyclerViewItemClickListener mItemClickListener;
public void setOnItemClickListener(onRecyclerViewItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
public interface onRecyclerViewItemClickListener {
void onItemClickListener(View view, int position);
}
Then inside your ViewHolder (which I've added as an inner class inside my adapter), you would apply the listener to the components you'd like the user to click, like so:
class RecyclerViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public ImageView imageview;
RecyclerViewHolder(View view) {
super(view);
this.imageview = (ImageView) view
.findViewById(R.id.image);
imageview.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if (mItemClickListener != null) {
mItemClickListener.onItemClickListener(v, getAdapterPosition());
}
}
}
This example shows an onClickListener being applied to the image inside a ViewHolder.
recyclerView.setAdapter(adapter);// set adapter on recyclerview
adapter.notifyDataSetChanged();// Notify the adapter
adapter.setOnItemClickListener(new RecyclerViewAdapter.onRecyclerViewItemClickListener() {
#Override
public void onItemClickListener(View view, int position) {
//perform click logic here (position is passed)
}
});
To implement this code, you would setOnItemClickListener to your adapter inside MainActivity as shown above.
EDIT
Because the View is getting passed into the OnItemClickListener, you can perform a switch statement inside the listener to ensure that the right logic is being performed to the right component. All you would need to do is take the logic from the MyMethod function and copy and paste it to the component you wish it to be applied to.
Example:
recyclerView.setAdapter(adapter);// set adapter on recyclerview
adapter.notifyDataSetChanged();// Notify the adapter
adapter.setOnItemClickListener(new RecyclerViewAdapter.onRecyclerViewItemClickListener() {
#Override
public void onItemClickListener(View view, int position) {
Switch (view.getId()) {
case R.id.letter:
//logic for TextView with ID Letter here
break;
case R.id.firstname:
//logic for TextView with ID firstname here
break;
....
//the same can be applied to other components in Row_Layout.xml
}
}
});
You would also need to change something inside the ViewHolder. instead of applying the OnClickListener to an ImageView, you would need to apply to the whole row like so:
RecyclerViewHolder(View view) {
super(view);
this.imageview = (ImageView) view
.findViewById(R.id.image);
view.setOnClickListener(this);
}
EDIT 2
Explanation:
So, with every RecyclerView. You need three components, The RecyclerView, RecyclerViewAdapter and the RecyclerViewHolder. These are what define the actual components the user sees (RecyclerView) and the Items within that View. The Adapter is where everything is pieced together and the Logic is implemented. The ins and outs of these components are nicely explained by Bill Phillips with the article RecyclerView Part 1: Fundamentals For ListView Experts over at Big Nerd Ranch.
But to further explain the logic behind the click events, it's basically utilizing an interface to pass information from the RecyclerViewAdapter to the RecyclerViewHolder to your MainActivity. So if you follow the life-cycle of the RecyclerView adapter, it'll make sense.
The adapter is initialized inside your MainActivity, the adapter's constructor would then be called with the information being passed. The components would then be passed into the adapter via the OnCreateViewHolder method. This itself tells the adapter, that's how you would like the list to look like. The components in that layout, would then need to be individually initialized, that's where the ViewHolder comes into play. As you can see like any other components you would initialize in your Activities, you do the same in the ViewHolder but because the RecyclerViewAdapter inflates the ViewHolder you can happily use them within your adapter as shown by Zeeshan Shabbir. But, for this example you would like multiple components to have various logic applied to each individual one in your MainActivity class.
That's where we create the click listener as a global variable (so it can be accessed by both the ViewHolder and the Adapter) the adapter's job in this case is to ensure the listener exists by creating an Interface you can initialize the listener through.
public interface onRecyclerViewItemClickListener {
void onItemClickListener(View view, int position);
}
After you've defined the information you would like the interface to hold (E.G. the component and it's position), you can then create a function in which the adapter will call to apply the logic from your Activity (same way you would called View.OnClickListener) but by creating a setOnItemClickListener, you can customize it.
public void setOnItemClickListener(onRecyclerViewItemClickListener mItemClickListener) {
this.mItemClickListener = mItemClickListener;
}
This function then needs onRecyclerViewItemClickListener variable passed to it, as seen in your MainActivity. new RecyclerViewAdapter.onRecyclerViewItemClickListener() in this case it's the interface you created before with the method inside that would need to be implemented hence the
#Override
public void onItemClickListener(View view, int position) {
}
is called.
All the ViewHolder does in this scenario is pass the information (The components it's self and the position) into the onItemClickListener with the components attached (inside the onClick function) to finalize the actual click functionality.
if you would like me to update the explanation in anyway, let me know.
I think you are stuck with handling multiple clicks on ReceylerView if that is the case then let me share you the a code snippet from my project. That's how i handle the click in RecyclerView
public class ExploreItemAdapter extends RecyclerView.Adapter<ExploreItemAdapter.ViewHolder> {
private WSResponseDTO<Data> wsResponseDTO;
public ExploreItemAdapter(WSResponseDTO<Data> wsResponseDTO) {
this.wsResponseDTO = wsResponseDTO;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.consumer_dashboard_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.tvCompanyName.setText(wsResponseDTO.getData().getPosts().get(position).getStoreInfo().getStoreName());
holder.tvBranchName.setText("(" + wsResponseDTO.getData().getPosts().get(position).getStoreInfo().getBranchName() + ")");
holder.tvLikes.setText(wsResponseDTO.getData().getPosts().get(position).getPost().getFavoriteCount() + "");
holder.tvShares.setText(wsResponseDTO.getData().getPosts().get(position).getPost().getShareCount() + "");
holder.validity.setText(wsResponseDTO.getData().getPosts().get(position).getPost().getValidFrom() + "-" +
wsResponseDTO.getData().getPosts().get(position).getPost().getValidTo());
holder.sTime.setText(wsResponseDTO.getData().getPosts().get(position).getPost().getTime());
holder.tvTitle.setText(wsResponseDTO.getData().getPosts().get(position).getPost().getHeading());
holder.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(view.getContext(), "card is touched", Toast.LENGTH_SHORT).show();
view.getContext().startActivity(new Intent(view.getContext(), ConsumerDetailOfferActivity.class).putExtra("post", wsResponseDTO.getData().getPosts().get(position)));
}
});
}
#Override
public int getItemCount() {
return wsResponseDTO.getData().getPosts().size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.card_explore)
CardView cardView;
#BindView(R.id.tv_company_name)
TextView tvCompanyName;
#BindView(R.id.tv_branch_name)
TextView tvBranchName;
#BindView(R.id.tv_title)
TextView tvTitle;
#BindView(R.id.tv_like_count)
TextView tvLikes;
#BindView(R.id.tv_share_count)
TextView tvShares;
#BindView(R.id.tv_validity)
TextView validity;
#BindView(R.id.tv_sTime)
TextView sTime;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
you can set click listener to any item in bindViewHolder() like i did for cardView I hope this will help some.
Related
I have an activity which contain a recycler view, in this activity is implemented the edit mode like a lot of applications. everythings works well but I have some performance issue and i'am tryng to goes more deep in the pest practices.
User goes in edit mode by select a menuItem in the toolbar which is placed in the activity, so in the menuItemClickListener in the activity I call a method of the adapter which is used to tell him that user want to go in edit mode:
mAdapter.setEditMode(true);
then in the adapter:
public void setEditMode(boolean editMode){
this.editMode = editMode;
notifyDataSetChanged(); //in order to change the items layout
}
Now the most difficult part: I need to change the itemClickListener when the editMode variable is set to true, so the listener associated with the holder's itemView change dinamically. I am doing this think in onBindViewHolder so I can set the right listener when the edit mode variable change.
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder,int position) {
if(editMode){
holder.itemView.setOnClickListener(listener1);
}
else{
holder.itemView.setOnClickListener(listener2);
}
}
this solution works but I know that placing a listener inside onBindViewHolder method is a bad practice so I would like to find a solution that allows to implement the listener in the viewHolder constructor.
This is not simple because when the editMode variable is changing the viewHolder constructor is not being called, so he can't set the right listener.
are there any best practice to do this?
After scouring various StackOverFlow answers regarding the most optimum location for a clickListener, people seem to be divided across multiple implementations. Here is what I know for adding a listener in the ViewHolder.
1. Adapter:
In your Adapter, override the onCreateViewHolder() method
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(//pass in your args);
ImageView imageview1 = //init your views
TextView textView = //init your views
return new MyViewHolder(view, textView);
}
2. Viewholder:
When you create your Viewholder class, allow it to implement View.OnClickListener and override the onClick method there.
public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public ImageView imageView1;
private MyViewHolder(View itemView, ImageView imageView) {
super(itemView);
itemView.setOnClickListener(this);
imageView1 = imageView;
}
#Override
public void onClick(View view) {
//Implement your click functionality here
}
}
I have implement a RecyclerView and it works fine. I have an ArrayList which contains the data for the recycler view. The layout of each item is complicated. It contains two frameLayout. The framelayout1 contains an image and a text and the framelayout2 contains an image and four texts. When the user clicks on the framelayout1 I want to open the Activity1 and when the users clicks on the framelayout2 I want to open the Activity2. I have already search for the onClick in Recycler View and I have found very useful this. But how can I get the position of the arrayList in order to pass it via Intent in the activity1 or activity2?
Try getAdapterPosition() from inside the view holder so that you may get the adapter position of the click the user made.
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Toast.makeText(context, String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
}
}
For more in getAdapterPosition() follow this link
Try this
public class ClosetListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
ClosetListAdapter (CallBack callback){
this.callback = callback
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder baseholder, int position) {
ViewHolder holder = (ViewHolder) baseholder;
holder.setPosition(position);
holder.name.setText(product.getName());
}
static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView name = null;
private OnProductClickListener onProductClickListener;
public ViewHolder(View itemView, OnProductClickListener onClickListener) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.item_name);
itemView.setOnClickListener(this)
}
public void setProdcut(Product product) {
this.product = product;
}
#Override
public void onClick(View v) {
if (callback!= null) {
callback.itemClicked(pos);
}
}
public void setPosition(int position){
this.pos = position;
}
}
interface CallBack {
void itemClicked(int position);
}
}
I've also faced the same problem.
I wanted to find of the position of the clicked/selected item of the RecyclerView() and perform some specific operations on that particular item.
getAdapterPosition() method works like a charm for these kind of stuff. I found this method after a day of long research and after trying numerous other methods.
int position = getAdapterPosition();
Toast.makeText(this, "Position is: "+position, Toast.LENGTH_SHORT).show();
You do not have to use any extra method. Just create a global variable named 'position' and initialize it with getAdapterPosition() in any of the major method of the adapter (class or similar).
Here is a brief documentation from this link.
getAdapterPosition
added in version 22.1.0
int getAdapterPosition ()
Returns the Adapter position of the item represented by this ViewHolder.
Note that this might be different than the getLayoutPosition() if there are pending adapter updates but a new layout pass has not happened yet.
RecyclerView does not handle any adapter updates until the next layout traversal. This may create temporary inconsistencies between what user sees on the screen and what adapter contents have. This inconsistency is not important since it will be less than 16ms but it might be a problem if you want to use ViewHolder position to access the adapter. Sometimes, you may need to get the exact adapter position to do some actions in response to user events. In that case, you should use this method which will calculate the Adapter position of the ViewHolder.
Happy to help. Feel free to ask doubts.
I'm trying to implement a way to handle item selection on a RecyclerView. I personally don't like the way suggested in some answers on SO of passing through gestures, and I thought that implementing an OnClickListener, as suggested here and here, was waaay cleaner.
The fact is that... this pattern doesn't actually work! I'm really not able to understand why my OnClickListener.onClick is never called. It's kinda like another method intercepts the click before onClick can take care of it.
This is my code:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView tvName;
ImageView star;
public ViewHolder(View itemView) {
super(itemView);
tvName = (TextView) itemView.findViewById(R.id.CHAT_ITEM_name);
star = (ImageView) itemView.findViewById(R.id.CHAT_ITEM_star);
Fonts.setTypeface(tvName, regular);
}
#Override
public void onClick(View view) {
int position = getLayoutPosition();
select(position);
}
}
Unfortunately it's very important for me to able to access the position of the clicked item in the whole dataset, in order to remove it, so doing something like indexOfChild isn't acceptable too: I tried, but this method gives you the position of the item in the visibile part of the list, thus making list.remove(position) impossible.
Looking at the updated code: you are not setting the onClickListener to any of the views in the ViewHolder. It is an understandable mistake to forget the click listener.
Just use:
tvName.setOnClickListener(this);
star.setOnClickListener(this);
You can set to both or just one of them. You can also simply get the parent layout of these two views, so that the whole item itself in the adapter can be clickable.
itemView.setOnClickListener(this);
You can do it in your onBindViewHolder
#Override
public void onBindViewHolder(ReportViewHolder holder, int position {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// handle your click here.
} });
}
Simplely Click Handler your ViewHolder. Recycler View don't have special attaching click handlers like ListView which has the method setOnItemClickListener().
** public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
** in public ViewHolder(Context context, View itemView) set public void onClick(View view)
** get position by: int position = getLayoutPosition(); User user = users.get(position);
Hi I have a app that uses a Recycler view to display a bunch of items. Now I want to run an android test on the list but I don't know how to set a programmatic click on a given item. Can anyone tell me how to achieve this?
You have to implement it from scratch. The RecyclerView lacks for some of the awesome features that a ListView provides by default. Given this, in the adapter you have to declare an interface for the Observer between Fragment/Activity and the RecyclerView.
The correct method to attach this click event is onBindViewHolder. Then in the adapter you keep the reference to the CustomListener or the View.OnClickListener.
Here is a quick example on how to do this:
#Override
public void onBindViewHolder(SearchListAdapter.ViewHolder holder, final int position) {
//Item clicked
holder.mParent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Select or deselect
mListener.notify(holder, position);
}
});
}
And the ViewHolder should be like:
public class ViewHolder extends RecyclerView.ViewHolder {
private View mParent;
public ViewHolder(View itemView) {
super(itemView);
mParent = itemView;
}
}
Where mParent is the current row View.
I have been playing around with RecyclerView for a little bit. Is there any easy way to put OnClickListener for items in RecyclerView? I have tried implementing it in ViewHolder. The onClick event never got triggered.
And I have used notifyItemInserted(position) for adding new value into RecyclerView. The UI does not got refreshed automatically. Needed to pull up and down to refresh. But when I invoke notifyDatasetChanged(..), it is ok.
I have applied DefaultItemAnimator to RecyclerView. But, not seeing any animation when new item added.
Thanks advance for any idea.
This is the first Android L component I have tested out and I am stucking there.
Here is my Adapter class:
public class AdapterRecyclerView extends RecyclerView.Adapter<AdapterRecyclerView.MyViewHolder> {
private List<String> arrExperiences;
//Provide a reference to the type of views that you are using - Custom ViewHolder
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView tvExperienceTitle;
public TextView tvExperienceDesc;
public MyViewHolder(RelativeLayout itemView) {
super(itemView);
tvExperienceTitle = (TextView) itemView.findViewById(R.id.tv_experience_title);
tvExperienceDesc = (TextView) itemView.findViewById(R.id.tv_experience_desc);
}
}
//Provide a suitable constructor : depending on the kind of dataset.
public AdapterRecyclerView(List<String> arrExperiences){
this.arrExperiences = arrExperiences;
}
//Create new view : invoke by a Layout Manager
#Override
public AdapterRecyclerView.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RelativeLayout view = (RelativeLayout) LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item_recycler, parent, false);
MyViewHolder myViewHolder = new MyViewHolder(view);
return myViewHolder;
}
#Override
public void onBindViewHolder(AdapterRecyclerView.MyViewHolder viewHolder, int position) {
//get element from your dataset at this position.
//replace the content of the view with this element.
viewHolder.tvExperienceTitle.setText(arrExperiences.get(position));
}
#Override
public int getItemCount() {
return arrExperiences.size();
}
public void addExperience(String experience, int position){
arrExperiences.add(position, experience);
notifyItemInserted(position);
//notifyDataSetChanged();
}
public void removeExperience(){
int index = (int) (Math.random() * arrExperiences.size());
arrExperiences.remove(index);
notifyItemRemoved(index);
//notifyDataSetChanged();
}
}
Simply add this in your Adapter:
#Override
public void onBindViewHolder(AdapterRecyclerView.MyViewHolder viewHolder, int position) {
yourItems.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//do your stuff
}
});
}
Please see my answer here. You do need an extra class (which may be included as part of the full release) but it will allow you to create OnItemClickListeners the way you are used to for ListViews.
Since you still didn't mark correct any answer, and even if it's an old question, I will try to provide the way I do. I think it is very clean and professional. The functionalities are taken from different blogs (I still have to mention them in the page), merged and methods have been improved for speed and scalability, for all activities that use a RecycleView.
https://github.com/davideas/FlexibleAdapter
At lower class there is SelectableAdapter that provides selection functionalities and it's able to maintain the state after the rotation, you just need to call the onSave/onRestore methods from the activity.
Then the class FlexibleAdapter handles the content with the support of the animation (calling notify only for the position. Note: you still need to set your animation to the RecyclerView when you create it the activity).
Then you need to extend over again this class. Here you add and implements methods as you wish for your own ViewHolder and your Domain/Model class (data holder). Note: I have provided an example which does not compile because you need to change the classes with the ones you have in your project.
I think that, it's the ViewHolder that should keep the listeners of the clicks and that it should be done at the creation and not in the Binding method (that is called at each invalidate from notify...() ).
Also note that this adapter handles the basic clicks: single and long clicks, if you need a double tap you need to use the way Jabob Tabak does in its answer.
I still have to improve it, so keep an eye on it. I also want to add some new functionalities like the Undo.
Here you get a simple Adapter class which can perform onItemClick event on each list row for the recyclerview.