onCheckedChanged called automatically
AndroidAndroid RecyclerviewOncheckedchangedAndroid SwitchSwitchcompatAndroid Problem Overview
I have a switch in a recyclerview and data is displayed in the recyclerview after retrieving data from DB. When the recyclerview is opened I read DB and if a field in DB is "Y" I enable the switch or else I disable the switch. Now the problem is along with it the onCheckedchanged listener is also called, I want the onCheckedChanged to be called only when user sets the switch manually.
On opening the recyclerview below is executed:
holder.enabledisable.setChecked(messengerRecord.get_is_valid().equalsIgnoreCase("Y"));
ViewHolder class:
public class viewHolder extends RecyclerView.ViewHolder implements CompoundButton.OnCheckedChangeListener{
public SwitchCompat enabledisable;
public viewHolder(View v) {
enabledisable = (SwitchCompat) v.findViewById(R.id.enabledisable);
enabledisable.setOnCheckedChangeListener(this);
...................................
...................................
OncheckedChanged method which is called when the recyclerView is just opened:
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.v("ranjith","called oncheckedchanged");
MessengerRecord rec;
rec = dbHelper.getRecord(descview.getText().toString());
switch (buttonView.getId()) {
case R.id.enabledisable:
if (isChecked) {
rec.set_is_valid("Y");
dbHelper.updateRecord(rec);
}
}
In Layout file:
<android.support.v7.widget.SwitchCompat
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:id="@+id/enabledisable"
android:layout_alignRight="@+id/textview_to"
android:layout_alignEnd="@+id/textview_to"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
Android Solutions
Solution 1 - Android
It's weird all of us have this problem but not official Google answer to this simple problem.
The MOST simple it's to check:
buttonView.isPressed()
If true, means the user clicked the view.
No global variables are needed.
Solution 2 - Android
I've ended up using this subclass of SwitchCompat to avoid this issue. This way I don't need boilerplate code where I'm using this class.
Whenever you need to change the checked without firing the listener, use setCheckedSilent
instead of setChecked
:
import android.content.Context;
import android.os.Parcelable;
import android.support.v7.widget.SwitchCompat;
import android.util.AttributeSet;
/**
* Created by emanuel on 30/5/16.
*/
public class Switch extends SwitchCompat {
private OnCheckedChangeListener listener;
public Switch(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
this.listener = listener;
super.setOnCheckedChangeListener(listener);
}
@Override
public void onRestoreInstanceState(Parcelable state) {
super.setOnCheckedChangeListener(null);
super.onRestoreInstanceState(state);
super.setOnCheckedChangeListener(listener);
}
public void setCheckedSilent(boolean checked) {
super.setOnCheckedChangeListener(null);
super.setChecked(checked);
super.setOnCheckedChangeListener(listener);
}
}
onRestoreInstanceState
was triggering the listening also, when set in the onViewCreated
method and you are going back to a previous fragment.
Hope it works for you!
Solution 3 - Android
Try this
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.isPressed()) {
... //returns true, if user clicks the switch button
}}
Solution 4 - Android
Since RecyclerView
is recycling views, a previously attached OnCheckedChangeListener
can be triggered when setting checked value for the Switch
of the new item.
When binding new data to an item:
switch.setOnCheckedChangeListener(null) // remove any existing listener from recycled view
switch.isChecked = [true/false] // will no longer trigger any callback to listener
switch.setOnCheckedChangeListener { btnView, isChecked ->
// do exiting stuff
}
Solution 5 - Android
This works for me:
boolean selected = preferences.isChecked();
yourCheckBox.setOnCheckedChangeListener(new
CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (buttonView.isPressed()) {
preferences.setChecked(isChecked);
} else {
yourCheckBox.setChecked(selected);
}
}
});
Solution 6 - Android
use a global boolean variable and when read a data from DB set it "true" and after check(if) in onCheckChange set it "false" again. in first of onCheckChange method check if this variable is false execute codes else return(default value of this variable must be false ) ;
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(!isSourceDB){
Log.v("ranjith","called oncheckedchanged");
MessengerRecord rec;
rec = dbHelper.getRecord(descview.getText().toString());
switch (buttonView.getId()) {
case R.id.enabledisable:
if (isChecked) {
rec.set_is_valid("Y");
dbHelper.updateRecord(rec);
}
}//end if
isSourceDB = false; }// end oncheckedchange
Solution 7 - Android
I faced the same problem inside ViewPager screen for fragments. When we switch between fragments, onCheckedChanged Listener is called again and again.
If anyone still looking for this problem, please try it.
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.isInTouchMode()) {
... //Your code will come here.
}
}
Solution 8 - Android
Kotlin Developers:
checkboxXYZ.setOnCheckedChangeListener { btnView, isChecked ->
if (btnView.isPressed) {
}
}
Solution 9 - Android
If you are changing state in adapter then
holder.rawManageDriverLayoutBinding.rawManageDriverSwitch.setOnCheckedChangeListener(null);
before setOnCheckedChangeListener
.
Solution 10 - Android
Only solution is to use the method isPressed() in the listener