I am trying to get child view by position. I could get view when one item is clicked:
rvSellRecords.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
((MainActivity) getActivity()).showSellRecordFragment(position, view);
}
}));
Now I cannot get child view, without click - let's say by position for example:
rvSellRecords.someMagicalMethodWhichReturnsViewByPosition(5);
Question: How to get child view from RecyclerView?
EDIT FOR BOUNTY:
I have RecyclerView to show products list. When I click on it, I am adding new Fragment where I show product information. While opening I am updating toolbar with view from RecyclerView - this is working perfectly:
rvSellRecords.addOnItemTouchListener(new RecyclerItemClickListener(getContext(), new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
sellPresenter.onSellRecordSelected(position, view);
}
}));
When I click blue button with "+", I am incrementing quantity by 1.
public void onIncrementButtonClicked(){
sellRecord.setCount(sellRecord.getCount() + 1);
showQuantity();
bus.post(new SellRecordChangedEvent(sellRecord, sellRecordPosition));
}
Then I am posting updated sellRecord to first fragment using EventBus. There I am updating list data. I supposed that updating value(sell) automatically updates adapter. Now I am getting view from adapter using custom method(getView) which was created by me(you can find it below).
#Subscribe
public void onEvent(SellRecordChangedEvent event){
sell.getSellRecords().set(event.getSellRecordPosition(), event.getSellRecord());
sell.recalculate();
int position = event.getSellRecordPosition();
View view = adapter.getView(position);
bus.post(new TransactionTitleChangedEvent(null, view));
}
This is my adapter class - I changed adapter little bit to collect view in list and added method which returns view for respective position:
public class SellRecordsAdapter extends RecyclerView.Adapter<SellRecordsAdapter.ViewHolder> {
.....
.....
.....
List<View> viewList;
public SellRecordsAdapter(List<SellRecord> sellRecordList) {
.....
viewList = new ArrayList<>();
}
.....
.....
.....
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
.....
.....
.....
viewList.add(i, viewHolder.itemView);
}
public View getView(int position){
return viewList.get(position);
}
}
My problem: when I updating view in toolbar, I am getting old view. When quantity is 3, I am getting view with 2. When quantity 10 - view is with 9.
My question: how to get view from recycler view using position of item(without on click listener)?
Use recyclerView.findViewHolderForLayoutPosition(position) or
reyclerView.findViewHolderForAdapterPosition(position) to get the viewholder for postion. Then you can access any child from your viewholder.
Checkout Recyclerview
RecyclerView.ViewHolder holder = recycleView.findViewHolderForAdapterPosition(position);
ImageView imageView = holder.itemView.findViewById(R.id.iv_product);
This is a supplement to #Ravi Teja's answer. You can get the viewHolder from the recyclerView using position of the particular item, then get a particular view from the viewHolder as shown above
You can use RecyclerView's LayoutManager for it.
View view = layoutManager.findViewByPosition(position)
Hope this helps someone:
I was getting null pointer exceptions with:
recyclerView.findViewHolderForAdapterPosition
recyclerView.findViewHolderForItemId
layoutManager.findViewByPosition.
The reason was that there is a slight delay for the viewholder to be created.
I found the solution here: https://stackoverflow.com/a/33414430/7952427
I post an answer because which is really complex to findviews() from RecyclerView.
#Joe: After spending 4hours found one answer. Which gives me the proper view of the index.
mAdapter is adapter of RecyclerView
View v = recyclerView.findViewHolderForItemId(mAdapter.getItemId(index/position)).itemView;
Now just access your views by:
v.findViewById(R.id.edittext) OR any id.
it helped me, make a 100 ms delay before manipulate it, like this:
Handler handler = new Handler();
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
// rcv is my recyclerview
rcvStatus.getChildAt(1).setBackground(getActivity().getResources().getDrawable(R.drawable.disabled));
// or:
rcvStatus.getChildAt(1).setClickable(false);
}
}, 100);
Write this method in adapter.
public Object getItem(int position) {
return yourArrayList.get(position);
}
and you just need to call it like
yourAdapter.getItem(2);
pass your required position.
Hope it solves your problem.
just put this method in your code and you can call it as you likes
void someMagicalMethodWhichReturnsViewByPosition(int position){
//I assumes child views are CardView
CardView c = (CardView)rvSellRecords.getItem(int position);
///optional codes
//////////
}
now I understand your problem. you need to use interface for join recyclerview item and activity.
you must define an interface class like below:
public interface IViewClick {
public void onClickButtonAdd();
}
add this parameter to your adapter class:
private IViewClick mListener;
and initialize it in constructor with value that get from inputs.
when user click on PLUS button, you send event to activity by this line:
mListener.onClickButtonAdd();
in your activity class you must implements IViewClick interface and add your code there, like this:
#Override
public void onClickButtonAdd() {
/// TODO every thing that you want.
/// change your toolbar values.
}
it is not good solution for you.
RecyclerView.ViewHolder holder =
mRecyclerView.findViewHolderForItemId(mAdapter.getItemId(i));
I wouldn't recommend tracking the view list yourself. It could lead to weird issues with item updates, position updates, etc.
Instead on your SellRecordChangedEvent, use findViewHolderForAdapterPosition() instead of adapter.getView().
#Subscribe
public void onEvent(SellRecordChangedEvent event){
sell.getSellRecords().set(event.getSellRecordPosition(), event.getSellRecord());
sell.recalculate();
int position = event.getSellRecordPosition();
View view = yourrecyclerview.findViewHolderForAdapterPosition(position);
bus.post(new TransactionTitleChangedEvent(null, view));
}
http://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#findViewHolderForAdapterPosition(int)
And as a side note, it's better to implement an actual item click listener to the itemView on the ViewHolder instead of using touch listener. There's lots of examples of this online.
So the recyclerview and your product information are in 2 different fragments yes? You are expecting the recyclerview's views to update when they are not even in foreground? also you are changing adapter data item's data at position event.getSellRecordPosition() , but you are not notifying the adapter that its dataset changed, either by adapter.notifyDataSetChanged() or the other notifyItemChanged(position) methods.
I'd modify your onEvent() like so:
#Subscribe
public void onEvent(SellRecordChangedEvent event){
sell.getSellRecords().set(event.getSellRecordPosition(), event.getSellRecord());
sell.recalculate();
int position = event.getSellRecordPosition();
MyViewHolder holder = adapter.onCreateViewHolder(yourRecyclerView, 0);
adapter.onBindViewHolder(holder,position);
View view = adapter.getView(position);
bus.post(new TransactionTitleChangedEvent(null, view));
}
Calling on createViewHolder and next BindViewHolder on your adapter will definitely update the views for that position, then your adapter.getView(position) should return you the latest view.
Here MyViewHolder is your viewholder class and yourRecyclerview, is the reference to your recycler view
for (int i = 0; i < recycler_view.getAdapter().getItemCount(); i++) {
View viewTelefone = recycler_view.getChildAt(i);
}
If you want to replace text on a particular edit text for same position:
for (int i = 0; i < recycler_view.getAdapter().getItemCount(); i++) {
if(adpterPostion==i)
{
View viewTelefone = recycler_view.getChildAt(i);
EditText et_mobile = (EditText) viewTelefone.findViewById(R.id.et_mobile);
et_mobile.setText("1111111");
}
}
Related
I'm building an Android app of media, and trying to add a Playlist feature to it, the user will be able to create a playlist of his own and modify it.
I'm using a RecyclerView to show the user list of songs which he can choose from.
The problem is I don't understand how to pass the Arraylist of chosen songs from the adapter to the fragment.
I've tried to use the Observer pattern but the don't know how to use that information.
This is my Fragment for creating the playlist:
public class CreatePlaylistFragment extends Fragment implements PlaylistAdapterInterface {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_create_playlist, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ArrayList<ProgramsData> dataArrayList = ProgramsReceiver.getPrograms();
ArrayList<ProgramsData> sortedList = new ArrayList<>(dataArrayList);
adapter = new CreatePlaylistAdapter(dataArrayList, view.getContext(), this);
adapter.adapterInterface = this;
ivCreatePlaylist.setOnClickListener(v -> {
Toast.makeText(v.getContext(), "Creating Playlist!", Toast.LENGTH_SHORT).show();
new PlaylistsJsonWriter(playlistArrayList,getContext()).execute();
});
}
#Override
public void OnItemClicked(ArrayList<ProgramsData> programs) {
programsToCreate = programs;
String s = etListName.getText().toString();
playlistArrayList.add(new Playlist(s, programsToCreate));
}
}
This is the Recycler Adapter with ViewHolder as inner class:
public class CreatePlaylistAdapter extends RecyclerView.Adapter<CreatePlaylistViewHolder> {
List<ProgramsData> programsDataList;
Context context;
public PlaylistAdapterInterface adapterInterface = null;
public CreatePlaylistAdapter(List<ProgramsData> programsDataList, Context context , PlaylistAdapterInterface adapterInterface) {
this.programsDataList = programsDataList;
this.context = context;
this.adapterInterface = adapterInterface;
}
#NonNull
#Override
public CreatePlaylistViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.chose_program_to_playlist_item, viewGroup, false);
return new CreatePlaylistViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull CreatePlaylistViewHolder holder, int i) {
ProgramsData programsData = programsDataList.get(i);
holder.tvProgramName.setText(programsData.getProgramName());
if (programsData.getStudentName() != null)
holder.tvStudentName.setText(programsData.getStudentName());
else holder.tvLine.setText(""); //if there is no student the line won't be printed
holder.ivProfilePic.setImageResource(programsData.getProfilePic());
holder.programsData = programsData;
// holder.mAdapterInterface = adapterInterface;
adapterInterface.OnItemClicked(holder.programs);
}
#Override
public int getItemCount() {
return programsDataList.size();
}
}
class CreatePlaylistViewHolder extends RecyclerView.ViewHolder {
TextView tvProgramName;
TextView tvStudentName;
TextView tvLine;
CircleImageView ivProfilePic;
ToggleButton tbCheck;
ProgramsData programsData;
ArrayList<ProgramsData> programs;
PlaylistAdapterInterface mAdapterInterface;
public CreatePlaylistViewHolder(#NonNull View itemView) {
super(itemView);
tvProgramName = itemView.findViewById(R.id.tvProgramName);
tvStudentName = itemView.findViewById(R.id.tvStudentName);
ivProfilePic = itemView.findViewById(R.id.ivProfilePic);
tvLine = itemView.findViewById(R.id.tvLine);
tbCheck = itemView.findViewById(R.id.tbCheck);
programs= new ArrayList<>();
tbCheck.setOnClickListener(v -> {
if (tbCheck.isChecked()) {
tbCheck.setBackgroundResource(R.drawable.ic_radio_button_checked);
programs.add(programsData);
} else if (!tbCheck.isChecked()) {
tbCheck.setBackgroundResource(R.drawable.ic_check);
programs.remove(programsData);
}
});
}
}
And this is the interface for the Observer Pattern:
public interface PlaylistAdapterInterface {
void OnItemClicked(ArrayList<ProgramsData> programs);
}
I know it's a lot of code, but I just don't understand how to pass the data from the adapter back to the fragment...
I don't understand exactly what are you trying to do.
The code contains several errors that I'll try to explain.
A clear error that you have made stays in onBindViewholder where you call the listener at the creation of every item instead than after clicking on it.
You have simply add an onClickListener in the viewHolder.getItemView() or in a specific view of the viewholder and then perform the operation you need to do once an item is clicked.
If you set a listener inside onBindViewHolder, you also have a method called
holder.getAdapterPosition() that you can use to understand which item are you clicking on.
The viewholder should be used only to setup the views accordingly to the data you are binding and nothing else. For this reason, you should not pass any object or listener to it and instead use the approach above.
If you have just to retrieve the selected songs after an user confirms it's playlist you can just add a public method on your adapter
public List<ProgramsData> getSelectedSongs()
that you can call from your fragment when an user click a confirm button.
In order to have a list of all selected song, you can have another list
ArrayList<ProgramsData> selectedPrograms;
that you are going to fill after the click.
The content of the listener inside the onBindViewHolder could be
ProgramsData currentProgram = programs.get(holder.getAdapterPosition());
if(selectedPrograms.contains(currentProgram){
selectedPrograms.remove(currentProgram);
}else{
selectedPrograms.add(currentProgram);
}
notifyItemChanged(holder.getAdapterPosition); //You can use this to update the view of the selected item
Then inside the onBindViewHolderMethod you can check whether the items you are binding are part of the selectedList and update the views accordingly.
You can use callback method. Maintain list of selected items in array list and send back to fragment when done button is clicked or any other button you have placed for complete action.
Follow these steps
-Create an Interface with list parameter.
-Fragment should implement this interface.
-Then when you initialize Recyclerview adapter pass this interface object.
-When done is clicked call overridden method of this interface and send selected songs list as argument.
I have a recyclerview which its items contain textView and switchCompat. And in the same activity I have also a textView that have a numerical value in it. The task is when the switchCompat turned on the text view above the recyclerview which contain the numerical value should increase by the value in the recyclerview item textview. I already did that but when scrolling in the recyclerview the switchCompat back to the default state and the value of the numerical textview backs to its old value,
Any help with that?
I Apology for not being able to post a part of the code now and I'll do this as soon as i can, I just posted it now in case anyone pass through something like this before
Thank you
The key to a recycler view or any adapter view in Android is to have the adapter adapt your models to the view. In your case your view is a TextView plus a Switch, so your adapter must adapt some model to this view. In this case I'd choose a simple model like this:
class ItemModel {
String text;
boolean on;
}
I've omitted getters and setters for simplicity
This model contains an string text which reflects the text in your text view and a boolean on that reflects the state of the switch. When true the switch is checked and when false it's unchecked.
There's tons of ways to represent this model. I've chosen this one, you may choose a different one. The point is, you need to save the state somewhere and this is what I mean by model - the view model.
Now let's build an adapter that can do 2 things - Update the models when the switch is clicked and tell the activity that the switch changed state. Here's one way to do this:
public class ItemsAdapter extends
RecyclerView.Adapter<ItemsAdapter.ViewHolder> {
#NonNull
private final List<ItemModel> itemModels;
#Nullable
private OnItemCheckedChangeListener onItemCheckedChangeListener;
ItemsAdapter(#NonNull List<ItemModel> itemModels) {
this.itemModels = itemModels;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item, parent, false));
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, int position) {
ItemModel item = itemModels.get(position);
holder.text.setText(item.text);
holder.switchCompat.setChecked(item.on);
// Make sure we update the model if the user taps the switch
holder.switchCompat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int adapterPosition = holder.getAdapterPosition();
ItemModel tapped = itemModels.get(adapterPosition);
itemModels.set(adapterPosition, new ItemModel(tapped.text, isChecked));
if (onItemCheckedChangeListener != null) {
onItemCheckedChangeListener.onItemCheckedChanged(adapterPosition, isChecked);
}
}
});
}
#Override
public void onViewRecycled(#NonNull ViewHolder holder) {
super.onViewRecycled(holder);
holder.switchCompat.setOnCheckedChangeListener(null);
}
#Override
public int getItemCount() {
return itemModels.size();
}
public void setOnItemCheckedChangeListener(#Nullable OnItemCheckedChangeListener onItemCheckedChangeListener) {
this.onItemCheckedChangeListener = onItemCheckedChangeListener;
}
interface OnItemCheckedChangeListener {
/**
* Fired when the item check state is changed
*/
void onItemCheckedChanged(int position, boolean isChecked);
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView text;
SwitchCompat switchCompat;
ViewHolder(View itemView) {
super(itemView);
text = itemView.findViewById(R.id.item_text);
switchCompat = itemView.findViewById(R.id.item_switch);
}
}
}
There's a lot to digest, but let's focus on the important bits - the method onBindViewHolder. The first 3 lines are the classic recycling of the view. We grab the model at the correct position and set the elements in the view that correspond to model's attributes.
Then it gets more interesting. We set a OnCheckedChangeListener to update the model and the activity every time the switch changes state. The first 3 lines change the model in the adapter and the rest uses the custom interface OnItemCheckedChangeListener to notify the listener about the switch change. It's important to notice that inside the method OnCheckedChangeListener you should no longer use position, but rather use holder.getAdapterPosition. This will give you the correct position in the adapter's data list.
Since now the adapter has always the correct models inside the data list, every time the method onBindViewHolder is called the adapter knows exactly how to setup the view. This means that while scrolling and recycling the views, it will preserve the state of each item within the models inside the data list.
It's important to remove the OnCheckedChangeListener when the view gets recycled - onViewRecycled. This avoids messing the count when the adapter is setting the value of switchCompat in the onBindViewHolder.
Here's an example of how the activity could look like:
public class MainActivity extends AppCompatActivity {
private int count = 0;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<ItemModel> data = new ArrayList<>();
for (int i = 1; i <= 100; i++)
data.add(new ItemModel("Item " + i, false));
ItemsAdapter adapter = new ItemsAdapter(data);
((RecyclerView) findViewById(R.id.recyclerview)).setAdapter(adapter);
final TextView countTextView = findViewById(R.id.count);
drawCount(countTextView);
adapter.setOnItemCheckedChangeListener(new ItemsAdapter.OnItemCheckedChangeListener() {
#Override
public void onItemCheckedChanged(int position, boolean isChecked) {
if (isChecked)
count++;
else
count--;
drawCount(countTextView);
}
});
}
private void drawCount(TextView countTextView) {
countTextView.setText(String.valueOf(count));
}
}
This code is meant to demonstrate the idea, not to follow :) In any case, we setup all the initial state and then set up the custom listener OnItemCheckedChangeListener to update the text view in the activity.
The layout files shouldn't be relevant here, but as you can imagine the activity has a text view with id count and there's a recycler view with the id recyclerview.
Hope this helps
It solved for me after adding the below method to the adapter:
#Override
public int getItemViewType(int position) {
return position;
}
My problem is: I have a video streaming happening on one of the views inside the RecyclerView.
When the user scrolls, the view gets recycled and other cameras starts their own streaming on that recycled viewholder. This is bad for user interface since the streaming process takes some seconds to start.
How can I say to the RecyclerView: "Hey Recycler, please, do not recycle that exact position x and give that position ALWAYS the same viewholder you gave it the first time, instead of random one"?
Please someone help me =(
In your getItemViewType(int position) method of adapter, assign unique values for each video, so it will always return same ViewHolder for same video as you wish.
return unique positive number as type for each video type (here i used the adapter position as unique key)
return negative numbers for any non-video items. (nothing special here, just to avoid conflicts with video items, we use negative numbers for non-video items)
I hope you get the idea. cheers :)
#Override
public int getItemViewType(int position) {
// Just as an example, return 0 or 2 depending on position
// Note that unlike in ListView adapters, types don't have to be contiguous
if(dataList.get(position).isVideo()){
return position;
}else{
return -1;//indicates general type, if you have more types other than video, you can use -1,-2,-3 and so on.
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case -1: View view1 = LayoutInflater.from(parent.getContext())
.inflate(R.layout.general_item, parent, false);
return new GeneralViewHolder(view1);
default:View view2 = LayoutInflater.from(parent.getContext())
.inflate(R.layout.video_item, parent, false);
return new VideoViewHolder(view2);
}
}
Perform viewHolder.setIsRecyclable(false) on the ViewHolder you want not to be recycled.
From docs of ViewHolder#setIsRecyclable(boolean):
Informs the recycler whether this item can be recycled. Views which are not recyclable will not be reused for other items until setIsRecyclable() is later set to true.
This will cause only one ViewHolder to be created.
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
...
#Override
public void onViewAttachedToWindow(final RecyclerView.ViewHolder holder) {
if (holder instanceof VideoViewHolder) {
holder.setIsRecyclable(false);
}
super.onViewAttachedToWindow(holder);
}
#Override
public void onViewDetachedFromWindow(final RecyclerView.ViewHolder holder) {
if (holder instanceof VideoViewHolder){
holder.setIsRecyclable(true);
}
super.onViewDetachedFromWindow(holder);
}
...
}
RecyclerView uses one view multiple times, when it contains the list which is not displaying on the screen at a time(means a list contain large amount of items which is not displaying on screen at same time you need to scroll up and down). When user scroll the list the offscreen items are reused to display the remaining list items which is called recycling.
To Stop recycling the items call this method in your onBindViewHolder method:
viewHolder.setIsRecyclable(false);
This statement stop the recycling the views.
To Start recycling the items call this method in your onBindViewHolder method:
viewHolder.setIsRecyclable(true);
I hope this will solve your problem.
Thanks
Your problem comes from the viewholder itself. Viewholders keep reference to views, while the adapter don't. The adapter keeps the data collection only. So, add a field to the viewholder to keep a reference of the data element you used to populate the view in the viewholder. In other words:
public class SomeViewHolder extends RecyclerView.ViewHolder{
private View view;
private Data data;
public SomeViewHolder(View itemView) {
super(itemView);
view = itemView;
}
public void bindData(Data data){
view.setData(data);
this.data = data;
}
public void setData(Data data){
this.data = data;
}
public Data getData(){
return data;
}
public View getView(){
return view;
}
}
Now, the viewholder know which element of the adapter is using. Therefore, when overriding the binding method in the adapter, you can check if the holder has already bonded with some data, and, if the data contains video, you can avoid the binding and forcefully set an already loaded view.
#Override
public void onBindViewHolder(SomeViewHolder holder, int position) {
//videoViewData is a data field you have to put into the adapter.
//videoView is a view field you have to put into the adapter.
if(adapterData.get(position).equals(videoViewData)){
holder.setView(videoView);
holder.setData(adapterData.get(position));
}else{
holder.bindData(adapterData.get(position));
if(adapterData.get(position).isVideo()){
videoViewData = adapterData.get(position);
videoView = holder.getView();
}
}
}
Finally, you'll have to override the onViewRecycled method in the adapter, so, when a view containing a video gets recycled, you can get the view and put it somewhere else.
public void onViewRecycled(SomeViewHolder holder){
if(holder.getData().isVideo()){
videoViewData = holder.getData().
videoView = holder.getView();
videoView.pauseVideo();
}
}
keep in mind, this can cause some serious leaks if you don't manage the stored view. Also, you have to define methods for telling when your data is video, and a properly defined equals method.
Best way to handle item not to recycle in recyclerview this answer will resolve your problem.
Not to recycle item
Try using this for that particular position:
holder.setIsRecyclable(false);
Hope this may help.
If You are using query, you can use
query.limit(//no of items you want to show in your RecyclerView)
give it a try.
or Plese post your QueryCode
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 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.