How to make single selection in recyclerview [duplicate] - android

I know there are no default selection methods in the RecyclerView class, but I have tried in the following way:
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.mTextView.setText(fonts.get(position).getName());
holder.checkBox.setChecked(fonts.get(position).isSelected());
holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked) {
for (int i = 0; i < fonts.size(); i++) {
fonts.get(i).setSelected(false);
}
fonts.get(position).setSelected(isChecked);
}
}
});
}
While trying this code, I got the expected output, but not completely.
I will explain this with images.
By default, the first item is selected from my adapter.
Then I select the 2nd, then the 3rd, then the 4th and finally the 5th one.
Here only the 5th should be selected, but all five are getting selected.
If I scroll the list to the bottom and come again to the top, I get what I expect.
How can I overcome this issue? And sometimes if I scroll the list very fast, some other item gets selected. How can I overcome this problem too?
While I was trying to use notifyDataSetChanged() after fonts.get(position).setSelected(isChecked);, I got the following exception:
java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:1462)
at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onChanged(RecyclerView.java:2982)
at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyChanged(RecyclerView.java:7493)
at android.support.v7.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:4338)
at com.app.myapp.screens.RecycleAdapter.onRowSelect(RecycleAdapter.java:111)

The solution for the issue:
public class yourRecyclerViewAdapter extends RecyclerView.Adapter<yourRecyclerViewAdapter.yourViewHolder> {
private static CheckBox lastChecked = null;
private static int lastCheckedPos = 0;
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.mTextView.setText(fonts.get(position).getName());
holder.checkBox.setChecked(fonts.get(position).isSelected());
holder.checkBox.setTag(new Integer(position));
//for default check in first item
if(position == 0 && fonts.get(0).isSelected() && holder.checkBox.isChecked())
{
lastChecked = holder.checkBox;
lastCheckedPos = 0;
}
holder.checkBox.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
CheckBox cb = (CheckBox)v;
int clickedPos = ((Integer)cb.getTag()).intValue();
if(cb.isChecked())
{
if(lastChecked != null)
{
lastChecked.setChecked(false);
fonts.get(lastCheckedPos).setSelected(false);
}
lastChecked = cb;
lastCheckedPos = clickedPos;
}
else
lastChecked = null;
fonts.get(clickedPos).setSelected(cb.isChecked);
}
});
}
}

It's quite late, but I'm still posting it as it may help someone else.
Use the code below as a reference to check a single item in RecyclerView:
/**
* Created by subrahmanyam on 28-01-2016, 04:02 PM.
*/
public class SampleAdapter extends RecyclerView.Adapter<SampleAdapter.ViewHolder> {
private final String[] list;
private int lastCheckedPosition = -1;
public SampleAdapter(String[] list) {
this.list = list;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = View.inflate(parent.getContext(), R.layout.sample_layout, null);
ViewHolder holder = new ViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.choiceName.setText(list[position]);
holder.radioButton.setChecked(position == lastCheckedPosition);
}
#Override
public int getItemCount() {
return list.length;
}
public class ViewHolder extends RecyclerView.ViewHolder {
#Bind(R.id.choice_name)
TextView choiceName;
#Bind(R.id.choice_select)
RadioButton radioButton;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
radioButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int copyOfLastCheckedPosition = lastCheckedPosition;
lastCheckedPosition = getAdapterPosition();
notifyItemChanged(copyOfLastCheckedPosition);
notifyItemChanged(lastCheckedPosition);
}
});
}
}
}

This is how it looks:
Inside your Adapter:
private int selectedPosition = -1;
And onBindViewHolder
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
if (selectedPosition == position) {
holder.itemView.setSelected(true); //using selector drawable
holder.tvText.setTextColor(ContextCompat.getColor(holder.tvText.getContext(),R.color.white));
} else {
holder.itemView.setSelected(false);
holder.tvText.setTextColor(ContextCompat.getColor(holder.tvText.getContext(),R.color.black));
}
holder.itemView.setOnClickListener(v -> {
if (selectedPosition >= 0)
notifyItemChanged(selectedPosition);
selectedPosition = holder.getAdapterPosition();
notifyItemChanged(selectedPosition);
});
}
that’s it!
As you can see, I am just notifying (updating) the previous selected item and newly selected item.
My Drawable set it as a background for recyclerview child views:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="false" android:state_selected="true">
<shape android:shape="rectangle">
<solid android:color="#color/blue" />
</shape>
</item>

You need to clear the OnCheckedChangeListener before setting setChecked():
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mRadioButton.setOnCheckedChangeListener(null);
holder.mRadioButton.setChecked(position == mCheckedPosition);
holder.mRadioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mCheckedPosition = position;
notifyDataSetChanged();
}
});
}
This way it won't trigger the java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling error.

Default value:
private int mCheckedPostion = -1;
Just use mCheckedPosition to save the status:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.checkBox.setChecked(position == mCheckedPostion);
holder.checkBox.setOnClickListener(v -> {
if (position == mCheckedPostion) {
holder.checkBox.setChecked(false);
mCheckedPostion = -1;
}
else {
mCheckedPostion = position;
notifyDataSetChanged();
}
});
}

It looks like there are two things at play here:
(1) The views are reused, so the old listener is still present.
(2) You are changing the data without notifying the adapter of the change.
I will address each separately.
(1) View reuse
Basically, in onBindViewHolder you are given an already initialized ViewHolder, which already contains a view. That ViewHolder may or may not have been previously bound to some data!
Note this bit of code right here:
holder.checkBox.setChecked(fonts.get(position).isSelected());
If the holder has been previously bound, then the checkbox already has a listener for when the checked state changes! That listener is being triggered at this point, which is what was causing your IllegalStateException.
An easy solution would be to remove the listener before calling setChecked. An elegant solution would require more knowledge of your views - I encourage you to look for a nicer way of handling this.
(2) Notify the adapter when data changes
The listener in your code is changing the state of the data without notifying the adapter of any subsequent changes. I don't know how your views are working so this may or may not be an issue. Typically when the state of your data changes, you need to let the adapter know about it.
RecyclerView.Adapter has many options to choose from, including notifyItemChanged, which tells it that a particular item has changed state. This might be good for your use:
if(isChecked) {
for (int i = 0; i < fonts.size(); i++) {
if (i == position) continue;
Font f = fonts.get(i);
if (f.isSelected()) {
f.setSelected(false);
notifyItemChanged(i); // Tell the adapter this item is updated
}
}
fonts.get(position).setSelected(isChecked);
notifyItemChanged(position);
}

This might help for those who want a single radiobutton to work -->
Radio Button RecycleView - Gist
If lambda expressions aren't supported, use this instead:
View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View v) {
notifyItemChanged(mSelectedItem); // to update last selected item.
mSelectedItem = getAdapterPosition();
}
};

This happens because RecyclerView, as the name suggests, does a good job at recycling its ViewHolders. This means that every ViewHolder, when it goes out of sight (actually, it takes a little more than going out of sight, but it makes sense to simplify it that way), it is recycled; this implies that the RecyclerView takes this ViewHolder that is already inflated and replaces its elements with the elements of another item in your data set.
Now, what is going on here is that once you scroll down and your first, selected, ViewHolders go out of sight, they are being recycled and used for other positions of your data set. Once you go up again, the ViewHolders that were bound to the first 5 items are not necessarely the same, now.
This is why you should keep an internal variable in your adapter that remembers the selection state of each item. This way, in the onBindViewHolder method, you can know if the item whose ViewHolder is currently being bound was selected or not, and modify a View accordingly, in this case your RadioButton's state (though I would suggest to use a CheckBox if you plan on selecting multiple items).
If you want to learn more about RecyclerView and its inner workings, I invite you to check FancyAdapters, a project I started on GitHub. It is a collection of adapters that implement selection, drag&drop of elements and swipe to dismiss capabilities. Maybe by checking the code you can obtain a good understanding on how RecyclerView works.

This simple one worked for me
private RadioButton lastCheckedRB = null;
...
#Override
public void onBindViewHolder(final CoachListViewHolder holder, final int position) {
holder.priceRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
RadioButton checked_rb = (RadioButton) group.findViewById(checkedId);
if (lastCheckedRB != null && lastCheckedRB != checked_rb) {
lastCheckedRB.setChecked(false);
}
//store the clicked radiobutton
lastCheckedRB = checked_rb;
}
});

The following might be helpful for RecyclerView with Single Choice.
Three steps to do that,
1) Declare a global integer variable,
private int mSelectedItem = -1;
2) in onBindViewHolder
mRadio.setChecked(position == mSelectedItem);
3) in onClickListener
mSelectedItem = getAdapterPosition();
notifyItemRangeChanged(0, mSingleCheckList.size());
mAdapter.onItemHolderClick(SingleCheckViewHolder.this);

public class GetStudentAdapter extends
RecyclerView.Adapter<GetStudentAdapter.MyViewHolder> {
private List<GetStudentModel> getStudentList;
Context context;
RecyclerView recyclerView;
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView textStudentName;
RadioButton rbSelect;
public MyViewHolder(View view) {
super(view);
textStudentName = (TextView) view.findViewById(R.id.textStudentName);
rbSelect = (RadioButton) view.findViewById(R.id.rbSelect);
}
}
public GetStudentAdapter(Context context, RecyclerView recyclerView, List<GetStudentModel> getStudentList) {
this.getStudentList = getStudentList;
this.recyclerView = recyclerView;
this.context = context;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.select_student_list_item, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
holder.textStudentName.setText(getStudentList.get(position).getName());
holder.rbSelect.setChecked(getStudentList.get(position).isSelected());
holder.rbSelect.setTag(position); // This line is important.
holder.rbSelect.setOnClickListener(onStateChangedListener(holder.rbSelect, position));
}
#Override
public int getItemCount() {
return getStudentList.size();
}
private View.OnClickListener onStateChangedListener(final RadioButton checkBox, final int position) {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
if (checkBox.isChecked()) {
for (int i = 0; i < getStudentList.size(); i++) {
getStudentList.get(i).setSelected(false);
}
getStudentList.get(position).setSelected(checkBox.isChecked());
notifyDataSetChanged();
} else {
}
}
};
}
}

This is how the Adapter class looks like :
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewHolder>{
Context context;
ArrayList<RouteDetailsFromFirestore> routeDetailsFromFirestoreArrayList_;
public int lastSelectedPosition=-1;
public MyRecyclerViewAdapter(Context context, ArrayList<RouteDetailsFromFirestore> routeDetailsFromFirestoreArrayList)
{
this.context = context;
this.routeDetailsFromFirestoreArrayList_ = routeDetailsFromFirestoreArrayList;
}
#NonNull
#Override
public MyRecyclerViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i)
{
// LayoutInflater layoutInflater = LayoutInflater.from(mainActivity_.getBaseContext());
LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
View view = layoutInflater.inflate(R.layout.route_details, viewGroup, false);
return new MyRecyclerViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final MyRecyclerViewHolder myRecyclerViewHolder, final int i) {
/* This is the part where the appropriate checking and unchecking of radio button happens appropriately */
myRecyclerViewHolder.mRadioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b) {
if (lastSelectedPosition != -1) {
/* Getting the reference to the previously checked radio button and then unchecking it.lastSelectedPosition has the index of the previously selected radioButton */
//RadioButton rb = (RadioButton) ((MainActivity) context).linearLayoutManager.getChildAt(lastSelectedPosition).findViewById(R.id.rbRadioButton);
RadioButton rb = (RadioButton) ((MainActivity) myRecyclerViewHolder.mRadioButton.getContext()).linearLayoutManager.getChildAt(lastSelectedPosition).findViewById(R.id.rbRadioButton);
rb.setChecked(false);
}
lastSelectedPosition = i;
/* Checking the currently selected radio button */
myRecyclerViewHolder.mRadioButton.setChecked(true);
}
}
});
}
#Override
public int getItemCount() {
return routeDetailsFromFirestoreArrayList_.size();
}
} // End of Adapter Class
Inside MainActivity.java we call the ctor of Adapter class like this. The context passed is of MainActivity to the Adapter ctor :
myRecyclerViewAdapter = new MyRecyclerViewAdapter(MainActivity.this, routeDetailsList);

I got a solution that will save your selection when you open a recycler list.
var mSelectedItem = -1
// store saved selection
fun setSelection(position: Int) {
mSelectedItem = position
}
override fun onBindViewHolder(holder: GroupHolder, position: Int) {
holder.bind(dataList[position])
holder.radioButton.isChecked = position == mSelectedItem
}
inner class GroupHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val radioButton: RadioButton = itemView.rbValue
fun bind(data: Data) = with(itemView) {
radioButton.text = data.name
val clickListener = View.OnClickListener {
mSelectedItem = bindingAdapterPosition
notifyDataSetChanged()
setSelection(mSelectedItem)
}
radioButton.setOnClickListener(clickListener)
}
}

After spending so many days over this, this is what I came up with which worked for me, and is good practice as well,
Create an interface, name it some listener: SomeSelectedListener.
Add a method which takes an integer:void onSelect(int position);
Initialise the listener in the recycler adapter's constructor as: a) first declare globally as: private SomeSelectedListener listener b) then in constructor initialise as: this.listener = listener;
Inside onClick() of checkbox inside onBindViewHolder(): update the method of the interface/listener by passing the position as: listener.onSelect(position)
In the model, add a variable for deselect say, mSelectedConstant and initialise it there to 0. This represents the default state when nothing is selected.
Add getter and setter for the mSelectedConstant in the same model.
Now, go to your fragment/activity and implement the listener interface. Then override its method: onSelect(int position). Within this method, iterate through your list which you are passing to your adapter using a for loop and setSelectedConstant to 0 for all:
Code
#Override
public void onTicketSelect(int position) {
for (ListType listName : list) {
listName.setmSelectedConstant(0);
}
Outside this, make the selected position constant 1:
Code
list.get(position).setmSelectedConstant(1);
Notify this change by calling: adapter.notifyDataSetChanged(); immediately after this.
Last step: go back to your adapter and update inside onBindViewHolder() after onClick() add the code to update the checkbox state,
Code
if (listVarInAdapter.get(position).getmSelectedConstant() == 1) {
holder.checkIcon.setChecked(true);
selectedTicketType = dataSetList.get(position);}
else {
commonHolder.checkCircularIcon.setChecked(false);
}

Here is a similar thing I have achieved.
The below code is from the application to select an address from a list of addresses that are displayed in cardview(cvAddress), so that on click of particular item(cardview) the imageView inside the item should set to a different resource (select/unselect):
#Override
public void onBindViewHolder(final AddressHolder holder, final int position)
{
holderList.add(holder);
holder.tvAddress.setText(addresses.get(position).getAddress());
holder.cvAddress.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
selectCurrItem(position);
}
});
}
private void selectCurrItem(int position)
{
int size = holderList.size();
for(int i = 0; i<size; i++)
{
if(i==position)
holderList.get(i).ivSelect.setImageResource(R.drawable.select);
else
holderList.get(i).ivSelect.setImageResource(R.drawable.unselect);
}
}
I don't know if this is the best solution or not, but this worked for me.

Please try this...
This works for me...
In the adapter, take a sparse Boolean array.
SparseBooleanArray sparseBooleanArray;
In the constructor, initialise this,
sparseBooleanArray = new SparseBooleanArray();
In the bind holder, add:
#Override
public void onBindViewHolder(DispositionViewHolder holder, final int position) {
holder.tv_disposition.setText(dispList.get(position).getName());
if(sparseBooleanArray.get(position,false))
{
holder.rd_disp.setChecked(true);
}
else
{
holder.rd_disp.setChecked(false);
}
setClickListner(holder,position);
}
private void setClickListner(final DispositionViewHolder holder, final int position) {
holder.rd_disp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sparseBooleanArray.clear();
sparseBooleanArray.put(position, true);
notifyDataSetChanged();
}
});
}
rd_disp is a radio button in the XML file.
So when the recycler view loads the items, in the bindView Holder, it checks whether the sparseBooleanArray contains the value "true", corresponding to its position.
If the value returned is true then we set the radio button selection true. Else we set the selection false.
In onclickHolder I have cleared the sparseArray and set the value to true corresponding to that position.
When I call notify datasetChange, it again calls the onBindViewHolder and the conditions are checked again. This makes our selection to only select a particular radio.

public class LastTransactionAdapter extends RecyclerView.Adapter<LastTransactionAdapter.MyViewHolder> {
private Context context;
private List<PPCTransaction> ppcTransactionList;
private static int checkedPosition = -1;
public LastTransactionAdapter(Context context, List<PPCTransaction> ppcTransactionList) {
this.context = context;
this.ppcTransactionList = ppcTransactionList;
}
#NonNull
#Override
public LastTransactionAdapter.MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.last_transaction_item, parent, false));
}
#Override
public void onBindViewHolder(#NonNull LastTransactionAdapter.MyViewHolder holder, int position) {
holder.setLastTransaction(ppcTransactionList.get(position), position);
}
#Override
public int getItemCount() {
return ppcTransactionList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private LinearLayout linTransactionItem;
private RadioButton radioButton;
private TextView tvDate, tvMerchantName, tvAmount, tvStatus;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
linTransactionItem = itemView.findViewById(R.id.linTransactionItem);
tvDate = itemView.findViewById(R.id.tvDate);
tvMerchantName = itemView.findViewById(R.id.tvMerchantName);
tvAmount = itemView.findViewById(R.id.tvAmount);
tvStatus = itemView.findViewById(R.id.tvStatus);
radioButton = itemView.findViewById(R.id.radioButton);
}
public void setLastTransaction(PPCTransaction ppcTransaction, int position) {
tvDate.setText(ppcTransaction.getmDate());
tvMerchantName.setText(ppcTransaction.getmMerchantName());
tvAmount.setText(ppcTransaction.getmAmount());
tvStatus.setText(ppcTransaction.getmStatus());
if (checkedPosition == -1) {
radioButton.setChecked(false);
} else {
if (checkedPosition == getAdapterPosition()) {
radioButton.setChecked(true);
} else {
radioButton.setChecked(false);
}
}
linTransactionItem.setOnClickListener(v -> {
radioButton.setChecked(true);
if (checkedPosition != getAdapterPosition()) {
notifyItemChanged(checkedPosition);
checkedPosition = getAdapterPosition();
}
});
}
}

// This work for my with out any visual isue
data class buttonData(val button:RadioButton, val position: Int)
var selectedPosition = -1
override fun onBindViewHolder(holder: SingleChoiceAdapter.OptionHolder, position: Int) {
holder.viewDataBinding.option= options[position]
holder.viewDataBinding.radioButton.setChecked(position == selectedPosition);
buttonList.add( buttonData(holder.viewDataBinding.radioButton,position))
holder.viewDataBinding.radioButton.setOnClickListener {
selectedPosition= position
for(buttonData in buttonList ){
buttonData.button.isChecked = buttonData.position == position
}
}
}

Don't make it too complicated:
SharedPreferences sharedPreferences = getSharedPreferences("appdetails", MODE_PRIVATE);
String selection = sharedPreferences.getString("selection", null);
public void onBindViewHolder(ViewHolder holder, final int position) {
String name = fonts.get(position).getName();
holder.mTextView.setText(name);
if(selection.equals(name)) {
holder.checkBox.setChecked(false); //
holder.checkBox.setChecked(true);
}
holder.checkbox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SharedPreferences sharedPreferences = getSharedPreferences("appdetails", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("selection", name);
r.setChecked(false);
editor.apply();
holder.checkBox.setChecked(true);
}
});
}

Related

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

Im trying to implement a RecyclerView in my app. This is my Adapter class:
public class AdapterItemsList extends RecyclerView.Adapter<AdapterItemsList.ViewHolderItems>
{
private ArrayList<CItem> items;
public AdapterItemsList(ArrayList<CItem> items)
{
this.items = items;
}
#NonNull
#Override
public ViewHolderItems onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_counter, null, false);
return new ViewHolderItems(view);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolderItems holder, final int position)
{
holder.asignarDatos(items.get(holder.getAdapterPosition()));
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
//deleteSelectedItem() method gonna be here
Toast.makeText(view.getContext(), String.valueOf(holder.getAdapterPosition())
, Toast.LENGTH_SHORT).show();
return false;
}
});
holder.counterAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
auxInteger += 1;
holder.itemNumber.setText(auxInteger.toString());
items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
}
});
holder.counterSubtract.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
if (auxInteger > 0)
{
auxInteger -= 1;
holder.itemNumber.setText(auxInteger.toString());
items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
}
}
});
}
#Override
public int getItemCount()
{
return items.size();
}
public class ViewHolderItems extends RecyclerView.ViewHolder
{
TextView itemName;
TextView itemNumber;
Button counterAdd;
Button counterSubtract;
public ViewHolderItems(View itemView)
{
super(itemView);
itemName = (TextView) itemView.findViewById(R.id.textViewItemCounterName);
itemNumber = (TextView) itemView.findViewById(R.id.textViewItemCounterNumber);
counterAdd = (Button) itemView.findViewById(R.id.buttonItemCounterAdd);
counterSubtract = (Button) itemView.findViewById(R.id.buttonItemCounterSubtract);
}
public void asignarDatos(CItem item)
{
itemName.setText(item.getObjectName());
itemNumber.setText(item.getObjectNumber().toString());
}
}
}
The code that "creates" the adapter:
private void CreateAdapter() {
recyclerViewItems.setAdapter(null);
items.clear();
//I dont post the code, but this just gets data from a SQLite database and populate "items".
LoadClientsList();
final AdapterItemsList adapter = new AdapterItemsList(items);
recyclerViewItems.setAdapter(adapter);
}
As you can see, I never use "implements View.OnLongClickListener" or something like that, but I just set the OnClickListener inside onBindViewHolder. Now there is a "Toast" inside (which appears just fine when I test the app), but later on, there should be my "delete item" dialog, that gonna give the user the change to Accept or Cancel (an usual dialog). If the user press "accept" then I gonna remove the item from the list.
I gonna admit, Im not an Android or Java Ninja, so I know there are horrendous mistakes probably. But the thing is, the app is working by now (the Toast appears correctlywhen I long-press an item), but the fact that I never use "implements", while I see everyone does it in StackOverflow questions related to RecyclerViews and Adapters, make me think Im making (several) serious mistakes.
I could admit since Im not a master at Java fundamentals (some years working with COBOL already that I remember almost NULL of OOP), I don't see where my mistakes are, but still, any help will be much appreciated.
Thanks in regard!
UPDATE:
Ok I was changing my code and now its like follows:
public class AdapterItemsList extends
RecyclerView.Adapter<AdapterItemsList.ViewHolderItems> implements
View.OnClickListener {
private ArrayList<CItem> items;
/* Added this */
private View.OnClickListener listener;
public AdapterItemsList(ArrayList<CItem> items)
{
this.items = items;
}
#NonNull
#Override
public ViewHolderItems onCreateViewHolder(#NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_counter, null, false);
/* Added this */
view.setOnClickListener(this);
return new ViewHolderItems(view);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolderItems holder, final int position)
{
holder.asignarDatos(items.get(holder.getAdapterPosition()));
holder.counterAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
auxInteger += 1;
holder.itemNumber.setText(auxInteger.toString());
items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
}
});
holder.counterSubtract.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Integer auxInteger = Integer.parseInt(holder.itemNumber.getText().toString());
if (auxInteger > 0)
{
auxInteger -= 1;
holder.itemNumber.setText(auxInteger.toString());
items.get(holder.getAdapterPosition()).setObjectNumber(Integer.parseInt(holder.itemNumber.getText().toString()));
}
}
});
}
#Override
public int getItemCount() { [...] }
/* Added this */
public void setOnClickListener(View.OnClickListener listener)
{
this.listener = listener;
}
/* Added this */
#Override
public void onClick(View view) {
if (listener != null)
{
listener.onClick(view);
}
}
public class ViewHolderItems extends RecyclerView.ViewHolder { [...] } }
Code where I create the adapter:
private void CreateAdapter() {
recyclerViewItems.setAdapter(null);
items.clear();
LoadClientsList();
final AdapterItemsList adapter = new AdapterItemsList(items);
/* Added this */
adapter.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), String.valueOf(recyclerViewItems.getChildAdapterPosition(view)), Toast.LENGTH_SHORT).show();
}
});
recyclerViewItems.setAdapter(adapter);
}
The "recyclerViewItems.getChildAdapterPosition(view)" finally solves my problem (didn't mentioned before, but it was the reason why I put the "setOnClick..." inside the onBindViewHolder method, just because I needed the position of the pressed item and I couldnt figure out how on earth to access it).
So now I set the listener when I create the adapter instead of when Binding the View holder, which I guess it's better.
Still, I didnt change the "holder.counterAdd.setOnClickListener" and "holder.counterSubtract.setOnClickListener" lines, since I think they must be inside the onBindViewHolder method, but I guess Im still wrong on that.
I disagree with most of the comments on your question, and I think that the way you've written your adapter is pretty good. It's not perfect, but there is nothing wrong with manually setting a View.OnClickListener to some (or many) views inside your ViewHolder. In fact, this is the Google-recommended way of doing things!
I would make some changes, however. The first would be to move the declaration of your click listeners into the onCreateViewHolder() method (by putting them inside the ViewHolder's constructor). The second would be to add checks for NO_POSITION to make sure you handle the case where an item has been removed from your adapter but the layout hasn't been recalculated yet.
Here is what I'd recommend:
public ViewHolderItems(View itemView)
{
super(itemView);
itemName = (TextView) itemView.findViewById(R.id.textViewItemCounterName);
itemNumber = (TextView) itemView.findViewById(R.id.textViewItemCounterNumber);
counterAdd = (Button) itemView.findViewById(R.id.buttonItemCounterAdd);
counterSubtract = (Button) itemView.findViewById(R.id.buttonItemCounterSubtract);
itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
deleteItem();
return true;
}
});
counterAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
itemAdd();
}
});
counterSubtract.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
itemSubtract();
}
});
}
private void deleteItem() {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
items.remove(position);
adapter.notifyItemRemoved(position);
}
}
private void itemAdd() {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
CItem item = items.get(position);
item.setObjectNumber(item.getObjectNumber() + 1);
adapter.notifyItemChanged(position);
}
}
private void itemSubtract() {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
CItem item = items.get(position);
if (item.getObjectNumber() > 0) {
item.setObjectNumber(item.getObjectNumber() - 1);
adapter.notifyItemChanged(position);
}
}
}
And then your onBindViewHolder() is extremely simple:
#Override
public void onBindViewHolder(#NonNull ViewHolderItems holder, int position)
{
holder.asignarDatos(items.get(position));
}
So, let's talk about what I'm doing here.
First, I pushed all of the listener setup into the constructor of your ViewHolder. This means that the listeners are all assigned exactly once. In order to make that still function in a world where ViewHolders are recycled and attached to different items, we have to use getAdapterPosition() to make sure that we're always interacting with the correct item in your list.
We have to check for NO_POSITION because RecyclerView layout operations are asynchronous: when you delete an item from the list, there is a gap where the item doesn't exist in your adapter any more but still is displayed on screen. In this gap, the user might tap a button! In these cases, we just ignore the user input.
I also changed how you update the values of your items, and how you display those updates. Rather than getting the text and parsing it and then re-setting it, I always go through the adapter. Remember that the goal of your RecyclerView implementation is to be able to display any element in your list correctly. The best way to do this is to update the element in your data set, and then notify the RecyclerView that that element has changed, and let onBindViewHolder() take care of the required updates.

Recyclerview pass the data to next item upon deletion of one item

The title seems confusing because I cannot explain this properly using text.
I'll try my best to explain my problem.
Currently I have items in my recyclerview :
Each item contains a delete button that will remove the item in the recyclerview.
Lets assume that I have 5 items in the list:
what I want to attain is when the user deletes
Item 2
the information/data from item 3 will be transferred to Item 2,
the data from item 4 will be transferred to item 3 and
the data from item 5 will be transferred to item 4
and lastly the item 5 will be deleted.
I currently have no code for this but I'm trying my best to construct it.
Here is my adapter code, it might help:
public class CreditCard_PostPayAdapter extends RecyclerView.Adapter<CreditCard_PostPayAdapter.MyViewHolder> {
private static String TAGedittext = "";
//private final AccountHistoryTransactionActivity homeActivity;
private Context mContext;
private ArrayList<Integer> mHeaderText;
CreditCard_PostPayAdapter adapter;
public CreditCard_PostPayAdapter(Context mContext, ArrayList<Integer> mHeaderTextList ) {
this.mContext = mContext;
this.mHeaderText = mHeaderTextList;
}
#Override
public CreditCard_PostPayAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.creditcard_postpay_item, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final CreditCard_PostPayAdapter.MyViewHolder holder, int position) {
final int pos = position + 1;
final int mPosition = position;
if (mHeaderText.size() == 1) {
holder.mDeleteButton.setVisibility(View.GONE);
} else {
holder.mDeleteButton.setVisibility(View.VISIBLE); }
holder.mDeleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mHeaderText.remove(mPosition);
ArrayList<Integer> temp = new ArrayList<Integer>();
for (int i = 0 ; mHeaderText.size() - 1 >= i ; i++) {
temp.add(i);
Log.d("Counter++",""+i);
}
holder.mMerchantName.setText("");
holder.mTransactionAmountEditText.setText("");
holder.mTransactionDateEditText.setText("");
holder.mPostingDateEditText.setText("");
mHeaderText.clear();
mHeaderText.addAll(temp);
notifyDataSetChanged();
}
});
}
#Override
public int getItemCount() {
return mHeaderText.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView mHeaderTitle
public ImageView mArrowHeader;
public ImageButton mDeleteButton;
public TextInputEditText mTransactionDateEditText,
mPostingDateEditText,
mTransactionAmountEditText;
public MyViewHolder(View view) {
super(view);
this.mHeaderTitle = (TextView) view.findViewById(R.id.header_title);
this.mDeleteButton = view.findViewById(R.id.mDeleteButton);
this.mMerchantName = view.findViewById(R.id.mMerchantNameTextView);
this.mTransactionDateEditText = view.findViewById(R.id.Transaction_date);
this.mPostingDateEditText = view.findViewById(R.id.posting_date);
this.mTransactionAmountEditText = view.findViewById(R.id.Transaction_amount);
}
}
}
My current delete button function is to:
Delete the item(n) and recount all of the remaining item.
Replace your onBindViewHolder method and try,
#Override
public void onBindViewHolder(final CreditCard_PostPayAdapter.MyViewHolder holder, int position) {
holder.mDeleteButton.setTag(position);
holder.mDeleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int clickedPos = (int) view.getTag();
mHeaderText.remove(clickedPos);
notifyItemRemoved(position);
}
});
}
If you want to delete the 2nd element. delete the element and remaining will be taken care by recyclerAdapter to remove the row and align the data.
inside your onClickListener, remove the data from ArrayList and the call notifyItemRemoved()
write your onClick inside ViewHolder class
onClick(View view){
mHeaderText.remove(getAdapterPosition());
notifyItemRemoved(getAdapterPosition());
}
i hope this will help you..
you an delete the item in list and refresh your adapter by just doing this in your onClick:
holder.mDeleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mHeaderText.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mHeaderText.size());
}
});

How to implement RecycerView With Items having Radio buttons with single choice mode

I need to show a recyclerview with single choice item. but Recyclerview doesn't have the choice mode.
Does any one know how to resolve this?
Thanks in advance.
Store the states of the checked items in a SparseBooleanArray with the position as key.As you change the state of the button,update it's status in the booleanArray and call notifyItemChanged(position).And in BindviewHolder(ViewHolder viewholder,int position) method load the state from booleanArray like viewholder.radioButton.setChecked(boolean.get(position)).Have a look at this for basic idea
You could use something like this, it stores position for selected item, you could add a bit more code to store the item itself if your adapter reorders itself
public class SingleChoiceAdapter extends RecyclerView.Adapter<SingleChoiceAdapter.ItemViewHolder> {
private static final int NO_POSITION = -1;
private static final String SELECTED_ITEM_POSITION = "SELECTED_ITEM_POSITION";
private final LayoutInflater inflater;
private final List<Item> items;
private int selectedItemPosition = NO_POSITION;
public SingleChoiceAdapter(Context context, Bundle savedInstanceState) {
inflater = LayoutInflater.from(context);
items = new ArrayList<>();
if (savedInstanceState != null) {
selectedItemPosition = savedInstanceState.getInt(SELECTED_ITEM_POSITION, NO_POSITION);
}
}
#Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = inflater.inflate(R.layout.item, parent, false);
return new ItemViewHolder(view);
}
#Override
public void onBindViewHolder(final ItemViewHolder holder, int position) {
final Item item = items.get(position);
holder.textView.setText(item.name);
holder.textView.setBackgroundColor(position == selectedItemPosition
? Color.LTGRAY
: Color.TRANSPARENT);
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
updateSelectedItem(holder.getAdapterPosition());
}
});
}
#Override
public int getItemCount() {
return items.size();
}
public void setItems(List<Item> items) {
this.items.clear();
this.items.addAll(items);
notifyDataSetChanged();
}
public Item getSelectedItem() {
if (selectedItemPosition == NO_POSITION) {
return null;
}
return items.get(selectedItemPosition);
}
public void onSaveInstanceState(Bundle outState) {
outState.putInt(SELECTED_ITEM_POSITION, selectedItemPosition);
}
private void updateSelectedItem(int newSelectedItemPosition) {
if (selectedItemPosition != NO_POSITION) {
notifyItemChanged(selectedItemPosition);
}
selectedItemPosition = newSelectedItemPosition;
notifyItemChanged(newSelectedItemPosition);
}
public static class ItemViewHolder extends RecyclerView.ViewHolder {
public final TextView textView;
public ItemViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.text_view);
}
}
}
Displaying already checks-
You can maintain a boolean array of size of your list. On bind view handle the boolean(true/ false) logic, like setting check true/false of a radio button.
Handling new clicks-
Then on the onclick of the radio button, set the value of every array element to false and setting the current array position to true after that. Now call notifyDatatSetChanged().
You can even do it with just one single integer variable instead of maintaining an array which is costly in devices like android where space is a constraint.
So, just maintain a single integer variable, lets say int selectedPosition=-1 initially.
In onBind check if the position==selectedPosition, if true check the button else uncheck.
Whenever user checks/unchecks the button, just update the selectedPosition
Something like this,
if(selectedPosition==position)
selectedPosition=-1
else{
selectedPosition=position
notifyItemChanged(selectedPosition);
}
notifyItemChanged(position);
Please set the id for the position in onBindViewHolder then you can process action in this method too. Update data set and call notifyDataSetChanged
Here is my example code
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ItemObject itemObject = mDataset.get(position);
((ViewHolder)holder).mTextView.setText(itemObject.getTitle());
Button mButton = ((ViewHolder) holder).mButton;
mButton.setSelected(itemObject.isSelected());
if(itemObject.isSelected()){
((ViewHolder)holder).mTextView.setText("OK");
}
mButton.setTag(position);
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
updateDatset((Integer) v.getTag());
}
});
}
In the updateDataset method, I update information of this dataset and call notifyDataSetChanged()
Good luck

CheckBox in RecyclerView keeps on checking different items

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

Android RecyclerView is not saving its state when scrolling back

I have a recyclerview in which onLongClick() of an item, I am showing a button. But when scroll down the recycler view and scrolling back, that button is showing on top of another item or sometimes it is not showing at all.
Here is my code
public static class TextViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnLongClickListener{
public LinearLayout enq_layout;
public LinearLayout item_layout;
public TextView enquire;
public int position;
public TextViewHolder(View itemView) {
super(itemView);
item_layout= (LinearLayout) itemView.findViewById(R.id.item_layout);
enq_layout= (LinearLayout) itemView.findViewById(R.id.enq_layout);
enquire=(TextView) itemView.findViewById(R.id.enquire);
//position=getLayoutPosition();
}
}
#Override
public int getItemViewType(int position) {
return product.get(position)!=null? VIEW_ITEM: VIEW_PROG;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cardlayout_product, parent, false);
RecyclerView.ViewHolder vh = new TextViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// Toast.makeText(act, "onBindViewHolder" +position, Toast.LENGTH_LONG).show();
final ProductDetails item = product.get(position);
final TextViewHolder hold=((TextViewHolder)holder);
//hold.position=position;
// hold.item_layout.setTag(position);
hold.item_layout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// go to next activity
}
});
hold.item_layout.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
// show enquiry button
hold.enq_layout.setVisibility(View.VISIBLE);
}
});
hold.enquire.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//do some operation
int productid = Integer.parseInt(product.get(item.getPosition()).getProduct_id());
}
});
}
#Override
public int getItemCount() {
return product.size();
}
I tried this way Why doesn't RecyclerView have onItemClickListener()? And how RecyclerView is different from Listview?, but I am not able to access the views inside onCreateViewHolder's onclick methods.
I had the same problem and solved this. You need to use the method of RecyclerView and set the items cache size:
mRecyclerView.setItemViewCacheSize(300);
You are going to have to create a global variable called for instance boolean shouldShowView like this:
public static class TextViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnLongClickListener{
public LinearLayout enq_layout;
private boolean shouldShowView;
Then in onBindViewHolder use can use the following code:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// Toast.makeText(act, "onBindViewHolder" +position, Toast.LENGTH_LONG).show();
final ProductDetails item = product.get(position);
final TextViewHolder hold=((TextViewHolder)holder);
if (shouldShowView) {
hold.enq_layout.setVisibility(View.VISIBLE);
} else {
hold.enq_layout.setVisibility(View.INVISIBLE);
}
Then in your onLongClick method just set the shouldShowView variable to true if there was a long click. You should also set it to false somewhere else when you want to hide the button. You might also have to keep track of which item was selected in global variable state and check whether the current position is equal to the selected position.
You might also want to move the shouldShowView check below the click listeners.
Let me know if you have any questions.
you can try this
public View getViewByPosition(int position, ListView lv1) {
final int firstListItemPosition = lv1.getFirstVisiblePosition();
final int lastListItemPosition = firstListItemPosition +
lv1.getChildCount() - 1;
if (position < firstListItemPosition || position >
lastListItemPosition ) {
return lv1.getAdapter().getView(position, null, lv1);
} else {
final int childIndex = position - firstListItemPosition;
return lv1.getChildAt(childIndex);
}
}
And in your declare it like this
selectedids ="";
for (int i = 0; i < len; i++){
//View childv = lv1.getChildAt(i);
View childv=getViewByPosition(i, lv1);

Categories

Resources