onBindViewHolder repeats the results [duplicate] - java
Here's the XML for my items inside the RecyclerView
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/cvItems"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_margin="2dp"
card_view:cardElevation="0dp"
card_view:contentPadding="0dp"
card_view:cardBackgroundColor="#FFFFFF"
>
<LinearLayout
android:orientation="horizontal"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<TextView
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="0.8"
android:id="#+id/tvContent"
android:textSize="15dp"
android:paddingLeft="5dp"
android:paddingRight="5dp" />
<CheckBox
android:id="#+id/cbSelect"
android:layout_width="0dip"
android:layout_weight="0.2"
android:layout_height="match_parent"
android:button="#drawable/cb_checked"
android:gravity="center_horizontal"
android:textAlignment="center"
android:layout_gravity="center_horizontal" />
</LinearLayout>
</android.support.v7.widget.CardView>
And here's the RecyclerView adapter that inflate the layout above for each of its items:
public class AdapterTrashIncome extends RecyclerView.Adapter<AdapterTrashIncome.ViewHolder> {
private ArrayList<ObjectIncome> myItems = new ArrayList<>();
public AdapterTrashIncome(ArrayList<ObjectIncome> getItems, Context context){
try {
mContext = context;
myItems = getItems;
}catch (Exception e){
Log.e(FILE_NAME, "51: " + e.toString());
e.printStackTrace();
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvContent;
public CheckBox cbSelect;
public ViewHolder(View v) {
super(v);
tvContent = (TextView) v.findViewById(R.id.tvContent);
cbSelect = (CheckBox) v.findViewById(R.id.cbSelect);
}
}
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
final ObjectIncome objIncome = myItems.get(position);
String content = "<b>lalalla</b>";
holder.tvContent.setText(Html.fromHtml(content));
}
}
The problem is, let's say I have 10 items inside the RecyclerView. When I checked the checkbox on item 1,2,3 then I scroll down the RecyclerView, suddenly some of the other items eg items 8,9 is checked. And when I scroll up again, item 1 and 3 is checked but not item 2. Any idea why this happen?
That's an expected behavior. You are not setting your checkbox selected or not. You are selecting one and View holder keeps it selected. You can add a boolean variable into your ObjectIncome object and keep your item's selection status.
You may look at my example. You can do something like that:
public class AdapterTrashIncome extends RecyclerView.Adapter<AdapterTrashIncome.ViewHolder> {
private ArrayList<ObjectIncome> myItems = new ArrayList<>();
public AdapterTrashIncome(ArrayList<ObjectIncome> getItems, Context context){
try {
mContext = context;
myItems = getItems;
}catch (Exception e){
Log.e(FILE_NAME, "51: " + e.toString());
e.printStackTrace();
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvContent;
public CheckBox cbSelect;
public ViewHolder(View v) {
super(v);
tvContent = (TextView) v.findViewById(R.id.tvContent);
cbSelect = (CheckBox) v.findViewById(R.id.cbSelect);
}
}
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
final ObjectIncome objIncome = myItems.get(position);
String content = "<b>lalalla</b>";
holder.tvContent.setText(Html.fromHtml(content));
//in some cases, it will prevent unwanted situations
holder.cbSelect.setOnCheckedChangeListener(null);
//if true, your checkbox will be selected, else unselected
holder.cbSelect.setChecked(objIncome.isSelected());
holder.cbSelect.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//set your object's last status
objIncome.setSelected(isChecked);
}
});
}
}
In short, its because of recycling the views and using them again!
how can you avoid that :
1.In onBindViewHolder check whether you should check or uncheck boxes.
don't forget to put both if and else
if (...)
holder.cbSelect.setChecked(true);
else
holder.cbSelect.setChecked(false);
Put a listener for check box! whenever its checked statues changed, update the corresponding object too in your myItems array ! so whenever a new view is shown, it read the newest statue of the object.
Just add two override methods of RecyclerView
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
Use this only if you have limited number of items in your RecyclerView.
I tried using boolean value in model and keep the CheckBox status, but it did not help in my case. What worked for me is this.setIsRecyclable(false);
public class ComponentViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemView) {
super(itemView);
...
this.setIsRecyclable(false);
}
More explanation on this can be found here
NOTE: This is a workaround. To use it properly you can refer the document which states
Calls to setIsRecyclable() should always be paired (one call to setIsRecyclabe(false) should always be matched with a later call to setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally reference-counted.
I don't know how to do this in code, if someone can provide more code on this.
You can use Model class to keep track of each RecyclerView item's CheckBox. Full reference is from : RecyclerView Checkbox Android
setTag and getTag is used to keep track of CheckBox status. Check full reference link for more information. It also teaches how to send checked items to Next Activity.
Make Model class:
public class Model {
private boolean isSelected;
private String animal;
public String getAnimal() {
return animal;
}
public void setAnimal(String animal) {
this.animal = animal;
}
public boolean getSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
}
Create integer.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="btnplusview">1</integer>
<integer name="btnpluspos">2</integer>
</resources>
Finally RecyclerView.Adapter looks like this:
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> {
private LayoutInflater inflater;
public static ArrayList<Model> imageModelArrayList;
private Context ctx;
public CustomAdapter(Context ctx, ArrayList<Model> imageModelArrayList) {
inflater = LayoutInflater.from(ctx);
this.imageModelArrayList = imageModelArrayList;
this.ctx = ctx;
}
#Override
public CustomAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.rv_item, parent, false);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(final CustomAdapter.MyViewHolder holder, int position) {
holder.checkBox.setText("Checkbox " + position);
holder.checkBox.setChecked(imageModelArrayList.get(position).getSelected());
holder.tvAnimal.setText(imageModelArrayList.get(position).getAnimal());
// holder.checkBox.setTag(R.integer.btnplusview, convertView);
holder.checkBox.setTag(position);
holder.checkBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Integer pos = (Integer) holder.checkBox.getTag();
Toast.makeText(ctx, imageModelArrayList.get(pos).getAnimal() + " clicked!", Toast.LENGTH_SHORT).show();
if (imageModelArrayList.get(pos).getSelected()) {
imageModelArrayList.get(pos).setSelected(false);
} else {
imageModelArrayList.get(pos).setSelected(true);
}
}
});
}
#Override
public int getItemCount() {
return imageModelArrayList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
protected CheckBox checkBox;
private TextView tvAnimal;
public MyViewHolder(View itemView) {
super(itemView);
checkBox = (CheckBox) itemView.findViewById(R.id.cb);
tvAnimal = (TextView) itemView.findViewById(R.id.animal);
}
}
}
Using Kotlin the only thing which solved this problem for me was to clear the OnCheckedChangeListener before setting the variable and then create a new OnCheckedChangeListener after checked has been set.
I do the following in my RecyclerView.ViewHolder
task.setOnCheckedChangeListener(null)
task.isChecked = item.status
task.setOnCheckedChangeListener { _: CompoundButton, checked: Boolean ->
item.status = checked
...
do more stuff
...
}
I recommend that not to use checkBox.setOnCheckedChangeListener in RecyclerView.Adapter. Because on scrolling RecyclerView, checkBox.setOnCheckedChangeListener will be fired by adapter. It's not safe. Instead, use checkBox.setOnClickListener to interact with user inputs.
For example:
public void onBindViewHolder(final ViewHolder holder, int position) {
...
holder.checkBoxAdapterTasks.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
boolean isChecked = holder.checkBoxAdapterTasks.isChecked();
if (isChecked) {
// checkBox clicked and checked
} else {
// checkBox clicked and unchecked
}
}
});
}
It might be very late but the simplest of all answers is to assign the check state in bind ViewHolder. RecyclerView will check and apply that state when reusing.
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
binding.checkbox.isChecked = item.isChecked
}
Maintain that state in your class. (Assign a initial default value)
class MyItem {
val isChecked: Boolean = false
}
onClickListener do your stuff and assign the state to class variable. In my case, I have delegate clickListener in view. So, it is like this in Adapter:
binding.checkbox.setOnClickListener {
onClickListener.invoke(item)
}
Then, in view, I am doing this:
val adapter = MyItem { item->
viewModel.checkedContactsList.value?.let { list ->
if (list.contains(item)) {
item.isChecked = false
list.remove(item)
} else {
item.isChecked = true
list.add(item)
}
}
}
In my case this worked.
#Override
public void onViewRecycled(MyViewHolder holder) {
holder.checkbox.setChecked(false); // - this line do the trick
super.onViewRecycled(holder);
}
As stated above, the checked state of the object should be included within object properties. In some cases you may need also to change the object selection state by clicking on the object itself and let the CheckBox inform about the actual state (either selected or unselected). The checkbox will then use the state of the object at the actual position of the given adapter which is (by default/in most cases) the position of the element in the list.
Check the snippet below, it may be useful.
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class TakePicImageAdapter extends RecyclerView.Adapter<TakePicImageAdapter.ViewHolder>{
private Context context;
private List<Image> imageList;
public TakePicImageAdapter(Context context, List<Image> imageList) {
this.context = context;
this.imageList = imageList;
}
#Override
public TakePicImageAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view= LayoutInflater.from(context).inflate(R.layout.image_item,parent,false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final TakePicImageAdapter.ViewHolder holder, final int position) {
File file=new File(imageList.get(position).getPath());
try {
Bitmap bitmap= MediaStore.Images.Media.getBitmap(context.getContentResolver(), Uri.fromFile(file));
holder.image.setImageBitmap(bitmap
);
} catch (IOException e) {
e.printStackTrace();
}
holder.selectImage.setOnCheckedChangeListener(null);
holder.selectImage.setChecked(imageList.get(position).isSelected());
holder.selectImage.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
holder.selectImage.setChecked(isChecked);
imageList.get(position).setSelected(isChecked);
}
});
holder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (imageList.get(position).isSelected())
{
imageList.get(position).setSelected(false);
holder.selectImage.setChecked(false);
}else
{
imageList.get(position).setSelected(true);
holder.selectImage.setChecked(true);
}
}
});
}
#Override
public int getItemCount() {
return imageList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ImageView image;public CheckBox selectImage;
public ViewHolder(View itemView) {
super(itemView);
image=(ImageView)itemView.findViewById(R.id.image);
selectImage=(CheckBox) itemView.findViewById(R.id.ch);
}
}
}
Use an array to hold the state of the items
In the adapter use a Map or a SparseBooleanArray (which is similar to a Map, but is a key-value pair of int and boolean) to store the state of all the items in our list of items and then use the keys and values to compare when toggling the checked state.
In the Adapter create a SparseBooleanArray:
// sparse boolean array for checking the state of the items
private SparseBooleanArray itemStateArray = new SparseBooleanArray();
Then in the item click handler onClick() use the state of the items in the itemStateArray to check before toggling, here is an example
#Override
public void onClick(View v) {
int adapterPosition = getAdapterPosition();
if (!itemStateArray.get(adapterPosition, false)) {
mCheckedTextView.setChecked(true);
itemStateArray.put(adapterPosition, true);
} else {
mCheckedTextView.setChecked(false);
itemStateArray.put(adapterPosition, false);
}
}
Also, use sparse boolean array to set the checked state when the view is bound:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(position);
}
#Override
public int getItemCount() {
if (items == null) {
return 0;
}
return items.size();
}
void loadItems(List<Model> tournaments) {
this.items = tournaments;
notifyDataSetChanged();
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
CheckedTextView mCheckedTextView;
ViewHolder(View itemView) {
super(itemView);
mCheckedTextView = (CheckedTextView) itemView.findViewById(R.id.checked_text_view);
itemView.setOnClickListener(this);
}
// use the sparse boolean array to check
void bind(int position) {
if (!itemStateArray.get(position, false)) {
mCheckedTextView.setChecked(false);}
else {
mCheckedTextView.setChecked(true);
}
}
}
and final adapter will be like this.
This will happened when use setOnCheckedChangeListener instead of that use setObClickListener and inside that just do this easy handle:
if (list.get(position).isCheck()) {
list.get(position).setCheck(false);
} else {
list.get(position).setCheck(true);
}
Note: in your list model add one boolean variable with name check and set getter and setter for that , in above case mine is setCheck and isCheck
This is a Kotlin Solution That Worked for Me
class SpecialtyFragmentRecyclerAdapter : RecyclerView.Adapter<SpecialtyFragmentRecyclerAdapter.SpecialtyViewHolder>(){
private var _specialtySet = mutableSetOf(
"Yoruba Attires",
"Hausa Attires",
"Senator",
"Embroidery",
"Africa Fashion",
"School Uniform",
"Military and Para-Military Uniforms",
"Igbo Attires",
"South-South Attires",
"Kaftans",
"Contemporary",
"Western Fashion",
"Caps"
).toSortedSet()
val specialtySet: Set<String> get() = _specialtySet
val savedSpecialtySet = mutableSetOf<String>().toSortedSet()
inner class SpecialtyViewHolder(
var itemBinding: SpecialtyFragmentRecyclerItemBinding
) :
RecyclerView.ViewHolder(itemBinding.root) {
fun bind(specialty: String) = with(itemBinding) {
specialtyFragmentYorubaAttiresCheckBox.text = specialty
specialtyFragmentYorubaAttiresCheckBox.isChecked = savedSpecialtySet.contains(specialty)
//AREA OF INTEREST
//Either Setting the CheckBox onCheckChangeListener to works
specialtyFragmentYorubaAttiresCheckBox.setOnCheckedChangeListener(null)
specialtyFragmentYorubaAttiresCheckBox.setOnCheckedChangeListener(
CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
if (buttonView.isPressed) { //OR this Also Works {Check if the Button is Pressed Before verifying the Checked State}
if (isChecked) {
savedSpecialtySet.add(specialty) //Perform Your Operation for Checked State
} else {
savedSpecialtySet.remove(specialty) //Perform Your Operation for unChecked State
}
}
}
)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SpecialtyViewHolder {
val viewBinding = SpecialtyFragmentRecyclerItemBinding
.inflate(LayoutInflater.from(parent.context), parent, false)
return SpecialtyViewHolder(viewBinding)
}
override fun onBindViewHolder(holder: SpecialtyViewHolder, position: Int) {
val specialty = _specialtySet.elementAt(position)
holder.bind(specialty)
}
override fun getItemCount(): Int {
return _specialtySet.size
}
fun populateList(list: MutableList<String>) {
savedSpecialtySet.addAll(list)
_specialtySet.addAll(list)
notifyDataSetChanged()
}
fun addNewSpecialty(specialty: String) {
_specialtySet.add(specialty.trim())
savedSpecialtySet.add(specialty.trim())
notifyDataSetChanged()
}
fun removeSpecialty(element: String) {
_specialtySet.remove(element)
savedSpecialtySet.remove(element)
notifyDataSetChanged()
}
}
I had the same problem in a RecyclerView list with switches, and solved it using #oguzhand answer, but with this code inside the checkedChangeListener:
if (buttonView.isPressed()) {
if (isChecked) {
group.setSelected(true);
} else {
group.setSelected(false);
}
} else {
if (isChecked) {
buttonView.setChecked(false);
} else {
buttonView.setChecked(true);
}
}
Where 'group' is the entity I want to select/deselect.
I've had the same issue. When I was clicking on item's - toggle buttons become checked in my RecyclerView. Toggle buttons appeared in every 10th item (for example if it was clicked in item with 0 index, items with 9, 18, 27 indexes were getting clicked too).
My code in onBindViewHolder was:
if (newsItems.get(position).getBookmark() == 1) {
holder.getToggleButtonBookmark().setChecked(true);
}
But then I added else statement:
/**
* Else statement prevents auto toggling.
*/
if (newsItems.get(position).getBookmark() == 1) {
holder.getToggleButtonBookmark().setChecked(true);
} else{
holder.getToggleButtonBookmark().setChecked(false);
}
And the problem was solved!
You need to separate onBindViewHolder(logic) interactions with CheckBox and user interactions with checkBox. I used OnCheckedChangeListener for user interactions (obviously) and ViewHolder.bind() for logic, that's why you need to set checked listener to null before setting up holder and after holder is ready - configure checked listener for user interactions.
boolean[] checkedStatus = new boolean[numberOfRows];
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
final ViewHolderItem itemHolder = (ViewHolderItem) holder;
// holder.bind should not trigger onCheckedChanged, it should just update UI
itemHolder.checkBox.setOnCheckedChangeListener(null);
itemHolder.bind(position);
itemHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
checkedStatus[holder.getAdapterPosition()] = true;
performCheckedActions(); //your logic here
} else {
checkedStatus[holder.getAdapterPosition()] = false;
performUncheckedActions(); //your logic here
}
}
});
}
public void bind(int position) {
boolean checked = checkedStatus[position];
if (checked) {
checkBox.setChecked(false);
} else {
checkBox.setChecked(true);
}
}
I solved this problem by creating a static global array and using it in onBindViewHolder
RecyclerView.Adapter realization class:
In which I created all global variables/objects needed.
public class RVAdapter extends RecyclerView.Adapter<RVAdapter.PersonViewHolder> {
private Context context;
...
public static class PersonViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView question, category;
TextView personAge;
ImageView upvote;
Button b1;
public static int k;
private int visibleThreshold = 5;
public static int i = 0;
static int check[]; //Static array
PersonViewHolder(View itemView, int i) {
super(itemView);
if(i == PersonViewHolder.k) {
b1 = (Button) itemView.findViewById(R.id.loadmore);
} else {
cv = (CardView) itemView.findViewById(R.id.cv);
question = (TextView) itemView.findViewById(R.id.question);
category = (TextView) itemView.findViewById(R.id.text_categ);
personAge = (TextView) itemView.findViewById(R.id.text1);
upvote = (ImageView) itemView.findViewById(R.id.upvote);
}
}
}
...
}
Here (in contructor of RVAdapter class) I gave size to the array equals to the size of items I'm going to display in the RecyclerView:
List<Person> persons;
RVAdapter(List<Person> persons){
this.persons = persons;
PersonViewHolder.check = new int[persons.size()];
PersonViewHolder.k = persons.size();
}
In onBindViewHolder I applied this concept on a button. When I click on a button - the background image of the button changes.
Object of button I used is names as "upvote", as "i" holds the position of each item in RecyclerView. I used it as an index of array which is working as a flag and which is keeping track of status of elements.
#Override
public void onBindViewHolder(final PersonViewHolder personViewHolder, final int i) {
if (i == PersonViewHolder.k) {
personViewHolder.b1.setText("load more");
} else {
personViewHolder.question.setText(persons.get(i).name);
personViewHolder.personAge.setText(persons.get(i).age);
if (personViewHolder.check[i] == 0) {
personViewHolder.upvote.setBackgroundResource(R.drawable.noupvote);
} else {
personViewHolder.upvote.setBackgroundResource(R.drawable.upvote);
}
personViewHolder.upvote.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (personViewHolder.check[i] == 0) {
personViewHolder.check[i] = 1;
personViewHolder.upvote.setBackgroundResource(R.drawable.upvote);
} else {
personViewHolder.check[i] = 0;
personViewHolder.upvote.setBackgroundResource(R.drawable.noupvote);
}
}
});
// personViewHolder.personPhoto.setImageResource(persons.get(i).photoId);
}
}
Okay there is a lot of answers here. But I will post my code and I will simply explain what I did... it maybe help juniors like me :D.
Objective:
We will create a list of RecyclerView that has CheckBox and RadioButton, something like this:
Model for list item with all needed data:
public class ModelClass {
private String time;
private boolean checked;
private boolean free;
private boolean paid;
public TherapistScheduleModel(String time, boolean checked, boolean free, boolean paid) {
this.time = time;
this.checked = checked;
this.free = free;
this.paid = paid;
}
public boolean isFree() {
return free;
}
public void setFree(boolean free) {
this.free = free;
}
public boolean isPaid() {
return paid;
}
public void setPaid(boolean paid) {
this.paid = paid;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public boolean getChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked= checked;
}
}
My RecyclerView.Adapter amazing realization:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private ListAllListeners listAllListeners;
private ArrayList<ModelClass> mDataList;
public MyAdapter(
Context context,
ArrayList<ModelClass> mDataList,
ListAllListeners listAllListeners
) {
this.mDataList = mDataList;
this.listAllListeners = listAllListeners;
this.context = context;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.single_view, parent, false);
return new MyViewHolder(view);
}
#Override
public int getItemCount() {
if (mDataList != null) {
return mDataList.size();
} else {
return 0;
}
}
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, final int position) {
// important to:
// setOnCheckedChangeListener to 'null'
holder.checkBoxTime.setOnCheckedChangeListener(null);
holder.freeRB.setOnCheckedChangeListener(null);
holder.paidRB.setOnCheckedChangeListener(null);
// Check Box
holder.checkBoxTime.setText(mDataList.get(holder.getAdapterPosition()).getTime());
// Here we check if the item is checked or not from the model.
if(mDataList.get(holder.getAdapterPosition()).getChecked()) {
holder.checkBoxTime.setChecked(true);
} else {
holder.checkBoxTime.setChecked(false);
}
holder.checkBoxTime.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (b) {
mDataList.get(holder.getAdapterPosition()).setChecked(true);
listAllListeners.onItemCheck(holder.checkBoxTime.getText().toString(), holder.getAdapterPosition());
} else {
mDataList.get(holder.getAdapterPosition()).setChecked(false);
listAllListeners.onItemUncheck(holder.checkBoxTime.getText().toString(), holder.getAdapterPosition());
}
}
});
// Radio Buttons
if(mDataList.get(holder.getAdapterPosition()).isFree()) {
holder.freeRB.setChecked(true);
} else {
holder.freeRB.setChecked(false);
}
holder.freeRB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (b) {
mDataList.get(holder.getAdapterPosition()).setFree(true);
listAllListeners.onFreeCheck(holder.freeRB.getText().toString(), holder.getAdapterPosition());
} else {
mDataList.get(holder.getAdapterPosition()).setFree(false);
listAllListeners.onFreeUncheck(holder.freeRB.getText().toString(), holder.getAdapterPosition());
}
}
});
// And so on to paidRB
}
/**
* Here is a list of clicked listeners to use them as you want ;).
* You can get a list of checked or unchecked of all.
*/
public interface ListAllListeners {
void onItemCheck(String checkBoxName, int position);
void onItemUncheck(String checkBoxName, int position);
void onFreeCheck(String name, int pos);
void onFreeUncheck(String name, int pos);
void onPaidCheck(String name, int pos);
void onPaidUncheck(String name, int pos);
}
class MyViewHolder extends RecyclerView.ViewHolder {
CheckBox checkBoxTime;
RadioButton freeRB, paidRB;
MyViewHolder(View itemView) {
super(itemView);
checkBoxTime = itemView.findViewById(R.id.timeCheckBox);
freeRB = itemView.findViewById(R.id.freeRadioBtn);
paidRB = itemView.findViewById(R.id.paidRadioBtn);
}
}
}
In Activity you get them something like this:
myAdapter = new MyAdapter(this, mDataList, new MyAdapter.ListAllListeners() {
#Override
public void onItemCheck(String checkBoxName, int position) {
Toast.makeText(getActivity(), "" + checkBoxName + " " + position, Toast.LENGTH_SHORT).show();
}
#Override
public void onItemUncheck(String checkBoxName, int position) {
Toast.makeText(getActivity(), "" + checkBoxName + " " + position, Toast.LENGTH_SHORT).show();
}
#Override
public void onFreeCheck(String name, int position) {
Toast.makeText(getActivity(), "" + name + " " + position, Toast.LENGTH_SHORT).show();
}
#Override
public void onFreeUncheck(String name, int position) {
Toast.makeText(getActivity(), "" + name + " " + position, Toast.LENGTH_SHORT).show();
}
#Override
public void onPaidCheck(String name, int position) {
Toast.makeText(getActivity(), "" + name + " " + position, Toast.LENGTH_SHORT).show();
}
#Override
public void onPaidUncheck(String name, int position) {
Toast.makeText(getActivity(), "" + name + " " + position, Toast.LENGTH_SHORT).show();
}
});
this is due to again and again creating view ,best option is clear cache before setting adapter
recyclerview.setItemViewCacheSize(your array.size());
In onBindViewHolder for views (checkbox, radio, switch, ...) you should setOnCheckedChangeListener(null) before and after new creation. For example:
public void onBindViewHolder(#NonNull ViewHolder holder,
int position) {
...
holder.switchCompat.setOnCheckedChangeListener(null);
...
holder.switchCompat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton,
boolean b) {
// TODO: 10/23/2022 do something
}
});
}
Solution is while CheckBox is checked. Need to store this separate list, and use that list to populate CheckBox in RecyclerView.
You can refer this link.
Complete example:
public class ChildAddressAdapter extends RecyclerView.Adapter<ChildAddressAdapter.CartViewHolder> {
private Activity context;
private List<AddressDetail> addressDetailList;
private int selectedPosition = -1;
public ChildAddressAdapter(Activity context, List<AddressDetail> addressDetailList) {
this.context = context;
this.addressDetailList = addressDetailList;
}
#NonNull
#Override
public CartViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View myView = inflater.inflate(R.layout.address_layout, parent, false);
return new CartViewHolder(myView);
}
#Override
public void onBindViewHolder(#NonNull CartViewHolder holder, int position) {
holder.adress_checkbox.setOnClickListener(view -> {
selectedPosition = holder.getAdapterPosition();
notifyDataSetChanged();
});
if (selectedPosition==position){
holder.adress_checkbox.setChecked(true);
}
else {
holder.adress_checkbox.setChecked(false);
}
}
#Override
public int getItemCount() {
return addressDetailList.size();
}
class CartViewHolder extends RecyclerView.ViewHolder {
TextView address_text,address_tag;
CheckBox adress_checkbox;
CartViewHolder(View itemView) {
super(itemView);
address_text = itemView.findViewById(R.id.address_text);
address_tag = itemView.findViewById(R.id.address_tag);
adress_checkbox = itemView.findViewById(R.id.adress_checkbox);
}
}
}
public class TagYourDiseaseAdapter extends RecyclerView.Adapter<TagYourDiseaseAdapter.OrderHistoryViewHolder> {
private ReCyclerViewItemClickListener mRecyclerViewItemClickListener;
private Context mContext;
List<Datum> deviceList = Collections.emptyList();
/**
* Initialize the values
*
* #param context : context reference
* #param devices : data
*/
public TagYourDiseaseAdapter(Context context, List<Datum> devices,
ReCyclerViewItemClickListener mreCyclerViewItemClickListener) {
this.mContext = context;
this.deviceList = devices;
this.mRecyclerViewItemClickListener = mreCyclerViewItemClickListener;
}
/**
* #param parent : parent ViewPgroup
* #param viewType : viewType
* #return ViewHolder
* <p>
* Inflate the Views
* Create the each views and Hold for Reuse
*/
#Override
public TagYourDiseaseAdapter.OrderHistoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tag_disease, parent, false);
TagYourDiseaseAdapter.OrderHistoryViewHolder myViewHolder = new TagYourDiseaseAdapter.OrderHistoryViewHolder(view);
return myViewHolder;
}
/**
* #param holder : view Holder
* #param position : position of each Row set the values to the views
*/
#Override
public void onBindViewHolder(final TagYourDiseaseAdapter.OrderHistoryViewHolder holder, final int position) {
Picasso.with(mContext).load(deviceList.get(position).getIconUrl()).into(holder.document);
holder.name.setText(deviceList.get(position).getDiseaseName());
holder.radioButton.setOnCheckedChangeListener(null);
holder.radioButton.setChecked(deviceList.get(position).isChecked());
//if true, your checkbox will be selected, else unselected
//holder.radioButton.setChecked(objIncome.isSelected());
holder.radioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
deviceList.get(position).setChecked(isChecked);
}
});
}
#Override
public int getItemCount() {
return deviceList.size();
}
/**
* Create The view First Time and hold for reuse
* View Holder for Create and Hold the view for ReUse the views instead of create again
* Initialize the views
*/
public class OrderHistoryViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ImageView document;
TextView name;
CheckBox radioButton;
public OrderHistoryViewHolder(View itemView) {
super(itemView);
document = itemView.findViewById(R.id.img_tag);
name = itemView.findViewById(R.id.text_tag_name);
radioButton = itemView.findViewById(R.id.rdBtn_tag_disease);
radioButton.setOnClickListener(this);
//this.setIsRecyclable(false);
}
#Override
public void onClick(View view) {
mRecyclerViewItemClickListener.onItemClickListener(this.getAdapterPosition(), view);
}
}
}
If it is not late; this is actually RecyclerView general problem. You can put your RecyclerView into a NestedScrollView and then add one line code to your adapter. All is done.
In your activity or fragment;
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
And in your activity where you set adapter add this:
ViewCompat.setNestedScrollingEnabled(recyclerView, false);
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
// your adapter code...
recyclerView.setAdapter(textSearchAdapter);
I faced the similar issue while using checkbox inside recycler view. After some detail analysis I got the root cause. let's look at the code once
In onBindViewHolder the line "holder.cbSelect.setChecked(yourList.isSelected());"
will always execute.
If we scroll up or scroll down the page, the onBindViewHolder will get called. As soon as onBindViewHolder will get called "holder.cbSelect.setChecked(yourList.isSelected());" will get tiggered and as a result
"holder.cbSelect.setOnCheckedChangeListener" will also get called and it will change the checkbox state, even if you have not changed the checkbox state. The reason is simple that it found checkbox state is changed from your updated list (yourList.isSelected()) which you select or dis-select the check box .
Now as a solution in override method of "public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)" method we need to add one condition that is
holder.cbSelect.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.isPressed()) {
//Check box state changed by user
//update your list based on checkbox value
// yourList.setSelected(isChecked);
}
}
});
What worked for me is to nullify the listeners on the viewHolder when the view is going to be recycled (onViewRecycled):
override fun onViewRecycled(holder: AttendeeViewHolder) {
super.onViewRecycled(holder)
holder.itemView.hasArrived.setOnCheckedChangeListener(null);
holder.itemView.edit.setOnClickListener { null }
}
Adding setItemViewCacheSize(int size) to RecyclerView and passing size of list solved my problem.
My code:
mrecyclerview.setItemViewCacheSize(mOrderList.size());
mBinding.mrecyclerview.setAdapter(mAdapter);
Source: link
Related
How to show this check box in all items of RecyclerView? [duplicate]
This question already has answers here: How to implement multi-select in RecyclerView? [closed] (5 answers) Closed 2 years ago. I wanna show all check box in all item of recycler view . when i long click on them. I will show you what i want to do in this image. When i long click on the picture folder in other folders a empty check box will appear then we can choose them and do what we want to them. at the end how can i do this when i click on item and this happen. this my adapter . public class NoteAdapter extends Adapter<NoteAdapter.MyView> { List<Note> notes; Activity activity1; public NoteAdapter(List<Note> noteList, Activity activity) { activity1 = activity; notes = noteList; } #NonNull #Override public NoteAdapter.MyView onCreateViewHolder(#NonNull ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_note, parent, false); return new MyView(itemView); } #Override public void onBindViewHolder(#NonNull final NoteAdapter.MyView holder, final int position) { holder.note.setText(notes.get(position).note); holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { #Override public boolean onLongClick(View view) { holder.checkbox.setVisibility(View.VISIBLE); //the code at the line up just set visible the selected item but i want to set visible all items check boxs return false; } }); } #Override public int getItemCount() { return notes.size(); } public class MyView extends RecyclerView.ViewHolder { private TextView name; private CheckBox checkBox; public MyView(#NonNull View itemView) { super(itemView); note = itemView.findViewById(R.id.note); chackbox = itemView.findViewById(R.id.chackBox); } } }
You can try this: public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.MyView> { List< Note > notes; Activity activity1; ArrayList<Integer> checkedList = new ArrayList<>(); RecyclerView recyclerView; //..... #Override public void onAttachedToRecyclerView(#NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); this.recyclerView = recyclerView; } #Override public void onBindViewHolder(#NonNull final NoteAdapter.MyView holder, final int position) { holder.name.setText(notes.get(position).note); holder.checkBox.setVisibility(checkedList.size() > 0 ? View.VISIBLE : View.GONE); holder.checkBox.setChecked(checkedList.contains(position)); } //...... public class MyView extends RecyclerView.ViewHolder { private TextView name; private CheckBox checkBox; public MyView(#NonNull View itemView) { super(itemView); name = itemView.findViewById(R.id.name); checkBox = itemView.findViewById(R.id.checkBox); itemView.setOnLongClickListener(new View.OnLongClickListener() { #Override public boolean onLongClick(View view) { if(checkedList.contains(getAdapterPosition())){ checkedList.remove(Integer.valueOf(getAdapterPosition())); if (checkedList.size() == 0){ notifyDataSetChanged(); }else { notifyItemChanged(getAdapterPosition()); } }else { checkedList.add(getAdapterPosition()); if (checkedList.size() == 1){ recyclerView.post(new Runnable() { #Override public void run() { notifyDataSetChanged(); } }); }else { notifyItemChanged(getAdapterPosition()); } } //the code at the line up just set visible the selected item but i want to set visible all items check boxs return false; } }); checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { #Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { if(b){ if(!checkedList.contains(getAdapterPosition())) checkedList.add(getAdapterPosition()); }else{ checkedList.remove(Integer.valueOf(getAdapterPosition())); if (checkedList.size() == 0){ recyclerView.post(new Runnable() { #Override public void run() { notifyDataSetChanged(); } }); } } } }); } } } I added long click method in the MyView and I made some changes. It works more stable. There is a position error in the onBind method. onAttachedToRecyclerView : some times notifyDataSetChanged() method gives error. I added to be able to use recyclerView
Getting id of a selected recyclerView item android
I have made a note app and everything works fine. Just in RecyclerView List when I choose an item or items and in menu I click delete button I the note is not deleted. Although delete method works in another place. Here's the method of delete button in menu : #Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { if (item.getItemId() == R.id.delete_selected) { for (NotePad notePad : notes) { if (selectedIds.contains(notePad.getId())) { database = new Database(this); database.deleteNote(notePad.getId()); } } adapter.notifyDataSetChanged(); return true; } return false; } And method of delete note in my database class: void deleteNote(long id) { SQLiteDatabase database = this.getWritableDatabase(); database.delete(DATABASE_TABLE, ID + "=?", new String[] {String.valueOf(id)}); database.close(); } I don't know how to relate id of selected note to my method cause deletNote method receives long value but my selectedId is List. Thanks in advance. UPDATED I realized my code works find but it doesn't refresh the list and also goes back from ActionMode to Toolbar. I tried adapter.notifyDataSetChanged();
Use this adapter. I have used interface. public class NotePadAdapter extends RecyclerView.Adapter < NotePadAdapter.ViewHolder > { private Context context; private LayoutInflater inflater; private List < NotePad > notes; private List < Long > selectedIds = new ArrayList < > (); private NoteClickListener listener; NotePadAdapter(Context context, NoteClickListener listener, List < NotePad > notes) { this.context = context; this.inflater = LayoutInflater.from(context); this.notes = notes; this.listener = listener; } public interface NoteClickListener { public void onNoteClick(NotePad notePad); } #NonNull #Override public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) { View view = inflater.inflate(R.layout.notes_list, parent, false); return new ViewHolder(view); } #Override public void onBindViewHolder(#NonNull ViewHolder holder, int position) { String title = notes.get(position).getTitle(); String date = notes.get(position).getDate(); String time = notes.get(position).getTime(); holder.setOnClickListener() { listener.onNoteClick(notes.get(position)) } holder.noteTitle.setText(title); holder.noteTitle.setText(title); holder.noteDate.setText(date); holder.noteTime.setText(time); long id = notes.get(position).getId(); if (selectedIds.contains(id)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { holder.rooView.setForeground(new ColorDrawable(ContextCompat.getColor(context, R.color.colorControlActivated))); } } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { holder.rooView.setForeground(new ColorDrawable(ContextCompat.getColor(context, android.R.color.transparent))); } } } #Override public int getItemCount() { return notes.size(); } public NotePad getItem(int position) { return notes.get(position); } public void setSelectedIds(List < Long > selectedIds) { this.selectedIds = selectedIds; notifyDataSetChanged(); } class ViewHolder extends RecyclerView.ViewHolder { TextView noteTitle, noteDate, noteTime; ConstraintLayout rooView; ViewHolder(#NonNull View itemView) { super(itemView); noteTitle = itemView.findViewById(R.id.note_title); noteDate = itemView.findViewById(R.id.note_date); noteTime = itemView.findViewById(R.id.note_time); rooView = itemView.findViewById(R.id.root_view); itemView.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { Intent i = new Intent(v.getContext(), ContentActivity.class); i.putExtra("ID", notes.get(getAdapterPosition()).getId()); v.getContext().startActivity(i); } }); } } } In your activity/fragment where you are initiating this adapter pass this for the listener and implement the interface and whenever you click any item on the recyclerview then you will your whole NotePad object inside this function.
Using Checkbox with recyclerview
I am new to android. I am having trouble with recyclerview with check boxes. I am using a recycler view that has check boxes. Now the titles with checked check boxes should appear in my activity. I have added the check box selected title in the array list how do I get them in the activity? I just need the checked check boxes array list in my activity. below is my adapter class public class CheckboxAdapter extends RecyclerView.Adapter<CheckboxAdapter.ViewHolder> { private List<SearchListItem> searchListItems; private OnSearchItemSelected onSearchItemSelected; public ArrayList<String> addTitle; CheckboxAdapter(List<SearchListItem> arrayList, OnSearchItemSelected onSearchItemSelected) { this.searchListItems = arrayList; this.onSearchItemSelected = onSearchItemSelected; } class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { #BindView(R.id.title) TextView mTxtTitle; #BindView(R.id.checkBox) CheckBox checkBox; ViewHolder(View row) { super(row); ButterKnife.bind(this, row); row.setOnClickListener(this); } #Override public void onClick(View view) { int pos = getAdapterPosition(); SearchListItem searchListItem = searchListItems.get(pos); try { onSearchItemSelected.onClick(pos, searchListItem); } catch (Exception e) { e.printStackTrace(); } } } #Override public int getItemCount() { // TODO Auto-generated method stub return searchListItems.size(); } #Override public void onBindViewHolder(#NonNull final CheckboxAdapter.ViewHolder holder, final int position) { SearchListItem output = searchListItems.get(position); holder.mTxtTitle.setText(output.title); holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { #Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { addAllTitle(holder); } } }); } public OnSearchItemSelected getOnSearchItemSelected() { return onSearchItemSelected; } public void addAllTitle(ViewHolder holder) { for (int i = 0; i < addTitle.size(); i++) { addTitle.add(String.valueOf(holder.mTxtTitle)); } } #NonNull #Override public CheckboxAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int arg1) { Context context = parent.getContext(); LayoutInflater inflater = LayoutInflater.from(context); View contactView = inflater.inflate(R.layout.adapter_checkbox, parent, false); return new CheckboxAdapter.ViewHolder(contactView); } }
you have to add one boolean in your model class such as isChecked make it true if checkBox is checked else false get same list which is passed in activity to adapter, this list will give you all checked boxes for ex in activity adapter = new MyAdapter(myList); /*you can get all checked boxes here, paste this code where you need it*/ for(i=0; i<myList.size();i++){ if(myList.isChecked()){ //... do somehing ... } } in adapter onBindViewHolder() method holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { #Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { myList.setchecked(isChecked); } });
RecyclerView messed up data when scrolling
Having a problem when scrolling RecyclerView after scrolling down and up. The idea is to change elements color, but when I scroll down everything is great and when the scroll goes up - the elements, which are shouldn't be colored are changing color. Here's my adapter: public class NotificationsAdapter extends RecyclerView.Adapter<NotificationsAdapter.ViewHolder> { private NotificationData notificationData; private Context mContext; private ArrayList<NotificationData> infromationList = new ArrayList<>(); public NotificationsAdapter(Context context, ArrayList<NotificationData> infromationList) { this.infromationList = infromationList; this.mContext = context; } #Override public NotificationsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemLayoutView; ViewHolder viewHolder; itemLayoutView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.notification_single_item, parent, false); viewHolder = new ViewHolder(itemLayoutView, viewType); return viewHolder; } #Override public void onBindViewHolder(NotificationsAdapter.ViewHolder holder, int position) { notificationData = infromationList.get(position); holder.notificationDate.setText(convertDate(notificationData.getDate())); holder.notificationStatus.setText(notificationData.getNotificationStatus()); holder.orderDescription.setText(notificationData.getNotificationLabel()); if ("true".equals(notificationData.getReadStatus())) { holder.root.setBackgroundColor(mContext.getResources().getColor(R.color.white)); holder.notificationStatus.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL)); } } #Override public int getItemCount() { return (null != infromationList ? infromationList.size() : 0); } public static class ViewHolder extends RecyclerView.ViewHolder { public TextView notificationDate; public TextView notificationStatus; public TextView orderDescription; public LinearLayout root; public ViewHolder(View itemView, int position) { super(itemView); notificationDate = (TextView) itemView.findViewById(R.id.notificationDate); notificationStatus = (TextView) itemView.findViewById(R.id.notificationStatus); orderDescription = (TextView) itemView.findViewById(R.id.orderDescription); root = (LinearLayout) itemView.findViewById(R.id.root); } } private String convertDate(String date) { String convertedDate; String[] parts = new String[2]; parts = date.split("T"); date = parts[0]; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd"); Date testDate = null; try { testDate = sdf.parse(date); }catch(Exception ex){ ex.printStackTrace(); } SimpleDateFormat formatter = new SimpleDateFormat("dd.mm.yyyy"); convertedDate = formatter.format(testDate); return convertedDate; } }
I had the same problem and the only solution I found for this is: holder.setIsRecyclable(false); Your recycler will not recycle anymore so the items will be the same when you scroll, and if you want to delete some item do not use notifyitemRemoved(position), use notifyDataSetChanged() instead.
Add setHasStableIds(true); in your adapter constructor and Override these two methodes in adapter. #Override public long getItemId(int position) { return position; } #Override public int getItemViewType(int position) { return position; }
There is problem in your onBindViewHolder(...), should be: if ("true".equals(notificationData.getReadStatus())) { holder.root.setBackgroundColor(mContext.getResources().getColor(R.color.white)); holder.notificationStatus.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL)); } else { holder.root.setBackgroundColor(yourDefaultColor); holder.notificationStatus.setTypeface(yourDefaultTypeface); }
#Override public void onBindViewHolder(final MyViewHolder holder, int position) { final UserData userdata = userdataList.get(position); holder.setIsRecyclable(false); holder.name.setText(userdata.getName()); holder.active.setChecked(userdata.getActive()); String userPic = userdata.getPic(); holder.active.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked){ userdata.setActive(isChecked); } }); }
onBindHolder called several times as Recycler View needs a view unless new one. So each time you set visilibity in child views, other views states are also changes. Whenever you scroll up and down, these views are getting re-drawed with wrong visibility options so always specify both the conditions cause recycler view doesn't know the previous state/conditions/values of our widgets. Solution : If in If block you set visibility of any android widget.setVisibility(View.Gone) then in else block you have to set it's visibility opposite value like widget.setVisibility(View.Visible) to overcome the above problem. #Override public void onBindViewHolder(ViewHolder viewHolder, int i) { viewHolder.tvName.setText(ModelCategoryProducts.name.get(i)); viewHolder.tvPrice.setText("Rs."+String.format("%.2f", Float.parseFloat(ModelCategoryProducts.price.get(i)))); if(ModelCategoryProducts.special_price.get(i).equals("null")) { viewHolder.tvSpecialPrice.setVisibility(View.GONE); // here visibility is gone and in else it's opposite visibility i set. viewHolder.tvPrice.setTextColor(Color.parseColor("#ff0000")); viewHolder.tvPrice.setPaintFlags(0);// here paint flag is 0 and in else it's opposite flag that i want is set. }else if(!ModelCategoryProducts.special_price.get(i).equals("null")){ viewHolder.tvPrice.setTextColor(Color.parseColor("#E0E0E0")); viewHolder.tvSpecialPrice.setVisibility(View.VISIBLE); viewHolder.tvSpecialPrice.setText("Rs." + String.format("%.2f", Float.parseFloat(ModelCategoryProducts.special_price.get(i)))); viewHolder.tvPrice.setPaintFlags(viewHolder.tvPrice.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } if (!ModelCategoryProducts.image_url.get(i).isEmpty()) { Picasso.with(context) .load(ModelCategoryProducts.image_url.get(i)) .into(viewHolder.ivProduct); } viewHolder.setClickListener(new ItemClickListener() { #Override public void onClick(View view, int position, boolean isLongClick) { if (isLongClick) { // Toast.makeText(context, "#" + position + " - " + ModelCategoryProducts.name.get(position) + " (Long click)", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, "#" + position + " - " + ModelCategoryProducts.name.get(position), Toast.LENGTH_SHORT).show(); Intent i = new Intent(context, ProductDetail.class); i.putExtra("position",position); i.putExtra("flagHlvCheck", 5); context.startActivity(i); } } }); }
Try adding this in the adapter. #Override public int getItemViewType(int position) { return position; }
If someone might face issues with some of the fields in the viewholder getting random values, then try to set all the fields with atleast any default value.
#Override public DataObjectHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.custom_layout, parent, false); DataObjectHolder dataObjectHolder = new DataObjectHolder(view); dataObjectHolder.setIsRecyclable(false); return dataObjectHolder; }
The best way is indicate an ArrayList for example as a Model and have some parameters and define setter and getter for that. package com.test.mohammaddvi.snappfood.Model; public class OfferList { private boolean visibilityOrder; private int number; public OfferList(int number, boolean visibilityOrder) { this.number=number; this.visibilityOrder=visibilityOrder; } public boolean isVisibilityOrder() { return visibilityOrder; } public void setVisibilityOrder(boolean visibilityOrder) { this.visibilityOrder = visibilityOrder; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } } and set the the variables as where you want and for get you must do it in onBindViewHolder of your recyclerview Adapter: if (offerList.isVisibilityOrder()) { holder.foodMinusButton.setVisibility(View.VISIBLE); holder.foodOrderNumber.setText(offerList.getNumber() + ""); holder.foodOrderNumber.setVisibility(View.VISIBLE); } else { holder.foodMinusButton.setVisibility(View.INVISIBLE); } and indicate it your recyclerview adapter: public class RecyclerViewMenuFragmentAdapter extends RecyclerView.Adapter<RecyclerViewMenuFragmentAdapter.SingleItemInMenuFragment> { private ArrayList<Food> foodList; private Context mContext; private List<OfferList> offers; public RecyclerViewMenuFragmentAdapter(ArrayList<Food> foodList, Context mContext, List<OfferList> offers) { this.foodList = foodList; this.mContext = mContext; this.offers = offers; }
class AnyRVAdapter: androidx.recyclerview.widget.RecyclerView.Adapter<AnyRVAdapter.MViewHolder>() { // put saver outside viewholder val saveLayId = mutableListOf<Int>() inner class MViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) { fun bindModel(d: TesListModel.MList, position:Int) { // concept here val showedId= saveLayId.find { s -> s == layoutPosition} if (idClicked == null) { // save the layout id lyClicked.visibility = View.VISIBLE saveLayId.add(layoutPosition) } else { // remove the layout id lyClicked.visibility = View.INVISIBLE saveLayId.remove(layoutPosition) } } } but i think this code is heavy if you use for large data set.
Guys this has worked for me.. override fun setHasStableIds(hasStableIds: Boolean) { setHasStableIds(true) } override fun getItemId(position: Int): Long { return position.toLong() } override fun getItemViewType(position: Int): Int { return position }
Why doesn't RecyclerView have onItemClickListener()?
I was exploring RecyclerView and I was surprised to see that RecyclerView does not have onItemClickListener(). I've two question. Main Question I want to know why Google removed onItemClickListener()? Is there a performance issue or something else? Secondary Question I solved my problem by writing onClick in my RecyclerView.Adapter: public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener { public TextView txtViewTitle; public ImageView imgViewIcon; public ViewHolder(View itemLayoutView) { super(itemLayoutView); txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title); imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon); } #Override public void onClick(View v) { } } Is this ok / is there any better way?
tl;dr 2016 Use RxJava and a PublishSubject to expose an Observable for the clicks. public class ReactiveAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { String[] mDataset = { "Data", "In", "Adapter" }; private final PublishSubject<String> onClickSubject = PublishSubject.create(); #Override public void onBindViewHolder(final ViewHolder holder, int position) { final String element = mDataset[position]; holder.itemView.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { onClickSubject.onNext(element); } }); } public Observable<String> getPositionClicks(){ return onClickSubject.asObservable(); } } Original Post: Since the introduction of ListView, onItemClickListener has been problematic. The moment you have a click listener for any of the internal elements the callback would not be triggered but it wasn't notified or well documented (if at all) so there was a lot of confusion and SO questions about it. Given that RecyclerView takes it a step further and doesn't have a concept of a row/column, but rather an arbitrarily laid out amount of children, they have delegated the onClick to each one of them, or to programmer implementation. Think of Recyclerview not as a ListView 1:1 replacement but rather as a more flexible component for complex use cases. And as you say, your solution is what google expected of you. Now you have an adapter who can delegate onClick to an interface passed on the constructor, which is the correct pattern for both ListView and Recyclerview. public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener { public TextView txtViewTitle; public ImageView imgViewIcon; public IMyViewHolderClicks mListener; public ViewHolder(View itemLayoutView, IMyViewHolderClicks listener) { super(itemLayoutView); mListener = listener; txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title); imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon); imgViewIcon.setOnClickListener(this); itemLayoutView.setOnClickListener(this); } #Override public void onClick(View v) { if (v instanceof ImageView){ mListener.onTomato((ImageView)v); } else { mListener.onPotato(v); } } public static interface IMyViewHolderClicks { public void onPotato(View caller); public void onTomato(ImageView callerImage); } } and then on your adapter public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { String[] mDataset = { "Data" }; #Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_layout, parent, false); MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() { public void onPotato(View caller) { Log.d("VEGETABLES", "Poh-tah-tos"); }; public void onTomato(ImageView callerImage) { Log.d("VEGETABLES", "To-m8-tohs"); } }); return vh; } // Replace the contents of a view (invoked by the layout manager) #Override public void onBindViewHolder(ViewHolder holder, int position) { // Get element from your dataset at this position // Replace the contents of the view with that element // Clear the ones that won't be used holder.txtViewTitle.setText(mDataset[position]); } // Return the size of your dataset (invoked by the layout manager) #Override public int getItemCount() { return mDataset.length; } ... Now look into that last piece of code: onCreateViewHolder(ViewGroup parent, int viewType) the signature already suggest different view types. For each one of them you'll require a different viewholder too, and subsequently each one of them can have a different set of clicks. Or you can just create a generic viewholder that takes any view and one onClickListener and applies accordingly. Or delegate up one level to the orchestrator so several fragments/activities have the same list with different click behaviour. Again, all flexibility is on your side. It is a really needed component and fairly close to what our internal implementations and improvements to ListView were until now. It's good that Google finally acknowledges it.
Why the RecyclerView has no onItemClickListener The RecyclerView is a toolbox, in contrast of the old ListView it has less build in features and more flexibility. The onItemClickListener is not the only feature being removed from ListView. But it has lot of listeners and method to extend it to your liking, it's far more powerful in the right hands ;). In my opinion the most complex feature removed in RecyclerView is the Fast Scroll. Most of the other features can be easily re-implemented. If you want to know what other cool features RecyclerView added read this answer to another question. Memory efficient - drop-in solution for onItemClickListener This solution has been proposed by Hugo Visser, an Android GDE, right after RecyclerView was released. He made a licence-free class available for you to just drop in your code and use it. It showcase some of the versatility introduced with RecyclerView by making use of RecyclerView.OnChildAttachStateChangeListener. Edit 2019: kotlin version by me, java one, from Hugo Visser, kept below Kotlin / Java Create a file values/ids.xml and put this in it: <?xml version="1.0" encoding="utf-8"?> <resources> <item name="item_click_support" type="id" /> </resources> then add the code below to your source Kotlin Usage: recyclerView.onItemClick { recyclerView, position, v -> // do it } (it also support long item click and see below for another feature I've added). implementation (my adaptation to Hugo Visser Java code): typealias OnRecyclerViewItemClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Unit typealias OnRecyclerViewItemLongClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Boolean class ItemClickSupport private constructor(private val recyclerView: RecyclerView) { private var onItemClickListener: OnRecyclerViewItemClickListener? = null private var onItemLongClickListener: OnRecyclerViewItemLongClickListener? = null private val attachListener: RecyclerView.OnChildAttachStateChangeListener = object : RecyclerView.OnChildAttachStateChangeListener { override fun onChildViewAttachedToWindow(view: View) { // every time a new child view is attached add click listeners to it val holder = this#ItemClickSupport.recyclerView.getChildViewHolder(view) .takeIf { it is ItemClickSupportViewHolder } as? ItemClickSupportViewHolder if (onItemClickListener != null && holder?.isClickable != false) { view.setOnClickListener(onClickListener) } if (onItemLongClickListener != null && holder?.isLongClickable != false) { view.setOnLongClickListener(onLongClickListener) } } override fun onChildViewDetachedFromWindow(view: View) { } } init { // the ID must be declared in XML, used to avoid // replacing the ItemClickSupport without removing // the old one from the RecyclerView this.recyclerView.setTag(R.id.item_click_support, this) this.recyclerView.addOnChildAttachStateChangeListener(attachListener) } companion object { fun addTo(view: RecyclerView): ItemClickSupport { // if there's already an ItemClickSupport attached // to this RecyclerView do not replace it, use it var support: ItemClickSupport? = view.getTag(R.id.item_click_support) as? ItemClickSupport if (support == null) { support = ItemClickSupport(view) } return support } fun removeFrom(view: RecyclerView): ItemClickSupport? { val support = view.getTag(R.id.item_click_support) as? ItemClickSupport support?.detach(view) return support } } private val onClickListener = View.OnClickListener { v -> val listener = onItemClickListener ?: return#OnClickListener // ask the RecyclerView for the viewHolder of this view. // then use it to get the position for the adapter val holder = this.recyclerView.getChildViewHolder(v) listener.invoke(this.recyclerView, holder.adapterPosition, v) } private val onLongClickListener = View.OnLongClickListener { v -> val listener = onItemLongClickListener ?: return#OnLongClickListener false val holder = this.recyclerView.getChildViewHolder(v) return#OnLongClickListener listener.invoke(this.recyclerView, holder.adapterPosition, v) } private fun detach(view: RecyclerView) { view.removeOnChildAttachStateChangeListener(attachListener) view.setTag(R.id.item_click_support, null) } fun onItemClick(listener: OnRecyclerViewItemClickListener?): ItemClickSupport { onItemClickListener = listener return this } fun onItemLongClick(listener: OnRecyclerViewItemLongClickListener?): ItemClickSupport { onItemLongClickListener = listener return this } } /** Give click-ability and long-click-ability control to the ViewHolder */ interface ItemClickSupportViewHolder { val isClickable: Boolean get() = true val isLongClickable: Boolean get() = true } // Extension function fun RecyclerView.addItemClickSupport(configuration: ItemClickSupport.() -> Unit = {}) = ItemClickSupport.addTo(this).apply(configuration) fun RecyclerView.removeItemClickSupport() = ItemClickSupport.removeFrom(this) fun RecyclerView.onItemClick(onClick: OnRecyclerViewItemClickListener) { addItemClickSupport { onItemClick(onClick) } } fun RecyclerView.onItemLongClick(onLongClick: OnRecyclerViewItemLongClickListener) { addItemClickSupport { onItemLongClick(onLongClick) } } (Remember you also need to add an XML file, see above this section) Bonus feature of Kotlin version Sometimes you do not want all the items of the RecyclerView to be clickable. To handle this I've introduced the ItemClickSupportViewHolder interface that you can use on your ViewHolder to control which item is clickable. Example: class MyViewHolder(view): RecyclerView.ViewHolder(view), ItemClickSupportViewHolder { override val isClickable: Boolean get() = false override val isLongClickable: Boolean get() = false } Java Usage: ItemClickSupport.addTo(mRecyclerView) .setOnItemClickListener(new ItemClickSupport.OnItemClickListener() { #Override public void onItemClicked(RecyclerView recyclerView, int position, View v) { // do it } }); (it also support long item click) Implementation (comments added by me): public class ItemClickSupport { private final RecyclerView mRecyclerView; private OnItemClickListener mOnItemClickListener; private OnItemLongClickListener mOnItemLongClickListener; private View.OnClickListener mOnClickListener = new View.OnClickListener() { #Override public void onClick(View v) { if (mOnItemClickListener != null) { // ask the RecyclerView for the viewHolder of this view. // then use it to get the position for the adapter RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v); mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v); } } }; private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() { #Override public boolean onLongClick(View v) { if (mOnItemLongClickListener != null) { RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v); return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v); } return false; } }; private RecyclerView.OnChildAttachStateChangeListener mAttachListener = new RecyclerView.OnChildAttachStateChangeListener() { #Override public void onChildViewAttachedToWindow(View view) { // every time a new child view is attached add click listeners to it if (mOnItemClickListener != null) { view.setOnClickListener(mOnClickListener); } if (mOnItemLongClickListener != null) { view.setOnLongClickListener(mOnLongClickListener); } } #Override public void onChildViewDetachedFromWindow(View view) { } }; private ItemClickSupport(RecyclerView recyclerView) { mRecyclerView = recyclerView; // the ID must be declared in XML, used to avoid // replacing the ItemClickSupport without removing // the old one from the RecyclerView mRecyclerView.setTag(R.id.item_click_support, this); mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener); } public static ItemClickSupport addTo(RecyclerView view) { // if there's already an ItemClickSupport attached // to this RecyclerView do not replace it, use it ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support); if (support == null) { support = new ItemClickSupport(view); } return support; } public static ItemClickSupport removeFrom(RecyclerView view) { ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support); if (support != null) { support.detach(view); } return support; } public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) { mOnItemClickListener = listener; return this; } public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) { mOnItemLongClickListener = listener; return this; } private void detach(RecyclerView view) { view.removeOnChildAttachStateChangeListener(mAttachListener); view.setTag(R.id.item_click_support, null); } public interface OnItemClickListener { void onItemClicked(RecyclerView recyclerView, int position, View v); } public interface OnItemLongClickListener { boolean onItemLongClicked(RecyclerView recyclerView, int position, View v); } } How it works (why it's efficient) This class works by attaching a RecyclerView.OnChildAttachStateChangeListener to the RecyclerView. This listener is notified every time a child is attached or detached from the RecyclerView. The code use this to append a tap/long click listener to the view. That listener ask the RecyclerView for the RecyclerView.ViewHolder which contains the position. This is more efficient then other solutions because it avoid creating multiple listeners for each view and keep destroying and creating them while the RecyclerView is being scrolled. You could also adapt the code to give you back the holder itself if you need more. Final remark Keep in mind that it's COMPLETELY fine to handle it in your adapter by setting on each view of your list a click listener, like other answer proposed. It's just not the most efficient thing to do (you create a new listener every time you reuse a view) but it works and in most cases it's not an issue. It is also a bit against separation of concerns cause it's not really the Job of the Adapter to delegate click events.
I like this way and I'm using it Inside public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) Put View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_image_and_text, parent, false); v.setOnClickListener(new MyOnClickListener()); And create this class anywhere you want it class MyOnClickListener implements View.OnClickListener { #Override public void onClick(View v) { int itemPosition = recyclerView.indexOfChild(v); Log.e("Clicked and Position is ",String.valueOf(itemPosition)); } } I've read before that there is a better way but I like this way is easy and not complicated.
Android Recyclerview With onItemClickListener, Why we cant try this is working like ListView only. Source : Link public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener { private OnItemClickListener mListener; public interface OnItemClickListener { public void onItemClick(View view, int position); } GestureDetector mGestureDetector; public RecyclerItemClickListener(Context context, OnItemClickListener listener) { mListener = listener; mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { #Override public boolean onSingleTapUp(MotionEvent e) { return true; } }); } #Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { View childView = view.findChildViewUnder(e.getX(), e.getY()); if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) { mListener.onItemClick(childView, view.getChildAdapterPosition(childView)); } return false; } #Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { } #Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } } And Set this to RecyclerView: recyclerView = (RecyclerView)rootView. findViewById(R.id.recyclerView); RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity()); recyclerView.setLayoutManager(mLayoutManager); recyclerView.addOnItemTouchListener( new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() { #Override public void onItemClick(View view, int position) { // TODO Handle item click Log.e("#####",""+position); } }) );
Thanks to #marmor, I updated my answer. I think it's a good solution to handle the onClick() in the ViewHolder class constructor and pass it to the parent class via OnItemClickListener interface. MyAdapter.java public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{ private LayoutInflater layoutInflater; private List<MyObject> items; private AdapterView.OnItemClickListener onItemClickListener; public MyAdapter(Context context, AdapterView.OnItemClickListener onItemClickListener, List<MyObject> items) { layoutInflater = LayoutInflater.from(context); this.items = items; this.onItemClickListener = onItemClickListener; } #Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = layoutInflater.inflate(R.layout.my_row_layout, parent, false); return new ViewHolder(view); } #Override public void onBindViewHolder(ViewHolder holder, int position) { MyObject item = items.get(position); } public MyObject getItem(int position) { return items.get(position); } class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { private TextView title; private ImageView avatar; public ViewHolder(View itemView) { super(itemView); title = itemView.findViewById(R.id.title); avatar = itemView.findViewById(R.id.avatar); title.setOnClickListener(this); avatar.setOnClickListener(this); itemView.setOnClickListener(this); } #Override public void onClick(View view) { //passing the clicked position to the parent class onItemClickListener.onItemClick(null, view, getAdapterPosition(), view.getId()); } } } Usage of adapter in other classes: MyFragment.java public class MyFragment extends Fragment implements AdapterView.OnItemClickListener { private RecyclerView recycleview; private MyAdapter adapter; . . . private void init(Context context) { //passing this fragment as OnItemClickListener to the adapter adapter = new MyAdapter(context, this, items); recycleview.setAdapter(adapter); } #Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //you can get the clicked item from the adapter using its position MyObject item = adapter.getItem(position); //you can also find out which view was clicked switch (view.getId()) { case R.id.title: //title view was clicked break; case R.id.avatar: //avatar view was clicked break; default: //the whole row was clicked } } }
> How RecyclerView is different from Listview? One difference is that there is LayoutManager class with RecyclerView by which you can manage your RecyclerView like- Horizontal or Vertical scrolling by LinearLayoutManager GridLayout by GridLayoutManager Staggered GridLayout by StaggeredGridLayoutManager Like for horizontal scrolling for RecyclerView- LinearLayoutManager llm = new LinearLayoutManager(context); llm.setOrientation(LinearLayoutManager.HORIZONTAL); recyclerView.setLayoutManager(llm);
Guys use this code in Your main activity. Very Efficient Method RecyclerView recyclerView = (RecyclerView) findViewById(R.id.users_list); UsersAdapter adapter = new UsersAdapter(users, this); recyclerView.setAdapter(adapter); adapter.setOnCardClickListner(this); Here is your Adapter class. public class UsersAdapter extends RecyclerView.Adapter<UsersAdapter.UserViewHolder> { private ArrayList<User> mDataSet; OnCardClickListner onCardClickListner; public UsersAdapter(ArrayList<User> mDataSet) { this.mDataSet = mDataSet; } #Override public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_row_layout, parent, false); UserViewHolder userViewHolder = new UserViewHolder(v); return userViewHolder; } #Override public void onBindViewHolder(UserViewHolder holder, final int position) { holder.name_entry.setText(mDataSet.get(position).getUser_name()); holder.cardView.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { onCardClickListner.OnCardClicked(v, position); } }); } #Override public int getItemCount() { return mDataSet.size(); } #Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); } public static class UserViewHolder extends RecyclerView.ViewHolder { CardView cardView; TextView name_entry; public UserViewHolder(View itemView) { super(itemView); cardView = (CardView) itemView.findViewById(R.id.user_layout); name_entry = (TextView) itemView.findViewById(R.id.name_entry); } } public interface OnCardClickListner { void OnCardClicked(View view, int position); } public void setOnCardClickListner(OnCardClickListner onCardClickListner) { this.onCardClickListner = onCardClickListner; } } After this you will get this override method in your activity. #Override public void OnCardClicked(View view, int position) { Log.d("OnClick", "Card Position" + position); }
How to put it all together example... onClick() handling Cursor - RecyclerView ViewHolder types public class OrderListCursorAdapter extends CursorRecyclerViewAdapter<OrderListCursorAdapter.ViewHolder> { private static final String TAG = OrderListCursorAdapter.class.getSimpleName(); private static final int ID_VIEW_HOLDER_ACTUAL = 0; private static final int ID_VIEW_HOLDER = 1; public OrderListCursorAdapter(Context context, Cursor cursor) { super(context, cursor); } public static class ViewHolderActual extends ViewHolder { private static final String TAG = ViewHolderActual.class.getSimpleName(); protected IViewHolderClick listener; protected Button button; public ViewHolderActual(View v, IViewHolderClick listener) { super(v, listener); this.listener = listener; button = (Button) v.findViewById(R.id.orderList_item_button); button.setOnClickListener(this); } public void initFromData(OrderData data) { Log.d(TAG, "><initFromData(data=" + data + ")"); orderId = data.getId(); vAddressStart.setText(data.getAddressStart()); vAddressEnd.setText(data.getAddressEnd()); } #Override public void onClick(View view) { if (view instanceof Button) { listener.onButtonClick((Button) view, getPosition(), this); } else { super.onClick(view); } } public interface IViewHolderClick extends ViewHolder.IViewHolderClick { public void onButtonClick(Button button, int position, ViewHolder viewHolder); } } public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { private static final String TAG = ViewHolder.class.getSimpleName(); protected long orderId; protected IViewHolderClick listener; protected TextView vAddressStart; protected TextView vAddressEnd; protected TextView vStatus; public ViewHolder(View v, IViewHolderClick listener) { super(v); this.listener = listener; v.setOnClickListener(this); vAddressStart = (TextView) v.findViewById(R.id.addressStart); vAddressEnd = (TextView) v.findViewById(R.id.addressEnd); vStatus = (TextView) v.findViewById(R.id.status); } public void initFromData(OrderData data) { Log.d(TAG, "><initFromData(data=" + data + ")"); orderId = data.getId(); vAddressStart.setText(data.getAddressStart()); vAddressEnd.setText(data.getAddressEnd()); } public long getOrderId() { return orderId; } #Override public void onClick(View view) { listener.onCardClick(view, getPosition(), this); } public interface IViewHolderClick { public void onCardClick(View view, int position, ViewHolder viewHolder); } } #Override public int getItemViewType(int position) { return position == 0 ? ID_VIEW_HOLDER_ACTUAL : ID_VIEW_HOLDER; } #Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { Log.d(TAG, ">>onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")"); ViewHolder result; switch (viewType) { case ID_VIEW_HOLDER_ACTUAL: { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout_actual, parent, false); result = new ViewHolderActual(itemView, new ViewHolderActual.IViewHolderClick() { #Override public void onCardClick(View view, int position, ViewHolder viewHolder) { Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")"); Intent intent = new Intent(view.getContext(), OrderDetailActivity.class); intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId()); view.getContext().startActivity(intent); } #Override public void onButtonClick(Button button, int position, ViewHolder viewHolder) { Log.d(TAG, "><onButtonClick(button=" + button + ", position=" + position + ", viewHolder=" + viewHolder + ")"); Intent intent = new Intent(button.getContext(), OrderMapActivity.class); intent.putExtra(OrderMapActivity.ARG_ORDER_ID, viewHolder.getOrderId()); button.getContext().startActivity(intent); } }); break; } case ID_VIEW_HOLDER: default: { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout, parent, false); result = new ViewHolder(itemView, new ViewHolder.IViewHolderClick() { #Override public void onCardClick(View view, int position, ViewHolder viewHolder) { Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")"); Intent intent = new Intent(view.getContext(), OrderDetailActivity.class); intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId()); view.getContext().startActivity(intent); } }); break; } } Log.d(TAG, "<<onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")= " + result); return result; } #Override public void onBindViewHolder(ViewHolder viewHolder, Cursor cursor) { Log.d(TAG, "><onBindViewHolder(viewHolder=" + viewHolder + ", cursor=" + cursor + ")"); final OrderData orderData = new OrderData(cursor); viewHolder.initFromData(orderData); } }
Following up MLProgrammer-CiM's excellent RxJava solution Consume / Observe clicks Consumer<String> mClickConsumer = new Consumer<String>() { #Override public void accept(#NonNull String element) throws Exception { Toast.makeText(getApplicationContext(), element +" was clicked", Toast.LENGTH_LONG).show(); } }; ReactiveAdapter rxAdapter = new ReactiveAdapter(); rxAdapter.getPositionClicks().subscribe(mClickConsumer); RxJava 2.+ Modify the original tl;dr as: public Observable<String> getPositionClicks(){ return onClickSubject; } PublishSubject#asObservable() was removed. Just return the PublishSubject which is an Observable.
RecyclerView doesn't have an onItemClickListener because RecyclerView is responsible for recycling views (surprise!), so it's the responsibility of the view that is recycled to handle the click events it receives. This actually makes it much easier to use, especially if you had items that can be clicked in multiple places. Anyways, detecting click on a RecyclerView item is very easy. All you need to do is define an interface (if you're not using Kotlin, in which case you just pass in a lambda): public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> { private final Clicks clicks; public MyAdapter(Clicks clicks) { this.clicks = clicks; } private List<MyObject> items = Collections.emptyList(); public void updateData(List<MyObject> items) { this.items = items; notifyDataSetChanged(); // TODO: use ListAdapter for diffing instead if you need animations } public interface Clicks { void onItemSelected(MyObject myObject, int position); } public class MyViewHolder extends RecyclerView.ViewHolder { private MyObject myObject; public MyViewHolder(View view) { super(view); // bind views view.setOnClickListener((v) -> { int adapterPosition = getBindingAdapterPosition(); if(adapterPosition >= 0) { clicks.onItemSelected(myObject, adapterPosition); } }); } public void bind(MyObject myObject) { this.myObject = myObject; // bind data to views } } } Same code in Kotlin: class MyAdapter(val itemClicks: (MyObject, Int) -> Unit): RecyclerView.Adapter<MyViewHolder>() { private var items: List<MyObject> = Collections.emptyList() fun updateData(items: List<MyObject>) { this.items = items notifyDataSetChanged() // TODO: use ListAdapter for diffing instead if you need animations } inner class MyViewHolder(val myView: View): RecyclerView.ViewHolder(myView) { private lateinit var myObject: MyObject init { // binds views myView.onClick { val adapterPosition = getBindingAdapterPosition() if(adapterPosition >= 0) { itemClicks.invoke(myObject, adapterPosition) } } } fun bind(myObject: MyObject) { this.myObject = myObject // bind data to views } } } Thing you DON'T need to do: 1.) you don't need to intercept touch events manually 2.) you don't need to mess around with child attach state change listeners 3.) you don't need PublishSubject/PublishRelay from RxJava Just use a click listener.
As far as I understand MLProgrammer-CiM answer, simply it's possible to just do this: class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ private ImageView image; private TextView title; private TextView price; public MyViewHolder(View itemView) { super(itemView); image = (ImageView)itemView.findViewById(R.id.horizontal_list_image); title = (TextView)itemView.findViewById(R.id.horizontal_list_title); price = (TextView)itemView.findViewById(R.id.horizontal_list_price); image.setOnClickListener(this); title.setOnClickListener(this); price.setOnClickListener(this); } #Override public void onClick(View v) { Toast.makeText(context, "Item click nr: "+getLayoutPosition(), Toast.LENGTH_SHORT).show(); } }
After reading #MLProgrammer-CiM's answer, here is my code: class NormalViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ #Bind(R.id.card_item_normal) CardView cardView; public NormalViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); cardView.setOnClickListener(this); } #Override public void onClick(View v) { if(v instanceof CardView) { // use getAdapterPosition() instead of getLayoutPosition() int itemPosition = getAdapterPosition(); removeItem(itemPosition); } } }
I have done this way, its very simple: Just add 1 Line for Clicked RecyclerView position: int position = getLayoutPosition() Full code for ViewHolder class: private class ChildViewHolder extends RecyclerView.ViewHolder { public ImageView imageView; public TextView txtView; public ChildViewHolder(View itemView) { super(itemView); imageView= (ImageView)itemView.findViewById(R.id.imageView); txtView= (TextView) itemView.findViewById(R.id.txtView); itemView.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View view) { Log.i("RecyclerView Item Click Position", String.valueOf(getLayoutPosition())); } }); } } Hope this will help you.
I use this method to start an Intent from RecyclerView: #Override public void onBindViewHolder(ViewHolder viewHolder, int i) { final MyClass myClass = mList.get(i); viewHolder.txtViewTitle.setText(myclass.name); ... viewHolder.itemView.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View v){ Intent detailIntent = new Intent(mContext, type.class); detailIntent.putExtra("MyClass", myclass); mContext.startActivity(detailIntent); } } );
See my approach on this: First declare an interface like this: /** * Interface used for delegating item click events in a {#link android.support.v7.widget.RecyclerView} * Created by Alex on 11/28/2015. */ public interface OnRecyclerItemClickListener<T> { /** * Called when a click occurred inside a recyclerView item view * #param view that was clicked * #param position of the clicked view * #param item the concrete data that is displayed through the clicked view */ void onItemClick(View view, int position, T item); } Then create the adapter: public class CustomRecyclerAdapter extends RecyclerView.Adapter { private class InternalClickListener implements View.OnClickListener{ #Override public void onClick(View v) { if(mRecyclerView != null && mItemClickListener != null){ // find the position of the item that was clicked int position = mRecyclerView.getChildAdapterPosition(v); Data data = getItem(position); // notify the main listener mItemClickListener.onItemClick(v, position, data); } } } private final OnRecyclerItemClickListener mItemClickListener; private RecyclerView mRecyclerView; private InternalClickListener mInternalClickListener; /** * * #param itemClickListener used to trigger an item click event */ public PlayerListRecyclerAdapter(OnRecyclerItemClickListener itemClickListener){ mItemClickListener = itemClickListener; mInternalClickListener = new InternalClickListener(); } #Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false); v.setOnClickListener(mInternalClickListener); ViewHolder viewHolder = new ViewHolder(v); return viewHolder; } #Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { // do your binding here } #Override public int getItemCount() { return mDataSet.size(); } #Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); mRecyclerView = recyclerView; } #Override public void onDetachedFromRecyclerView(RecyclerView recyclerView) { super.onDetachedFromRecyclerView(recyclerView); mRecyclerView = null; } public Data getItem(int position){ return mDataset.get(position); } } And now let's see how to integrate this from a fragment: public class TestFragment extends Fragment implements OnRecyclerItemClickListener<Data>{ private RecyclerView mRecyclerView; #Override public void onItemClick(View view, int position, Data item) { // do something } #Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.test_fragment, container, false); } #Override public void onViewCreated(View view, Bundle savedInstanceState) { mRecyclerView = view.findViewById(idOfTheRecycler); mRecyclerView .setAdapter(new CustomRecyclerAdapter(this)); }
If you want to add onClick() to the child view of items, for example, a button in item, I found that you can do it easily in onCreateViewHolder() of your own RecyclerView.Adapter just like this: #Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater .from(parent.getContext()) .inflate(R.layout.cell, null); Button btn = (Button) v.findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { //do it } }); return new MyViewHolder(v); } i don't know whether it's a good way, but it works well. If anyone has a better idea, very glad to tell me and correct my answer! :)
Here is a way to implement it quite easily if you have a list of POJOs and want to retrieve one on click from outside the adapter. In your adapter, create a listener for the click events and a method to set it: public class MyAdapter extends RecyclerView.Adapter<SitesListAdapter.ViewHolder> { ... private List<MyPojo> mMyPojos; private static OnItemClickListener mOnItemClickListener; ... public interface OnItemClickListener { public void onItemClick(MyPojo pojo); } ... public void setOnItemClickListener(OnItemClickListener onItemClickListener){ mOnItemClickListener = onItemClickListener; } ... } In your ViewHolder, implement onClickListener and create a class member to temporarily store the POJO the view is presenting, that way (this is an example, creating a setter would be better): public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public MyPojo mCurrentPojo; ... public ViewHolder(View view) { super(v); ... view.setOnClickListener(this); //You could set this on part of the layout too } ... #Override public void onClick(View view) { if(mOnItemClickListener != null && mCurrentPojo != null){ mOnItemClickListener.onItemClick(mCurrentPojo); } } Back in your adapter, set the current POJO when the ViewHolder is bound (or to null if the current view doesn't have one): #Override public void onBindViewHolder(final ViewHolder holder, final int position) { final MyPojo currentPojo = mMyPojos.get(position); holder.mCurrentPojo = currentPojo; ... That's it, now you can use it like this from your fragment/activity: mMyAdapter.setOnItemClickListener(new mMyAdapter.OnItemClickListener() { #Override public void onItemClick(MyPojo pojo) { //Do whatever you want with your pojo here } });
Yes you can public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) { //inflate the view View view = LayoutInflator.from(parent.getContext()).inflate(R.layout.layoutID,null); ViewHolder holder = new ViewHolder(view); //here we can set onClicklistener view.setOnClickListener(new View.OnClickListeener(){ public void onClick(View v) { //action } }); return holder;
This worked for me: #Override public void onBindViewHolder(PlacesListViewAdapter.ViewHolder holder, int position) { ---- ---- ---- // Set setOnClickListener(holder); } #Override public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { ---- ---- ---- #Override public void onClick(View view) { // Use to get the item clicked getAdapterPosition() } }
Here you can handle multiple onclick see below code and it is very efficient public class RVNewsAdapter extends RecyclerView.Adapter<RVNewsAdapter.FeedHolder> { private Context context; List<News> newsList; // Allows to remember the last item shown on screen private int lastPosition = -1; public RVNewsAdapter(List<News> newsList, Context context) { this.newsList = newsList; this.context = context; } public static class FeedHolder extends RecyclerView.ViewHolder implements OnClickListener { ImageView img_main; TextView tv_title; Button bt_facebook, bt_twitter, bt_share, bt_comment; public FeedHolder(View itemView) { super(itemView); img_main = (ImageView) itemView.findViewById(R.id.img_main); tv_title = (TextView) itemView.findViewById(R.id.tv_title); bt_facebook = (Button) itemView.findViewById(R.id.bt_facebook); bt_twitter = (Button) itemView.findViewById(R.id.bt_twitter); bt_share = (Button) itemView.findViewById(R.id.bt_share); bt_comment = (Button) itemView.findViewById(R.id.bt_comment); img_main.setOnClickListener(this); bt_facebook.setOnClickListener(this); bt_twitter.setOnClickListener(this); bt_comment.setOnClickListener(this); bt_share.setOnClickListener(this); } #Override public void onClick(View v) { if (v.getId() == bt_comment.getId()) { Toast.makeText(v.getContext(), "Comment " , Toast.LENGTH_SHORT).show(); } else if (v.getId() == bt_facebook.getId()) { Toast.makeText(v.getContext(), "Facebook " , Toast.LENGTH_SHORT).show(); } else if (v.getId() == bt_twitter.getId()) { Toast.makeText(v.getContext(), "Twitter " , Toast.LENGTH_SHORT).show(); } else if (v.getId() == bt_share.getId()) { Toast.makeText(v.getContext(), "share " , Toast.LENGTH_SHORT).show(); } else { Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show(); } } } #Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); } #Override public FeedHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.feed_row, parent, false); FeedHolder feedHolder = new FeedHolder(view); return feedHolder; } #Override public void onBindViewHolder(FeedHolder holder, int position) { holder.tv_title.setText(newsList.get(position).getTitle()); // Here you apply the animation when the view is bound setAnimation(holder.img_main, position); } #Override public int getItemCount() { return newsList.size(); } /** * Here is the key method to apply the animation */ private void setAnimation(View viewToAnimate, int position) { // If the bound view wasn't previously displayed on screen, it's animated if (position > lastPosition) { Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left); viewToAnimate.startAnimation(animation); lastPosition = position; } } }
Modified my comment... public class MyViewHolder extends RecyclerView.ViewHolder { private Context mContext; public MyViewHolder(View itemView) { super(itemView); mContext = itemView.getContext(); itemView.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View view) { int itemPosition = getLayoutPosition(); Toast.makeText(mContext, "" + itemPosition, Toast.LENGTH_SHORT).show(); } }); }
Check this one in which I have implemented all the things with a proper way RecyclerViewHolder Class public class RecyclerViewHolder extends RecyclerView.ViewHolder { //view holder is for girdview as we used in the listView public ImageView imageView,imageView2; public RecyclerViewHolder(View itemView) { super(itemView); this.imageView=(ImageView)itemView.findViewById(R.id.image); } } Adapter public class RecyclerView_Adapter extends RecyclerView.Adapter<RecyclerViewHolder> { //RecyclerView will extend to recayclerview Adapter private ArrayList<ModelClass> arrayList; private Context context; private static RecyclerViewClickListener itemListener; //constructor of the RecyclerView Adapter RecyclerView_Adapter(Context context,ArrayList<ModelClass> arrayList,RecyclerViewClickListener itemListener){ this.context=context; this.arrayList=arrayList; this.itemListener=itemListener; } #Override public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //this method will inflate the custom layout and return as viewHolder LayoutInflater layoutInflater=LayoutInflater.from(parent.getContext()); ViewGroup mainGroup=(ViewGroup) layoutInflater.inflate(R.layout.single_item,parent,false); RecyclerViewHolder listHolder=new RecyclerViewHolder(mainGroup); return listHolder; } #Override public void onBindViewHolder(RecyclerViewHolder holder, final int position) { final ModelClass modelClass=arrayList.get(position); //holder RecyclerViewHolder mainHolder=(RecyclerViewHolder)holder; //convert the drawable image into bitmap Bitmap image= BitmapFactory.decodeResource(context.getResources(),modelClass.getImage()); //set the image into imageView mainHolder.imageView.setImageBitmap(image); //to handle on click event when clicked on the recyclerview item and // get it through the RecyclerViewHolder class we have defined the views there mainHolder.itemView.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { //get the position of the image which is clicked itemListener.recyclerViewListClicked(v,position); } }); } #Override public int getItemCount() { return (null!=arrayList?arrayList.size():0); } } The interface public interface RecyclerViewClickListener { //this is method to handle the event when clicked on the image in Recyclerview public void recyclerViewListClicked(View v,int position); } //and to call this method in activity RecyclerView_Adapter adapter=new RecyclerView_Adapter(Wallpaper.this,arrayList,this); recyclerView.setAdapter(adapter); adapter.notifyDataSetChanged(); #Override public void recyclerViewListClicked(View v,int position){ imageView.setImageResource(wallpaperImages[position]); }
Access the mainView of rowLayout(cell) for you RecyclerView and in your OnBindViewHolder write this code: #Override public void onBindViewHolder(MyViewHolder holder, final int position) { Movie movie = moviesList.get(position); holder.mainView.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View view) { System.out.println("pos " + position); } }); }
it worked for me. Hope it will help. Most simplest way. Inside View Holder class GeneralViewHolder extends RecyclerView.ViewHolder { View cachedView = null; public GeneralViewHolder(View itemView) { super(itemView); cachedView = itemView; } Inside OnBindViewHolder() #Override public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { final GeneralViewHolder generalViewHolder = (GeneralViewHolder) holder; generalViewHolder.cachedView.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { Toast.makeText(context, "item Clicked at "+position, Toast.LENGTH_SHORT).show(); } }); And let me know, do you have any question about this solution ?
main_recyclerview.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() { #Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { int position=rv.getChildAdapterPosition(rv.findChildViewUnder(e.getX(),e.getY())); switch (position) { case 0: { wifi(position); adapter2.notifyDataSetChanged(); } break; case 1: { sound(position); adapter2.notifyDataSetChanged(); } break; case 2: { bluetooth(position); adapter2.notifyDataSetChanged(); } break; } return true; } #Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { } #Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } });
Instead of implementing interface View.OnClickListener inside view holder or creating and interface and implementing interface in your activity.. I used this code for simple on OnClickListener implementation. public static class SimpleStringRecyclerViewAdapter extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> { // Your initializations goes here... private List<String> mValues; public static class ViewHolder extends RecyclerView.ViewHolder { //create a variable mView public final View mView; /*All your row widgets goes here public final ImageView mImageView; public final TextView mTextView;*/ public ViewHolder(View view) { super(view); //Initialize it here mView = view; /* your row widgets initializations goes here mImageView = (ImageView) view.findViewById(R.id.avatar); mTextView = (TextView) view.findViewById(android.R.id.text1);*/ } } public String getValueAt(int position) { return mValues.get(position); } public SimpleStringRecyclerViewAdapter(Context context, List<String> items) { mBackground = mTypedValue.resourceId; mValues = items; } #Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.list_item, parent, false); view.setBackgroundResource(mBackground); return new ViewHolder(view); } #Override public void onBindViewHolder(final ViewHolder holder, int position) { holder.mBoundString = mValues.get(position); holder.mTextView.setText(mValues.get(position)); //Here it is simply write onItemClick listener here holder.mView.setOnClickListener(new View.OnClickListener() { #Override public void onClick(View v) { Context context = v.getContext(); Intent intent = new Intent(context, ExampleActivity.class); context.startActivity(intent); } }); } #Override public int getItemCount() { return mValues.size(); } }
use PlaceHolderView #Layout(R.layout.item_view_1) public class View1{ #View(R.id.txt) public TextView txt; #Resolve public void onResolved() { txt.setText(String.valueOf(System.currentTimeMillis() / 1000)); } #Click(R.id.btn) public void onClick(){ txt.setText(String.valueOf(System.currentTimeMillis() / 1000)); } }
I wrote a library to handle android recycler view item click event. You can find whole tutorial in https://github.com/ChathuraHettiarachchi/RecycleClick RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemClickListener(new RecycleClick.OnItemClickListener() { #Override public void onItemClicked(RecyclerView recyclerView, int position, View v) { // YOUR CODE } }); or to handle item long press you can use RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemLongClickListener(new RecycleClick.OnItemLongClickListener() { #Override public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) { // YOUR CODE return true; } });
recyclerview animation has not been tested, the other is normal. I think it has been optimized to the maximum. Interface has other uses, you can temporarily ignore. public abstract class BaseAdapterRV<VH extends BaseViewHolder> extends RecyclerView.Adapter<VH> implements AdapterInterface { public final String TAG = getClass().getSimpleName(); protected final Activity mActivity; protected final LayoutInflater mInflater; protected ItemClickInterface<?, Integer> mListener; public BaseAdapterRV(Activity activity) { mActivity = activity; mInflater = LayoutInflater.from(mActivity); } #Override public final VH onCreateViewHolder(ViewGroup parent, int viewType) { return onCreateViewHolder(parent, viewType, mInflater); } #Override public final void onBindViewHolder(VH holder, int position) { holder.itemView.setTag(R.id.tag_view_click, position); //创建点击事件 holder.itemView.setOnClickListener(mListener); holder.itemView.setOnLongClickListener(mListener); onBindVH(holder, position); } /////////////////////////////////////////////////////////////////////////// // 以下是增加的方法 /////////////////////////////////////////////////////////////////////////// /** * 注意!涉及到notifyItemInserted刷新时立即获取position可能会不正确 * 里面也有onItemLongClick */ public void setOnItemClickListener(ItemClickInterface<?, Integer> listener) { mListener = listener; notifyDataSetChanged(); } #NonNull protected abstract VH onCreateViewHolder(ViewGroup parent, int viewType, LayoutInflater inflater); protected abstract void onBindVH(VH holder, int position); } This is Interface /** * OnItemClickListener的接口 * 见子类实现{#link OnItemClickListener}{#link OnItemItemClickListener} */ public interface ItemClickInterface<DATA1, DATA2> extends View.OnClickListener, View.OnLongClickListener { void onItemClick(DATA1 data1, DATA2 data2); boolean onItemLongClick(DATA1 data1, DATA2 data2); } This is an abstract class public abstract class OnItemClickListener<DATA> implements ItemClickInterface<View, DATA> { #Override public void onClick(View v) { onItemClick(v, (DATA) v.getTag(R.id.tag_view_click)); } #Override public boolean onLongClick(View v) { return onItemLongClick(v, (DATA) v.getTag(R.id.tag_view_click)); } #Override public boolean onItemLongClick(View view, DATA data) { return false; } } You only need it mAdapter.setOnItemClickListener(new OnItemClickListener<Integer>() { #Override public void onItemClick(View view, Integer integer) { } #Override public boolean onItemLongClick(View view, Integer integer) { return true; } });
I found one of the shortest ways using androidx lifecycle mutable live data Adapter: private val onItemClickListener = MutableLiveData<YourAdapterItem>() override fun onBindViewHolder(holder: GifsViewHolder, position: Int) { holder.itemView.setOnClickListener { onItemClickListener.value = gifs[position] } } fun getOnItemClickListener(): MutableLiveData<Gif> { return onItemClickListener } anywhere in MainActivity yourFancyAdapter.getOnItemClickListener().observe(this, Observer { println(it) })