Disable all other toggle buttons in recycler view after one is clicked - java

I am using recyclerView to build a list of toggle buttons (like a numpad), I would like to achieve:
when user 'turns on' a button by clicking it, 'turns off' all other buttons
I have tried to add an onClickListener inside the view holder class, and call notifyDataSetChanged() such that onBindViewHolder(final ViewHolder holder, int position) is called and then change the state of the button.
But it doesn't work, the button clicked does not change its state.
ViewHolder
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
ToggleButton button;
String id;
public ViewHolder(View itemView) {
super(itemView);
button = (ToggleButton) itemView.findViewById(R.id.toggle);
button.setOnclickListener(this);
}
#Override
public void onClick(View view) {
((ToggleButton) view).setChecked(true);
int pos = getAdapterPosition();
if (pos != RecyclerView.NO_POSITION) {
selected = pos; //store the position of the user clicked button
notifyDataSetChanged();
}
}
onBindViewHolder() in my adpater class
public void onBindViewHolder(final ViewHolder holder, int position) {
String data = mData.get(position);
holder.id = data;
holder.button.setText(data);
holder.button.setTextOn(data);
holder.button.setTextOff(data);
if (position != selected) { //if the button is not the user chosen one
if (holder.button.isChecked()) { // and it's in 'ON' state
holder.button.toggle(); // toggle it to switch 'OFF'
}
}

There are a couple of things you are doing incorrectly. The ViewHolder should contain only views and not click handlers or strings etc. (The clue is in the name). You add the handlers and manipulate the views when you bind the ViewHolder to your data class.
From what I can determine, you want a simple list of toggle buttons. When one button is on, the others should turn off. The test adapter I created for you below demonstrates that.
Ideally, you would avoid notifyDataSetChanged and use the row based versions but as each time the toggle is pressed, it affects every other row, you do not really have a choice in this use case unless you keep track of the selected row.
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.VH> {
public static class MyData {
public boolean Selected = false;
public String Text;
public MyData(String text) {
Text = text;
}
}
public List<MyData> items = new ArrayList<>();
public TestAdapter() {
this.items.add(new MyData("Item 1"));
this.items.add(new MyData("Item 2"));
this.items.add(new MyData("Item 3"));
}
#Override
public TestAdapter.VH onCreateViewHolder(ViewGroup parent, int viewType) {
return new VH((
LayoutInflater.from(parent.getContext())
.inflate(R.layout.test_layout, parent, false))
);
}
#Override
public void onBindViewHolder(TestAdapter.VH holder, int position) {
final MyData itm = items.get(position);
holder.button.setChecked(itm.Selected);
holder.text.setText(itm.Text);
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
for (MyData x: items){
x.Selected=false;
}
itm.Selected = true;
notifyDataSetChanged();
}
});
}
#Override
public int getItemCount() {
return items.size();
}
public class VH extends RecyclerView.ViewHolder {
ToggleButton button;
TextView text;
public VH(View itemView) {
super(itemView);
button = itemView.findViewById(R.id.toggle);
text = itemView.findViewById(R.id.text1);
}
}
}
test_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ToggleButton
android:id="#+id/toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

Try setting OnCheckedChangeListener instead of 'OnclickListener`
`button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// The toggle is enabled
} else {
// The toggle is disabled
}
}
});`
Android Doc : https://developer.android.com/guide/topics/ui/controls/togglebutton.html

set on click listener in the onBindView and In the onClick method,
first "turn off" all the toggle button by setting a parameter in the list model
and then "turn on" the selected one if it is not selected.
#Override
public void onClick(View view) {
// clear all model values to isSelected false in the list
}

Related

How to set OnClickListener to Expandable RecycleView

I want to set OnClickListeners to the items from my Expandable Recycleview. Each item from the Recycleview should have a button ( like this https://imgur.com/qlEJCkk : a + button to add tasks and an "x" button for each task to delete it)
I have tried to implement it from some other examples of onClickListeners but nothing worked so far
this is the ADAPTER:
public class ExpandableAdapter extends ExpandableRecyclerViewAdapter<RoutineViewHolder, TaskViewHolder> {
public ExpandableAdapter(List<? extends ExpandableGroup> groups) {
super(groups);
}
#Override
public RoutineViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.expandable_recyclerview_routine, parent, false);
return new RoutineViewHolder(v);
}
#Override
public TaskViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.expandable_recyclerview_task, parent, false);
return new TaskViewHolder(v);
}
#Override
public void onBindChildViewHolder(TaskViewHolder holder, int flatPosition, ExpandableGroup group, int childIndex) {
final Tasks tasks = (Tasks) group.getItems().get(childIndex);
holder.bind(tasks);
}
#Override
public void onBindGroupViewHolder(RoutineViewHolder holder, int flatPosition, ExpandableGroup group) {
final Routine routine = (Routine) group;
holder.bind(routine);
}
RoutineViewHolder:
import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder;
public class RoutineViewHolder extends GroupViewHolder implements View.OnClickListener {
private TextView mTextView;
public RoutineViewHolder(View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.exp_routine);
itemView.setOnClickListener(this);
}
public void bind(Routine routine){
mTextView.setText(routine.getTitle());
}
}
TaskViewHolder:
public class TaskViewHolder extends ChildViewHolder {
private TextView mTextView;
private CheckBox mCheckBox;
private Boolean checkVal;
public TaskViewHolder(View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.exp_task);
mCheckBox=itemView.findViewById(R.id.exp_task_checkbox);
}
public void bind(Tasks tasks) {
mTextView.setText(tasks.name);
checkVal=((tasks.checkBox==1)?Boolean.TRUE:Boolean.FALSE);
mCheckBox.setChecked(checkVal);
}
}
as you can see I have 2 ViewHolders : RoutineViewHolder and TaskViewHolder. I am very confused to where and how I should set the OnClickListener since I want it to behave different for the "Routines" and "Tasks" because they would have different buttons.
"Tasks" should have the + button to add tasks underneath it
and each task should have an X button to delete that specific task
the expandable recycleview is made out of 2 more of these "Tasks" cathegories.
I would prefer a solution where I can listen all these callbacks from some higher level (like Activity) where I can change data objects and refresh the RecyclerView keep things in sync based on callbacks. (This is eventually what you will need if you scale this.)
I implemented your code and modified a little to get the expected result.
For this solution:
I created an interface to get callbacks
On Add clicked on Routine
On Delete clicked on Task
On check status changed on Task
Made my activity to implement that interface and passed it to adapter.
Adapter passes it to ViewHolder
ViewHolder will invoke required function upon click.
In call backs:
For ADD: You can know which Routine was clicked
For Delete: You can know ParentRoutine, Child Index of Task and Task
For Check Change: You can know ParentRoutine, Child Index of Task, Task and New check status.
Code
1. Add new file ListActionListener.java
This is the interface.
public interface ListActionListener {
// Know add was clicked on given routine
void onAddTaskClicked(Routine routine);
// Know delete was clicked on given task.
void onDeleteTaskClicked(Routine routine, Tasks task, int index);
// Know checkbox clicked on given task (with new checked status)
void onTaskCheckChanged(Routine routine, Tasks task, int index, boolean checked);
}
2. Make your activity implement this interface. ExpandableListActivity.java
This is my sample activity that you see in the screenshots.
public class ExpandableListActivity extends AppCompatActivity implements ListActionListener{
ExpandableAdapter adapter;
RecyclerView recyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_expandable_list);
recyclerView = findViewById(R.id.recyclerView);
loadList();
}
private void loadList() {
List<Routine> routines = getDummyRoutineList();
adapter = new ExpandableAdapter(routines, this);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
private List<Routine> getDummyRoutineList() {
List<Routine> list = new ArrayList<Routine>();
Tasks rt1 = new Tasks("R1 Tasks1", 1);
Tasks rt2 = new Tasks("R1 Tasks2", 0);
Tasks rt3 = new Tasks("R1 Tasks3", 1);
Tasks rt4 = new Tasks("R1 Tasks4", 0);
Tasks rt5 = new Tasks("R1 Tasks5", 0);
List<Tasks> r1Tasks = new ArrayList<>();
r1Tasks.add(rt1);
r1Tasks.add(rt2);
r1Tasks.add(rt3);
r1Tasks.add(rt4);
r1Tasks.add(rt5);
Routine r1 = new Routine("Routine 1", r1Tasks);
Tasks r2t1 = new Tasks("R2 Tasks1", 1);
Tasks r2t2 = new Tasks("R2 Tasks2", 0);
Tasks r2t3 = new Tasks("R2 Tasks3", 1);
Tasks r2t4 = new Tasks("R2 Tasks4", 0);
Tasks r2t5 = new Tasks("R2 Tasks5", 1);
List<Tasks> r2Tasks = new ArrayList<>();
r2Tasks.add(r2t1);
r2Tasks.add(r2t2);
r2Tasks.add(r2t3);
r2Tasks.add(r2t4);
r2Tasks.add(r2t5);
Routine r2 = new Routine("Routine 2", r2Tasks);
list.add(r1);
list.add(r2);
return list;
}
#Override
public void onAddTaskClicked(Routine routine) {
Toast.makeText(this, "On Add Clicked", Toast.LENGTH_SHORT).show();
}
#Override
public void onDeleteTaskClicked(Routine routine, Tasks task, int index) {
Toast.makeText(this, "On Delete Clicked", Toast.LENGTH_SHORT).show();
}
#Override
public void onTaskCheckChanged(Routine routine, Tasks task, int index, boolean checked) {
Toast.makeText(this, "On Check changed:"+checked, Toast.LENGTH_SHORT).show();
}
}
3. Add "X" button to Task Row layout
This is my sample XML file, your XML may look different. Main thing is to add button for Delete.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp"
>
<Button
android:id="#+id/btn_delete"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="X"
/>
<CheckBox
android:id="#+id/exp_task_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
/>
<TextView
android:id="#+id/exp_task"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/btn_delete"
android:layout_toRightOf="#+id/exp_task_checkbox"
/>
</RelativeLayout>
4. Add "+" button to Routine Layout file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp"
>
<Button
android:id="#+id/btn_add"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="+"
/>
<TextView
android:id="#+id/exp_routine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="#+id/btn_delete"
android:layout_toRightOf="#+id/exp_task_checkbox"
/>
</RelativeLayout>
5. Update Adapter to accept a ListActionListener
public class ExpandableAdapter extends ExpandableRecyclerViewAdapter<RoutineViewHolder, TaskViewHolder> {
ListActionListener listActionListener;
public ExpandableAdapter(List<? extends ExpandableGroup> groups, ListActionListener listActionListener) {
super(groups);
this.listActionListener = listActionListener;
}
#Override
public RoutineViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.expandable_recyclerview_routine, parent, false);
return new RoutineViewHolder(v);
}
#Override
public TaskViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.expandable_recyclerview_task, parent, false);
return new TaskViewHolder(v);
}
#Override
public void onBindChildViewHolder(TaskViewHolder holder, int flatPosition, ExpandableGroup group, int childIndex) {
final Tasks tasks = (Tasks) group.getItems().get(childIndex);
holder.bind((Routine)group, childIndex, tasks, listActionListener);
}
#Override
public void onBindGroupViewHolder(RoutineViewHolder holder, int flatPosition, ExpandableGroup group) {
final Routine routine = (Routine) group;
holder.bind(routine, listActionListener);
}
}
6. Update TaskViewHolder.java
To accepte listener and invoke callback
public class TaskViewHolder extends ChildViewHolder {
private TextView mTextView;
private CheckBox mCheckBox;
private Boolean checkVal;
private Button btnDelete;
public TaskViewHolder(View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.exp_task);
mCheckBox=itemView.findViewById(R.id.exp_task_checkbox);
btnDelete = itemView.findViewById(R.id.btn_delete);
}
public void bind(final Routine parentRoutine, final int childIndex, final Tasks tasks, final ListActionListener listActionListener) {
mTextView.setText(tasks.name);
checkVal=((tasks.checkBox==1)?Boolean.TRUE:Boolean.FALSE);
mCheckBox.setChecked(checkVal);
//add delete button click
btnDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
listActionListener.onDeleteTaskClicked(parentRoutine, tasks, childIndex);
}
});
mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
//to avoid initial call back
if(checked != checkVal) {
listActionListener.onTaskCheckChanged(parentRoutine, tasks, childIndex, checked);
checkVal = checked;
}
}
});
}
}
7. Update RoutineViewHolder.java
To accepte listener and invoke callback.
public class RoutineViewHolder extends GroupViewHolder implements View.OnClickListener {
private TextView mTextView;
private Button btnAdd;
public RoutineViewHolder(View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.exp_routine);
btnAdd = itemView.findViewById(R.id.btn_add);
itemView.setOnClickListener(this);
}
public void bind(final Routine routine, final ListActionListener listActionListener) {
mTextView.setText(routine.getTitle());
btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
listActionListener.onAddTaskClicked(routine);
}
});
}
}
Bingo.... Run the code... :)
Have you tried to call setChildClickListener of your ExpandableAdapter object from your activity?
Have a look at this:
ExpandableAdapter adapter=new ExpandableAdapter(myExpandableGroupList);
adapter.setChildClickListener(new OnCheckChildClickListener() {
#Override
public void onCheckChildCLick(View v, boolean checked, CheckedExpandableGroup group,int childIndex) {
}
});
I hope this helps you.

How to fix RecyclerView OnClick gap display bug?

I'm setting up a RecyclerView who contains mulptiple ImageButtons,
Each ImageButton has a OnClick action (launch an Activity)
My problem is, the OnClick action is not working on ImageButtons, but in a little space between each ImageButton (if I want to click on 1st button, I have to click on the space between 1st and 2nd Button)
I've tried changing image size
RecyclerView Adapter:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.recycler_view, parent, false);
return new ViewHolder(view);
}
// binds the data to the ImageButton in each row
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
int currentCategory = mData.get(position).getImage();
holder.categoriesButton.setImageResource(currentCategory);
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ImageButton categoriesButton;
ViewHolder(View itemView) {
super(itemView);
categoriesButton = itemView.findViewById(R.id.image_Button_Category_id);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
// convenience method for getting data at click position
public int getItem(int id) {
return mData.get(id).getImage();
}
// allows clicks events to be caught
public void setClickListener(ItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}
RecyclerLayout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageButton
android:id="#+id/image_Button_Category_id"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
And this is in my MainActivity:
private void setRecyclerViewOn(ArrayList<Category> list) {
RecyclerView recyclerView = findViewById(R.id.recyclerView_id);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
adapter = new RecyclerViewAdapter(this, list);
adapter.setClickListener(this);
recyclerView.setAdapter(adapter);
}
#Override
public void onItemClick(View view, int position) {
Toast.makeText(this, "You clicked " + adapter.getItem(position) + " on row number " + position, Toast.LENGTH_SHORT).show();
startActivity(createIntent(position));
}
Expected results: Click listener have to be on each ImageButton
Try adding on click listener on onBindView() instead
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
int currentCategory = mData.get(position).getImage();
holder.categoriesButton.setImageResource(currentCategory);
holder.vategoriesButton.setOnClickListener(...)
}

Android - How to change the visibility of a widget of and show it in all items in recyclerView?

I have a RecyclerView in my activity and I want to add Multiple selection to the RecyclerView, and this is my Adapter:
public class RingtoneItemAdabter extends RecyclerView.Adapter<RingtoneItemAdabter.ViewHolder> {
private Context context;
private Activity activity;
private List<RingtoneItem> list;
private boolean selectionMode = false;
private List<RingtoneItem> selectedItems = new ArrayList<>();
public class ViewHolder extends RecyclerView.ViewHolder {
public Switch aSwitch;
public TextView textView;
public View view;
public CheckBox checkBox;
public ViewHolder(View view) {
super(view);
this.view = view;
aSwitch = (Switch) view.findViewById(R.id.ringtone_state_switch);
textView = (TextView) view.findViewById(R.id.ringtone_title_textview);
checkBox = (CheckBox) view.findViewById(R.id.selection_checkbox);
}
}
public RingtoneItemAdabter(Context context, Activity activity, List<RingtoneItem> list) {
this.context = context;
this.activity = activity;
this.list = list;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.recyclerview_item, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
final RingtoneItem item = list.get(position);
holder.checkBox.setVisibility(selectionMode ? View.VISIBLE : View.GONE);
holder.textView.setText(item.getTitle());
holder.aSwitch.setChecked(item.isActive());
holder.textView.setTextColor(item.isActive() ? Color.BLACK : Color.GRAY);
holder.aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
holder.textView.setTextColor(Color.BLACK);
item.setActive(true);
writeToFile();
} else {
holder.textView.setTextColor(Color.GRAY);
item.setActive(false);
writeToFile();
}
}
});
holder.view.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
if(selectionMode){
return false;
}else {
startSelecting();
holder.checkBox.setChecked(true);
return true;
}
}
});
holder.view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(selectionMode){
holder.checkBox.setChecked(!holder.checkBox.isChecked());
}
}
});
holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
selectedItems.add(item);
}else {
selectedItems.remove(item);
}
if(selectedItems.size() == 0){
stopSelecting();
}
}
});
}
#Override
public int getItemCount() { ... }
private void writeToFile() { ... }
private void startSelecting(){
selectionMode = true;
notifyDataSetChanged();
}
private void stopSelecting(){
selectionMode = false;
notifyDataSetChanged();
}
}
and this is the checkbox tag in recyclerview_item:
<CheckBox
android:id="#+id/selection_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
so I want the startSelection method to show the checkbox in every item, and the stopSelection method to hide the checkbox in every item.
EDIT:
It's looks like I got a problem, the problem is now when I click and hold on an item the checkboxes appear and the checkbox of the item I long clicked must get checked, and if I unchecked every box, the checkboxed should disappear,this is how the app should work, but the when I tried it, it's didn't work, when I long click an item the checkboxes appear in some cases another checkbox get checked, but in another cases no one of the checkboxes get checked, and when I uncheck every checkbox, the checkboxes stay there.
I have an idea about why the checkboxed don't disappear, because for example if I long clicked the fourth item, the checkboxes will appear, but rather than the fourth item which should get checked the first item will get checked, but the fourth item is the one which will added to selectedItems list, so if I unchecked the first box nothing will happen because it's not stored in the list, but if I checked the fourth item, then another fourth item will added to the list, and if I unchecked it, one of the two copies of the fourth item will removed, but one will stay.
check the code above.
I know my explain is very bad, and my English isn't very good, but hope you understand :)
Define a boolean variable for show/hide checkbox state and in onBindViewHolder set visibility of your checkbox related to that variable
boolean showCheckboxes = false;
public void startSelection() {
showCheckboxes = true;
notifyDataSetChanged();
}
public void stopSelection() {
showCheckboxes = false;
notifyDataSetChanged();
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
final RingtoneItem item = list.get(position);
holder.checkBox.setVisibility(showCheckboxes ? View.VISIBLE: View.GONE);
/*
...
*/
}

Select only one image through Recyclerview

Out of the recyclerview item, I need to select only one and make its background white and deselect the previous one.
public class viewHolder extends RecyclerView.ViewHolder {
TextView periodCategoryName;
ImageView periodCategoryPhoto;
View dottedLine;
public viewHolder(View itemView) {
super(itemView);
periodCategoryPhoto=(ImageView) itemView.findViewById(R.id.mood_icon);
periodCategoryPhoto.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i("Get Adapeter position ",Integer.toString(getAdapterPosition()));
selectedPosition=getAdapterPosition();
notifyItemRangeChanged(0,periodListPhoto.size()-1);
}
});
}
}
#Override
public void onBindViewHolder(final PeriodListAdaptor.viewHolder holder, final int position) {
holder.periodCategoryPhoto.setImageResource(periodListPhoto.get(position));
//only one period item is highlighted
if(selectedPosition == position){
//already selected item
Log.i("Selected position is ",Integer.toString(selectedPosition));
holder.periodCategoryPhoto.setBackground(ContextCompat.getDrawable(mContext,R.drawable.item_selected));
}else{
//do nothing
}
}
I am getting the strange behavior. Sometimes previously selected item is deselected upon selecting a new item. Sometimes both are selected.
ViewHolders are being reused, which means that you should take care of handling both cases in your onBindViewHolder():
if (selectedPosition == position) {
...
} else {
holder.periodCategoryPhoto.setBackground(defaultBagroundColor);
}

How to implement Android Recyclerview item click AND checkbox item click?

I had the following holder that worked, i.e. I was able to check/uncheck the checkbox. (The checkbox is part of the RecyclerView card):
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.bindData(numbers.get(position));
//in some cases, it will prevent unwanted situations
holder.checkbox.setOnCheckedChangeListener(null);
//if true, your checkbox will be selected, else unselected
holder.checkbox.setChecked(numbers.get(position).isSelected());
holder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
numbers.get(holder.getAdapterPosition()).setSelected(isChecked);
}
});
}
I wanted to implement click of the item on RecyclerView. So, I took from this solution - please check: https://stackoverflow.com/a/26196831/4013399
However, now, the click of the entire card item works, but the checkbox can not be checked/unchecked. How to solve this?
Please help. Thanks.
Update
I made changes - however, "HERE-1,2 and 3" lines are never entered.
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
Log.e("I AM ", "HERE-0");
holder.bindData(numbers.get(position));
final RelativeLayout rlyItem = holder.rlyItem;
final CheckBox checkbox = holder.checkbox;
rlyItem.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
mListener.onItemClicked(holder.getLayoutPosition());
Log.e("I AM ", "HERE-1");
}
});
checkbox.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
if (((CheckBox) view).isChecked()) {
checkbox.setChecked(false);
Log.e("I AM ", "HERE-2");
} else {
checkbox.setChecked(true);
Log.e("I AM ", "HERE-3");
}
// Inform to Activity or the Fragment where the RecyclerView reside.
mListener.onItemCheckBoxChecked(((CheckBox) view).isChecked(), holder.getLayoutPosition());
}
});
//in some cases, it will prevent unwanted situations
holder.checkbox.setOnCheckedChangeListener(null);
//if true, your checkbox will be selected, else unselected
holder.checkbox.setChecked(numbers.get(position).isSelected());
checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
numbers.get(holder.getAdapterPosition()).setSelected(isChecked);
Log.e("I AM ", "HERE-4");
//Log.e(Integer.toString(holder.getAdapterPosition()), " IS CHECKED");
}
});
}
Simply call performClick() for your checkbox on click of your adapter inflated view in adapter class.
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.checkbox.performClick();
}
});
First You add the below property in your checkbox
android:clickable="false"
android:focusable="false"
android:longClickable="false"
Below is my itemclick
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
Utils.hideSoftKeyboard(getActivity());
cityAdapter.itemSelected(position);
}
}));
Add below method in your adapter :
public void itemSelected(final int position) {
if (townModelArrayList != null && !townModelArrayList.isEmpty()) {
townModelArrayList.get(position).setSelected(!townModelArrayList.get(position).isSelected());
notifyDataSetChanged();
}
}
I wrote a library to handle android recycler view item click event. You can find whole tutorial in https://github.com/ChathuraHettiarachchi/RecycleClick
RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemClickListener(new RecycleClick.OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// YOUR CODE
}
});
or to handle item long press you can use
RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemLongClickListener(new RecycleClick.OnItemLongClickListener() {
#Override
public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
// YOUR CODE
return true;
}
});
to handle checkbox click you need to setCheckChangeListner on your adapter
I solved this problem by removing the whole class referenced in the question link (https://stackoverflow.com/a/26196831/4013399). Instead I implemented an onClick listener only for the checkbox. And for the whole card view item click?, you may ask. For that, I simply implemented another onClick listener for a layout that contained my textbox.
Also, to save the state of the checkboxes a simple setting and getting of key,value pairs like (checkbox position, "1") (where 1 indicates checked) and simultaneously updating this in the SharedPreferences worked.
TL; DR
To implement RecyclerView item click and a clickable view in RecyclerView item, You need to define listener for each of them.
First, you need to define the layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<RelativeLayout
android:id="#+id/item__rly"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:selectableItemBackground"
android:clickable="true"
>
<CheckBox
android:id="#+id/item_cbx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sample CheckBox" />
</RelativeLayout>
</android.support.v7.widget.CardView>
From the above xml code, you can find that the RelativeLayout using:
android:background="?android:selectableItemBackground"
android:clickable="true"
This is to make the item having an animation when clicked.
Second, you need to define click listener interface within your Adapter, something like this:
public class YourAdapter extends
RecyclerView.Adapter<YourAdapter.ViewHolder> {
private List<YourData> mDatas;
// Define listener member variable
private static OnRecyclerViewItemClickListener mListener;
// Define the listener interface
public interface OnRecyclerViewItemClickListener {
void onItemClicked(int position);
void onItemCheckBoxChecked(boolean isChecked, int position);
}
// Define the method that allows the parent activity or fragment to define the listener.
public void setOnRecyclerViewItemClickListener(OnRecyclerViewItemClickListener listener) {
this.mListener = listener;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
RelativeLayout rlyItem;
CheckBox cbxSample;
// We also create a constructor that accepts the entire item row
// and does the view lookups to find each subview
public ViewHolder(View itemView) {
// Stores the itemView in a public final member variable that can be used
// to access the context from any ViewHolder instance.
super(itemView);
rlyItem = (RelativeLayout) itemView.findViewById(R.id.item__rly);
cbxSample = (CheckBox) itemView.findViewById(R.id.item_cbx);
}
}
// Usually involves inflating a layout from XML and returning the holder
#Override public ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
// Inflate the custom layout
View viewHolder = inflater.inflate(R.layout.item_layout, parent, false);
// Return a new holder instance
return new ViewHolder(viewHolder);
}
// Involves populating data into the item through holder
#Override public void onBindViewHolder(final ViewHolder viewHolder, int position) {
// Get the data model based on position
final YourData data = mDatas.get(position);
final RelativeLayout rlyItem = viewHolder.rlyItem;
CheckBox cbxSample = viewHolder.cbxSample;
rlyItem.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
mListener.onItemClicked(viewHolder.getLayoutPosition());
}
});
cbxSample.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View view) {
if (((CheckBox) v).isChecked()) {
cbxSample.setChecked(false);
} else {
cbxSample.setChecked(true);
}
// Inform to Activity or the Fragment where the RecyclerView reside.
mListener.onItemCheckBoxChecked(((CheckBox) v).isChecked(), viewHolder.getLayoutPosition());
}
});
}
#Override public int getItemCount() {
return mDatas.size();
}
Then, in your Activity or Fragment where RecyclerView code reside, you need to set the listener:
yourAdapter.setOnRecyclerViewItemClickListener(new YourAdapter.OnRecyclerViewItemClickListener() {
#Override
public void onItemClicked(int position) {
// Do something when item clicked.
}
#Override
public void onItemCheckBoxChecked(boolean isChecked, int position) {
// Do something when check box check state change.
}
});
Addition:
To hold the the state of item in your ReyclerView, you need to use SparseBooleanArray

Categories