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

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());
}
});

Related

How to remove item in recyclerview in android

i am doing simple cart application. which will show cart details in recycler view.
Here i am going to decrease the quantity value upto 1.The item going to be remove from item adapter.it will be working fine.But when i was going to decrease the second item quantity value,it will remove without checking the condition. My condition is if the quantity is below 1 the item going to remove.
ItemAdapter.java
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ViewHolder> {
Context context;
ArrayList<ItemData> itemDataList;
int quantity,t_amount;
totalAmount totalAmount;
public ItemAdapter(Context context, ArrayList<ItemData> itemDataList) {
this.context = context;
this.itemDataList = itemDataList;
totalAmount = (ItemAdapter.totalAmount) context;
}
#NonNull
#Override
public ItemAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_details,parent,false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, final int pos) {
ItemData itemData = itemDataList.get(pos);
holder.txtPrice.setText(itemData.getPrice());
holder.txtDesc.setText(itemData.getDescription());
holder.txtCartCount.setText(itemData.getCartCount());
holder.btnCartDec.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(quantity <= 1){
itemDataList.remove(holder.getAdapterPosition());
notifyItemRemoved(holder.getAdapterPosition());
notifyItemRangeChanged(holder.getAdapterPosition(),itemDataList.size());
}else {
quantity = Integer.parseInt(itemDataList.get(pos).getCartCount());
quantity = quantity-1;
itemDataList.get(pos).setCartCount(String.valueOf(quantity));
notifyDataSetChanged();
holder.txtCartCount.setText(String.valueOf(quantity));
}
}
});
holder.btnCartInc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
quantity = Integer.parseInt(itemDataList.get(pos).getCartCount());
quantity = quantity+1;
itemDataList.get(pos).setCartCount(String.valueOf(quantity));
notifyDataSetChanged();
holder.txtCartCount.setText(String.valueOf(quantity));
}
});
}
#Override
public int getItemCount() {
return itemDataList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView txtPrice, txtDesc, txtCartCount, btnCartDec, btnCartInc;
public ViewHolder(#NonNull View itemView) {
super(itemView);
txtPrice = itemView.findViewById(R.id.itemView_price);
txtDesc = itemView.findViewById(R.id.itemView_desc);
txtCartCount = itemView.findViewById(R.id.itemView_Count);
btnCartDec = itemView.findViewById(R.id.itemViewDec);
btnCartInc = itemView.findViewById(R.id.itemViewInc);
}
}
public interface totalAmount{
void t_amount(int amount);
}
}
Just need knows the item position and adds below code in adapter
mDataset.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, mDataSet.size());
You must get the quantity related to the position before your if condition. In your case the quantity value is the previous value, that's why the if condition is true.
holder.btnCartDec.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// You must get the quantity of the currently clicked item here
quantity = Integer.parseInt(itemDataList.get(pos).getCartCount());
if(quantity <= 1){
itemDataList.remove(holder.getAdapterPosition());
notifyItemRemoved(holder.getAdapterPosition());
notifyItemRangeChanged(holder.getAdapterPosition(),itemDataList.size());
}else {
quantity = Integer.parseInt(itemDataList.get(pos).getCartCount());
quantity = quantity-1;
itemDataList.get(pos).setCartCount(String.valueOf(quantity));
notifyDataSetChanged();
holder.txtCartCount.setText(String.valueOf(quantity));
}
}
});
you can remove your item from model at specific position.After that notify Adapter that some changes has been done in list using notifyDataSetChanged();.
list.remove(getAdapterPosition())
notifyDataSetChanged();
You can remove from list of that position and notify adater ,it will remove.
replace this code
itemDataList.remove(holder.getAdapterPosition());
notifyItemRemoved(holder.getAdapterPosition());
notifyItemRangeChanged(holder.getAdapterPosition(),itemDataList.size());
to this
itemDataList.remove(holder.getAdapterPosition());notifyDataSetChanged();
it will work

How to make single selection in recyclerview [duplicate]

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

Recyclerview single item selection not working correctly

I've implemented a recyclerview with staggered gridLayout containing about 31 items in the arrayList, recyclerview is working correctly, but I faced issue relating to single item selection.
When I select the value till "26" as shown in figure, its working fine
But, when I select the value after "26", the values from top most item are also selected, as shown in this next figure.
I require to only select one item at a time.
I've implemented the following code in my adapter class
public class DialogAdapter extends
RecyclerView.Adapter<DialogAdapter.DialogHolder>
{
// components
public Context context;
public ArrayList<AlertDialogModel> dialogArrayList = new
ArrayList<AlertDialogModel>();
private final ArrayList<Integer> selected = new ArrayList<>();
private int lastCheckedPosition = -1;
public Interface interface;
// parameterized constructor
public DialogAdapter(Context context, ArrayList<AlertDialogModel>
dialogArrayList,Interface interface)
{
this.context = context;
this.dialogArrayList = dialogArrayList;
this.interface = interface;
}
#NonNull
#Override
public DialogHolder onCreateViewHolder(#NonNull ViewGroup parent, int
viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_cardview,parent,false);
DialogHolder dialogHolder = new DialogHolder(view);
return dialogHolder;
}
#Override
public void onBindViewHolder(#NonNull final DialogHolder holder, final int position)
{
final AlertDialogModel alertDialogModel = dialogArrayList.get(position);
holder.textView.setText(alertDialogModel.getDisplayValue());
if(lastCheckedPosition == position)
{
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
}
else
{
}
holder.textView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
lastCheckedPosition = position;
notifyDataSetChanged();
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
interface.getSelectedValue(alertDialogModel.getDisplayValue());
}
});
}
#Override
public int getItemCount()
{
return dialogArrayList.size();
}
public static class DialogHolder extends RecyclerView.ViewHolder
{
public TextView textView;
public DialogHolder(View itemView)
{
super(itemView);
textView = (TextView)itemView.findViewById(R.id.textView);
}
}
}
Can anyone relate my code and identify the issue ?
holder.textView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
lastCheckedPosition = position;
notifyDataSetChanged();
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
interface.getSelectedValue(alertDialogModel.getDisplayValue());
//below line is important to remove previous selected position from the variable
lastCheckedPosition = -1;
}
});
you should put the text view to the original state:
if(lastCheckedPosition == position)
{
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
}
else
{
holder.textView.setTextColor(context.getResources().getColor(R.color.transparent));
holder.textView.setBackground(null));
}

Count the clicks on an item of RecyclerView

I am developing an android app in which I use a RecyclerView. Suppose I have 6 items in my RecyclerView i.e. [A, B, C, D, E, F]. So how can I get the number of clicks on these items?
For example:
If an user open item B 4 times. How can I get the count = 4?
P.s. count should increase only when the user click on the same item.
First declare static count variables for each items
private static int aCount = 0;
Then inside your onBindViewHolder, pass a method after the button click along with your position id.
public void onBindViewHolder(ItemAdapter.ViewHolder holder, int position) {
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
thisWasClicked(position);
}
}
}
The method will be something like this
private void thisWasClicked(int position) {
if (position == 0) {
aCount++;
}
}
in holder
itemView.setOnClickListener(){
onClick(){
// count up
}
}
each item needs also an id to know, which item it is
I've made an example
CustomItem object
public class CustomItem {
private String item;
private int click;
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public int getClick() {
return click;
}
public void setClick(int click) {
this.click = click;
}
}
CustomAdapter and CustomViewHolder for the recyclerview
public class CustomAdapter extends RecyclerView.Adapter{
private List<CustomItem> itemList;
public CustomAdapter(List<CustomItem> itemList){
this.itemList = itemList;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new CustomViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.viewholder_generic, parent, false));;
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//increase click count for the current item
itemList.get(holder.getAdapterPosition()).setClick(itemList.get(holder.getAdapterPosition()).getClick() + 1);
}
});
}
#Override
public int getItemCount() {
return itemList.size();
}
// Custom ViewHolder
public class CustomViewHolder extends RecyclerView.ViewHolder {
//add fields you need
public CustomViewHolder(View itemView) {
super(itemView);
}
}
}
You can create a Map<Integer, Integer> clicksMap = new HashMap<>() in your adapter
You should also pass an interface to your ViewHolder that acts as a click Listener. Something like.
public interface OnItemLick {
void onItemClick(int position, int id);
}
Then declare an instance of this interface in your adapter.
OnItemLick listener = new OnItemClick() {
void onItemClick(int position, int id) {
//you can either use the item position or an id (if you have an unique one for the items). I will show an example using the position.
int count;
if(clicksMap.contains(position)) {
count = clickMap.get(position);
count ++;
clickMap.put(position, count);
}else {
count ++;
clickMap.put(position, count); }
}
In the end you clicksMap will contain the click count for every position of the items in the recycler.

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

Categories

Resources