Swiping the view pager fragment should not move the tabs - android

My program has a boolean variable name "isCorrect". I want, when isCorrect is false then the user should not able to open any other tab. (Either by swiping or by selecting tab). I tried to do this by below given logic but this cause the application to hang.
final boolean isCorrect=false;
tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
if(!isCorrect){
if(tab.getPosition()==1){
mViewPager.setCurrentItem(0);
}
}else{
mViewPager.setCurrentItem(1);
}
}

Define a custom ViewPager subclass. The class inherits from ViewPager and includes a new method called setSwipeable to control if swipe events are enabled or not. Make sure to change layout file.
public class LockableViewPager extends ViewPager {
private boolean swipeable;
public LockableViewPager(Context context) {
super(context);
}
public LockableViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
this.swipeable = true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (this.swipeable) {
return super.onTouchEvent(event);
}
return false;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.swipeable) {
return super.onInterceptTouchEvent(event);
}
return false;
}
public void setSwipeable(boolean swipeable) {
this.swipeable = swipeable;
}
}
When flag is false disable swipe.
if (!flag) {
mViewPager.setSwipeable(false);
} else {
mViewPager.setSwipeable(true);
}

Related

How to implement select all button to select all items in recyclerview using actionmode and selectiontracker?

I have this code in my MainActivity
recyclerView.setAdapter(customAdapter);
customAdapter.submitList(path_list);
selectionTracker = new SelectionTracker.Builder<>(
"my-selection-id",
recyclerView,
new ScrollKeyProvider(1, path_list),
new ScrollItemDetailsLookup(recyclerView),
StorageStrategy.createLongStorage()
)
.withOnItemActivatedListener(new OnItemActivatedListener<Long>() {
#Override
public boolean onItemActivated(#NonNull ItemDetailsLookup.ItemDetails<Long> item, #NonNull MotionEvent e) {
Log.d("TAG", "Selected ItemId: " + item.toString());
return true;
}
})
.withOnDragInitiatedListener(new OnDragInitiatedListener() {
#Override
public boolean onDragInitiated(#NonNull MotionEvent e) {
Log.d("TAG", "onDragInitiated");
return true;
}
}).build();
customAdapter.setSelectionTracker(selectionTracker);
selectionTracker.addObserver(new SelectionTracker.SelectionObserver() {
#Override
public void onItemStateChanged(#NonNull Object key, boolean selected) {
super.onItemStateChanged(key, selected);
}
#Override
public void onSelectionRefresh() {
super.onSelectionRefresh();
}
#Override
public void onSelectionChanged() {
super.onSelectionChanged();
if (selectionTracker.hasSelection() && actionMode == null) {
actionMode = startSupportActionMode(new ActionModeController(ScrollActivity.this, selectionTracker));
actionMode.getMenu().findItem(R.id.action_item_count).setTitle("" + selectionTracker.getSelection().size());
} else if (!selectionTracker.hasSelection() && actionMode != null) {
actionMode.finish();
actionMode = null;
} else {
actionMode.getMenu().findItem(R.id.action_item_count).setTitle("" + selectionTracker.getSelection().size());
}
Iterator<String> itemIterable = selectionTracker.getSelection().iterator();
while (itemIterable.hasNext()) {
Log.i("TAG", itemIterable.next());
}
}
#Override
public void onSelectionRestored() {
super.onSelectionRestored();
}
});
This is my ActionMode callback code
public class ActionModeController implements ActionMode.Callback {
private final Context context;
private final SelectionTracker selectionTracker;
public ActionModeController(Context context, SelectionTracker selectionTracker) {
this.context = context;
this.selectionTracker = selectionTracker;
}
#Override
public boolean onCreateActionMode(androidx.appcompat.view.ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.action_menu, menu);
return true;
}
#Override
public boolean onPrepareActionMode(androidx.appcompat.view.ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(androidx.appcompat.view.ActionMode mode, MenuItem item) {
if(item.getItemId()==R.id.action_clear){
if (selectionTracker.hasSelection()){
selectionTracker.clearSelection();
}
}
else if(item.getItemId()==R.id.action_select_all){
// **THIS IS PLACE WHERE I NEED HELP TO ENTER CODE FOR SELECT ALL FUNCTIONALITY**
}
return false;
}
#Override
public void onDestroyActionMode(androidx.appcompat.view.ActionMode mode) {
selectionTracker.clearSelection();
}
}
What should I do in select all section in onActionItemClicked for selecting all items in Recyclerview using SelectionTracker?
You can see for the clear option in onActionItemClicked, I have used functions of selectionTracker to clear all the selected items. I am expecting similar solution for select all option too.
Kotlin version using PagingDataAdapter - can be reworked replacing the snapshot to items in your implementation.
var itemsArray = arrayListOf<Long>()
adapter?.snapshot()?.items?.forEach {
if (!selectionTracker.isSelected(it.id.toLong()))
itemsArray.add(it.id.toLong())
}
selectionTracker?.setItemsSelected(itemsArray.asIterable(), true)

How to distinguish for EditText's between pasting into or setting per setText()?

I have an EditText what I populate via
editText.setText(content)
The reason this is an EditText and not a TextView is because I also want to paste stuff (later when user is operating the app) in it or manually type in it, if applicable.
But I have to
reset a flag if editText set via setText()
and nothing if pasted by user
How can I distinguish how a EditText was populated? addTextChangedListener()'s callbacks are triggered in each case.
You can set Listener Class:
public interface GoEditTextListener {
void onUpdate();
}
Custom Edittext
public class GoEditText extends EditText
{
ArrayList<GoEditTextListener> listeners;
public GoEditText(Context context)
{
super(context);
listeners = new ArrayList<>();
}
public GoEditText(Context context, AttributeSet attrs)
{
super(context, attrs);
listeners = new ArrayList<>();
}
public GoEditText(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
listeners = new ArrayList<>();
}
public void addListener(GoEditTextListener listener) {
try {
listeners.add(listener);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
/**
* Here you can catch paste, copy and cut events
*/
#Override
public boolean onTextContextMenuItem(int id) {
boolean consumed = super.onTextContextMenuItem(id);
switch (id){
case android.R.id.cut:
onTextCut();
break;
case android.R.id.paste:
onTextPaste();
break;
case android.R.id.copy:
onTextCopy();
}
return consumed;
}
public void onTextCut(){
}
public void onTextCopy(){
}
/**
* adding listener for Paste for example
*/
public void onTextPaste(){
for (GoEditTextListener listener : listeners) {
listener.onUpdate();
}
}
}
xml
<com.yourname.project.GoEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/editText1"/>
Code
private GoEditText editText1;
editText1 = (GoEditText) findViewById(R.id.editText1);
editText1.addListener(new GoEditTextListener() {
#Override
public void onUpdate() {
//here do what you want when text Pasted
}
});
Simply extend EditText, include the flag, and override setText:
public class MyEditText extends EditText {
boolean fromSetText;
#Override
public void setText(String text) {
super.setText(text);
fromSetText = true;
}
}
You can define your own setters/getters and constructors based on your requirements.

SwipeRefreshLayout detect swipe down event before onRefresh() start

I want to detect when users are pulling down for refresh
(refresh line in under ActionBar is starting expand to its UI width).
I want to replace ActionBar with message ("Swipe down to Refresh") but,
I don't know which event should I use to call my function.
You can extend SwipeRefreshLayout, override onTouchEvent, and make your change on the ACTION_DOWN event, ie:
public class MySwipeRefreshLayout extends SwipeRefreshLayout {
private Runnable onActionDown, onActionUp;
public MySwipeRefreshLayout(Context context, AttributeSet attributeSet) {
super(context,attributeSet);
}
public void setOnActionDown(Runnable onActionDown) {
this.onActionDown = onActionDown;
}
public void setOnActionUp(Runnable onActionUp) {
this.onActionUp = onActionUp;
}
#Override
public boolean onTouchEvent (MotionEvent ev) {
if (ev.getAction() == ev.ACTION_DOWN) {
if (onActionDown != null) {
onActionDown.run();
}
} else if (ev.getAction() == ev.ACTION_UP) {
if (onActionUp != null) {
onActionUp.run();
}
}
return super.onTouchEvent(ev);
}
:
:
}
Make sure you use the extended class in your layout. Then, in your view, you can pass a Runnable to setOnActionDown to update the actionbar or whatever else you want....

Change Checkbox value without triggering onCheckChanged

I have setOnCheckedChangeListener implemented for my checkbox
Is there a way I can call
checkbox.setChecked(false);
without triggering the onCheckedChanged
No, you can't do it. The onCheckedChanged method is called directly from setChecked. What you can do is the following:
mCheck.setOnCheckedChangeListener (null);
mCheck.setChecked (false);
mCheck.setOnCheckedChangeListener (mListener);
See the source of CheckBox, and the implementation of setChecked:
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
// Avoid infinite recursions if setChecked() is called from a listener
if (mBroadcasting) {
return;
}
mBroadcasting = true;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
}
if (mOnCheckedChangeWidgetListener != null) {
mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
}
mBroadcasting = false;
}
}
Add this code inside OnCheckedChangeListener:
if(!compoundButton.isPressed()) {
return;
}
This will help us to figure out weather checkBox state was changed programmatically or by user action.
Another possible way to achieve this is by using a custom CheckBox , which will let you choose if you want the listener to be called or not :
public class CheckBox extends AppCompatCheckBox {
private OnCheckedChangeListener mListener;
public CheckBox(final Context context) {
super(context);
}
public CheckBox(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public CheckBox(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setOnCheckedChangeListener(final OnCheckedChangeListener listener) {
mListener = listener;
super.setOnCheckedChangeListener(listener);
}
public void setChecked(final boolean checked, final boolean alsoNotify) {
if (!alsoNotify) {
super.setOnCheckedChangeListener(null);
super.setChecked(checked);
super.setOnCheckedChangeListener(mListener);
return;
}
super.setChecked(checked);
}
public void toggle(boolean alsoNotify) {
if (!alsoNotify) {
super.setOnCheckedChangeListener(null);
super.toggle();
super.setOnCheckedChangeListener(mListener);
return;
}
super.toggle();
}
}
Kotlin version, if you prefer:
class CheckBox #JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : AppCompatCheckBox(context, attrs, defStyleAttr) {
private var listener: CompoundButton.OnCheckedChangeListener? = null
override fun setOnCheckedChangeListener(listener: CompoundButton.OnCheckedChangeListener?) {
this.listener = listener
super.setOnCheckedChangeListener(listener)
}
fun setChecked(checked: Boolean, alsoNotify: Boolean) {
if (!alsoNotify) {
super.setOnCheckedChangeListener(null)
super.setChecked(checked)
super.setOnCheckedChangeListener(listener)
return
}
super.setChecked(checked)
}
fun toggle(alsoNotify: Boolean) {
if (!alsoNotify) {
super.setOnCheckedChangeListener(null)
super.toggle()
super.setOnCheckedChangeListener(listener)
return
}
super.toggle()
}
}
sample usage:
checkBox.setChecked(true,false);
Now also available on my repository:
https://github.com/AndroidDeveloperLB/CommonUtils
For anyone that stumbles across this, one simpler way to do this is to just use a tag on the checkbox and then check that tag on its listener (code is in Kotlin):
checkBox.tag = false
checkBox.setOnCheckedChangeListener{ buttonView, isChecked ->
if(checkBox.tag != true) {
// Do some stuff
} else {
checkBox.tag = false
}
Then when accessing just set the tag to true before you set the isChecked to true when you want to ignore the value change:
checkBox.tag = true
checkBox.isChecked = true
You could also map the tag to a key by using the alternative setTag method that requires a key if you were worried about understandability. But if its all contained to a single class a few comment strings will be more than enough to explain whats happening.
you use simply setonclickListener , it will works fine and this is very simple method, thanks :)
Is very simple, you just check isPressed inside setOnCheckedChangeListener
Kotlin
switch.setOnCheckedChangeListener { buttonView, isChecked ->
when {
buttonView.isPressed -> {
foo(isChecked)
}
}
Using Kotlin's extensions with #Shade answer :
fun CompoundButton.setCustomChecked(value: Boolean,listener: CompoundButton.OnCheckedChangeListener) {
setOnCheckedChangeListener(null)
isChecked = value
setOnCheckedChangeListener(listener)
}
You could use this SafeCheckBox class as your checkbox :
public class SafeCheckBox extends AppCompatCheckBox implements CompoundButton.OnCheckedChangeListener {
private OnSafeCheckedListener onSafeCheckedListener;
private int mIgnoreListener = CALL_LISTENER;
public static final int IGNORE = 0;
public static final int CALL_LISTENER = 1;
#Retention(RetentionPolicy.SOURCE)
#IntDef({IGNORE, CALL_LISTENER})
public #interface ListenerMode {
}
public SafeCheckBox(Context context) {
super(context);
init(context);
}
public SafeCheckBox(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SafeCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* #param checkState change state of the checkbox to
* #param mIgnoreListener true to ignore the listener else listener will be notified
*/
public void setSafeCheck(boolean checkState, #ListenerMode int mIgnoreListener) {
if (isChecked() == checkState) return; //already in the same state no need to fire listener.
if (onSafeCheckedListener != null) { // this to avoid a bug if the user listens for the event after using this method and in that case he will miss first check
this.mIgnoreListener = mIgnoreListener;
} else {
this.mIgnoreListener = CALL_LISTENER;
}
setChecked(checkState);
}
private void init(Context context) {
setOnCheckedChangeListener(this);
}
public OnSafeCheckedListener getOnSafeCheckedListener() {
return onSafeCheckedListener;
}
public void setOnSafeCheckedListener(OnSafeCheckedListener onSafeCheckedListener) {
this.onSafeCheckedListener = onSafeCheckedListener;
}
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (onSafeCheckedListener != null)
onSafeCheckedListener.onAlwaysCalledListener(buttonView, isChecked);// this has to be called before onCheckedChange
if (onSafeCheckedListener != null && (mIgnoreListener == CALL_LISTENER)) {
onSafeCheckedListener.onCheckedChanged(buttonView, isChecked);
}
mIgnoreListener = CALL_LISTENER;
}
/**
* Listener that will be called when you want it to be called.
* On checked change listeners are called even when the setElementChecked is called from code. :(
*/
public interface OnSafeCheckedListener extends OnCheckedChangeListener {
void onAlwaysCalledListener(CompoundButton buttonView, boolean isChecked);
}
}
Then you could call :-
setSafeCheck(true,ListenerMode.IGNORE);// OnCheckedChange listener will not be notified
Set null to changeListener before check radio button. You can set listener again after check radio button.
radioGroup.setOnCheckedChangeListener(null);
radioGroup.check(R.id.radioButton);
radioGroup.setOnCheckedChangeListener(new
RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup radioGroup, #IdRes int i) {
}
});
This is a simple solution I used:
Define a custom listener:
class CompoundButtonListener implements CompoundButton.OnCheckedChangeListener {
boolean enabled = false;
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
boolean isEnabled() {
return enabled;
}
}
Initialization:
CompoundButtonListener checkBoxListener = new CompoundButtonListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
if (isEnabled()) {
// Your code goes here
}
}
};
myCheckBox.setOnCheckedChangeListener(checkBoxListener);
Usage:
checkBoxListener.disable();
// Some logic based on which you will modify CheckBox state
// Example: myCheckBox.setChecked(true)
checkBoxListener.enable();
My interpretation which i think is the easiest
May be helpful)
public class ProgrammableSwitchCompat extends SwitchCompat {
public boolean isCheckedProgrammatically = false;
public ProgrammableSwitchCompat(final Context context) {
super(context);
}
public ProgrammableSwitchCompat(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public ProgrammableSwitchCompat(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setChecked(boolean checked) {
isCheckedProgrammatically = false;
super.setChecked(checked);
}
public void setCheckedProgrammatically(boolean checked) {
isCheckedProgrammatically = true;
super.setChecked(checked);
}
}
use it
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean on) {
if (((ProgrammableSwitchCompat) compoundButton).isCheckedProgrammatically) {
return;
}
//...
((ProgrammableSwitchCompat) compoundButton).setCheckedProgrammatically(true);
//...
((ProgrammableSwitchCompat) compoundButton).setCheckedProgrammatically(false);
//...
}
use will trigger setChecked(boolean) function
that is all
KOTLIN
class MyCheckBox #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.switchStyle
) : AppCompatCheckBox(context, attrs, defStyleAttr) {
var programmatically = false
override fun setChecked(checked: Boolean) {
programmatically = false
super.setChecked(checked)
}
fun setCheckedProgrammatically(checked: Boolean) {
programmatically = true
super.setChecked(checked)
}
}
How about this.
Try to use Tag in View
mCheck.setTag("ignore");
mCheck.setChecked(true);
mCheck.setTag(null);
and
switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean selected) {
//If switch has a tag, ignore below
if(compoundButton.getTag() != null)
return;
if (selected) {
// do something
} else {
// do something else
}
}
});
Try this one should work for you! You can use this with firebase also!
For get firebase data! Use this!
databaseReference.child(user.getPhoneNumber()).child("Reqs").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
SharedPreferences prefs = mContext.getSharedPreferences("uinfo", MODE_PRIVATE);
String pno = prefs.getString("username", "No name defined");
if(dataSnapshot.child(pno).getValue(String.class).equals("acc")){
holder.acc.setChecked(true);
}else{
holder.acc.setChecked(false);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w("dfs", "loadPost:onCancelled", databaseError.toException());
// ...
}
});
After that when user do something!
holder.acc.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked) {
if(buttonView.isPressed()) {
//your code
}
}
else {
if(buttonView.isPressed()) {
//your code
}
}
}
});
I found all the above answers way too complicated. Why not just create your own flag with a simple boolean?
Just use a simple flag system with a boolean. Create boolean noListener. Whenever you want to turn your switch on/off without running any code (in this example, represented as runListenerCode(), simply set noListener=true before calling switch.setChecked(false/true)
switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean selected) {
if (!noListener) { //If we want to run our code like usual
runListenerCode();
} else { //If we simply want the switch to turn off
noListener = false;
}
});
Very simple solution using simple flags. At the end, we set noListener=false once again so that our code continues to work. Hope this helps!
Here's a version of the tag technique that is very easy to use.
Usage:
// Pass true to enable bypassing of the listener
button.setOnCheckedChangedListener(true) { _, isChecked ->
// your usual code
}
// Use extension function to set the value and bypass the listener
button.setCheckedSilently(true)
It's done with a couple of utility extension functions:
inline fun CompoundButton.setOnCheckedChangeListener(canBypass: Boolean, crossinline listener: (CompoundButton, Boolean) -> Unit) {
if (canBypass) {
setOnCheckedChangeListener { view, isChecked ->
if (view.tag != ListenerBypass) {
listener(view, isChecked)
}
}
} else {
setOnCheckedChangeListener { view, isChecked -> listener(view, isChecked) }
}
}
fun CompoundButton.setCheckedSilently(isChecked: Boolean) {
val previousTag = tag
tag = ListenerBypass
this.isChecked = isChecked
tag = previousTag
}
object ListenerBypass
I used a ReentrantLock, and lock it whenever I'm setting isChecked:
Kotlin:
// lock when isChecked is being set programmatically
val isBeingProgrammaticallySet = ReentrantLock()
// set isChecked programmatically
isBeingProgrammaticallySet.withLock()
{
checkbox.isChecked = true
}
// do something only when preference is modified by user
checkbox.setOnCheckedChangeListener()
{
_,isChecked ->
if (isBeingProgrammaticallySet.isHeldByCurrentThread.not())
{
// do it
}
}
isPressed() checked once in onCreate().
add checkbox.isPressed() after your onCheckChangeListener to check every time your check box change.
I guess using reflection is the only way. Something like this:
CheckBox cb = (CheckBox) findViewById(R.id.checkBox1);
try {
Field field = CompoundButton.class.getDeclaredField("mChecked");
field.setAccessible(true);
field.set(cb, cb.isChecked());
cb.refreshDrawableState();
cb.invalidate();
} catch (Exception e) {
e.printStackTrace();
}
My solution written in java based on #Chris answer:
chkParent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(buttonView.getTag() != null){
buttonView.setTag(null);
return;
}
if(isChecked){
chkChild.setTag(true);
chkChild.setChecked(false);
}
else{
chkParent.setChecked(true);
}
}
});
chkChild.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(buttonView.getTag() != null){
buttonView.setTag(null);
return;
}
if(isChecked){
chkParent.setTag(true);
chkParent.setChecked(false);
}
else{
chkChild.setChecked(true);
}
}
});
2 checkboxes and always one will be checked (one be must checked initially though). Setting tag to true blocks onCheckedChanged listener.
I didn't really want to be having to pass the listener in each time we set checked changed, nor using enabled as a way of determining whether we should set the value (what happens in the case we have the switch disabled already when setting the value?)
Instead I'm making use of tags with an id and a couple of extension methods you can call:
fun CompoundButton.setOnCheckedWithoutCallingChangeListener(
listener: (view: CompoundButton, checked: Boolean) -> Unit
) {
setOnCheckedChangeListener { view, checked ->
if (view.getTag(R.id.compound_button_checked_changed_listener_disabled) != true) {
listener(view, checked)
}
}
this.setTag(R.id.compound_button_enabled_checked_change_supported, true)
}
fun CompoundButton.setCheckedWithoutCallingListener(checked: Boolean) {
check(this.getTag(R.id.compound_button_enabled_checked_change_supported) == true) {
"Must set listener using `setOnCheckedWithoutCallingChangeListener` to call this method"
}
setTag(R.id.compound_button_checked_changed_listener_disabled, true)
isChecked = checked
setTag(R.id.compound_button_checked_changed_listener_disabled, false)
}
Now you can call setCheckedWithoutCallingListener(bool) and it will enforce the correct listener usage.
You can also still call setChecked(bool) to fire the listener if you still need it
I think this could resolve your problem.
Extension function
fun CheckBox.setSilentCheck(isCheck:Boolean,listener:CompoundButton.OnCheckedChangeListener) {
setOnCheckedChangeListener(null)
isChecked = isCheck
setOnCheckedChangeListener(listener) }

How can I distinguish whether Switch,Checkbox Value is changed by user or programmatically (including by retention)?

setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// How to check whether the checkbox/switch has been checked
// by user or it has been checked programatically ?
if (isNotSetByUser())
return;
handleSetbyUser();
}
});
How to implement method isNotSetByUser()?
Answer 2:
A very simple answer:
Use on OnClickListener instead of OnCheckedChangeListener
someCheckBox.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
// you might keep a reference to the CheckBox to avoid this class cast
boolean checked = ((CheckBox)v).isChecked();
setSomeBoolean(checked);
}
});
Now you only pick up click events and don't have to worry about programmatic changes.
Answer 1:
I have created a wrapper class (see Decorator Pattern) which handles this problem in an encapsulated way:
public class BetterCheckBox extends CheckBox {
private CompoundButton.OnCheckedChangeListener myListener = null;
private CheckBox myCheckBox;
public BetterCheckBox(Context context) {
super(context);
}
public BetterCheckBox(Context context, CheckBox checkBox) {
this(context);
this.myCheckBox = checkBox;
}
// assorted constructors here...
#Override
public void setOnCheckedChangeListener(
CompoundButton.OnCheckedChangeListener listener){
if(listener != null) {
this.myListener = listener;
}
myCheckBox.setOnCheckedChangeListener(listener);
}
public void silentlySetChecked(boolean checked){
toggleListener(false);
myCheckBox.setChecked(checked);
toggleListener(true);
}
private void toggleListener(boolean on){
if(on) {
this.setOnCheckedChangeListener(myListener);
}
else {
this.setOnCheckedChangeListener(null);
}
}
}
CheckBox can still be declared the same in XML, but use this when initializing your GUI in code:
BetterCheckBox myCheckBox;
// later...
myCheckBox = new BetterCheckBox(context,
(CheckBox) view.findViewById(R.id.my_check_box));
If you want to set checked from code without triggering the listener, call myCheckBox.silentlySetChecked(someBoolean) instead of setChecked.
Maybe You can check isShown()? If TRUE - than it's user. Works for me.
setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (myCheckBox.isShown()) {// makes sure that this is shown first and user has clicked/dragged it
doSometing();
}
}
});
Inside the onCheckedChanged() just check whether the user has actually checked/unchecked the radio button and then do the stuff accordingly as follows:
mMySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.isPressed()) {
// User has clicked check box
}
else
{
//triggered due to programmatic assignment using 'setChecked()' method.
}
}
});
You can remove the listener before changing it programatically and add it again, as answered in the following SO post:
https://stackoverflow.com/a/14147300/1666070
theCheck.setOnCheckedChangeListener(null);
theCheck.setChecked(false);
theCheck.setOnCheckedChangeListener(toggleButtonChangeListener);
Try extending CheckBox. Something like that (not complete example):
public MyCheckBox extends CheckBox {
private Boolean isCheckedProgramatically = false;
public void setChecked(Boolean checked) {
isCheckedProgramatically = true;
super.setChecked(checked);
}
public Boolean isNotSetByUser() {
return isCheckedProgramatically;
}
}
Try NinjaSwitch:
Just call setChecked(boolean, true) to change the switch's checked state without detected!
public class NinjaSwitch extends SwitchCompat {
private OnCheckedChangeListener mCheckedChangeListener;
public NinjaSwitch(Context context) {
super(context);
}
public NinjaSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NinjaSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
super.setOnCheckedChangeListener(listener);
mCheckedChangeListener = listener;
}
/**
* <p>Changes the checked state of this button.</p>
*
* #param checked true to check the button, false to uncheck it
* #param isNinja true to change the state like a Ninja, makes no one knows about the change!
*/
public void setChecked(boolean checked, boolean isNinja) {
if (isNinja) {
super.setOnCheckedChangeListener(null);
}
setChecked(checked);
if (isNinja) {
super.setOnCheckedChangeListener(mCheckedChangeListener);
}
}
}
There is another simple solution that works pretty well. Example is for Switch.
public class BetterSwitch extends Switch {
//Constructors here...
private boolean mUserTriggered;
// Use it in listener to check that listener is triggered by the user.
public boolean isUserTriggered() {
return mUserTriggered;
}
// Override this method to handle the case where user drags the switch
#Override
public boolean onTouchEvent(MotionEvent ev) {
boolean result;
mUserTriggered = true;
result = super.onTouchEvent(ev);
mUserTriggered = false;
return result;
}
// Override this method to handle the case where user clicks the switch
#Override
public boolean performClick() {
boolean result;
mUserTriggered = true;
result = super.performClick();
mUserTriggered = false;
return result;
}
}
This should be enough :
SwitchCompact.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (buttonView.isPressed()) {
if (!isChecked) {
//do something
} else {
// do something else
}
}
});
Interesting question. To my knowledge, once you're in the listener, you can't detect what action has triggered the listener, the context is not enough. Unless you use an external boolean value as an indicator.
When you check the box "programmatically", set a boolean value before to indicate it was done programmatically. Something like:
private boolean boxWasCheckedProgrammatically = false;
....
// Programmatic change:
boxWasCheckedProgrammatically = true;
checkBoxe.setChecked(true)
And in your listener, don't forget to reset the state of the checkbox:
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isNotSetByUser()) {
resetBoxCheckSource();
return;
}
doSometing();
}
// in your activity:
public boolean isNotSetByUser() {
return boxWasCheckedProgrammatically;
}
public void resetBoxCheckedSource() {
this.boxWasCheckedProgrammatically = false;
}
If OnClickListener is already set and shouldn't be overwritten, use !buttonView.isPressed() as isNotSetByUser().
Otherwise the best variant is to use OnClickListener instead of OnCheckedChangeListener.
The accepted answer could be simplified a bit to not maintain a reference to the original checkbox. This makes it so we can use the SilentSwitchCompat (or SilentCheckboxCompat if you prefer) directly in the XML. I also made it so you can set the OnCheckedChangeListener to null if you desire to do so.
public class SilentSwitchCompat extends SwitchCompat {
private OnCheckedChangeListener listener = null;
public SilentSwitchCompat(Context context) {
super(context);
}
public SilentSwitchCompat(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
super.setOnCheckedChangeListener(listener);
this.listener = listener;
}
/**
* Check the {#link SilentSwitchCompat}, without calling the {#code onCheckChangeListener}.
*
* #param checked whether this {#link SilentSwitchCompat} should be checked or not.
*/
public void silentlySetChecked(boolean checked) {
OnCheckedChangeListener tmpListener = listener;
setOnCheckedChangeListener(null);
setChecked(checked);
setOnCheckedChangeListener(tmpListener);
}
}
You can then use this directly in your XML like so (Note: you will need the whole package name):
<com.my.package.name.SilentCheckBox
android:id="#+id/my_check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOff="#string/disabled"
android:textOn="#string/enabled"/>
Then you can check the box silently by calling:
SilentCheckBox mySilentCheckBox = (SilentCheckBox) findViewById(R.id.my_check_box)
mySilentCheckBox.silentlySetChecked(someBoolean)
Here is my implementation
Java Code for Custom Switch :
public class CustomSwitch extends SwitchCompat {
private OnCheckedChangeListener mListener = null;
public CustomSwitch(Context context) {
super(context);
}
public CustomSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setOnCheckedChangeListener(#Nullable OnCheckedChangeListener listener) {
if(listener != null && this.mListener != listener) {
this.mListener = listener;
}
super.setOnCheckedChangeListener(listener);
}
public void setCheckedSilently(boolean checked){
this.setOnCheckedChangeListener(null);
this.setChecked(checked);
this.setOnCheckedChangeListener(mListener);
}}
Equivalent Kotlin Code :
class CustomSwitch : SwitchCompat {
private var mListener: CompoundButton.OnCheckedChangeListener? = null
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
override fun setOnCheckedChangeListener(#Nullable listener: CompoundButton.OnCheckedChangeListener?) {
if (listener != null && this.mListener != listener) {
this.mListener = listener
}
super.setOnCheckedChangeListener(listener)
}
fun setCheckedSilently(checked: Boolean) {
this.setOnCheckedChangeListener(null)
this.isChecked = checked
this.setOnCheckedChangeListener(mListener)
}}
To change switch state without triggering listener use :
swSelection.setCheckedSilently(contact.isSelected)
You can monitor state change as normally by :
swSelection.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Do something
}
});
In Kotlin :
swSelection.setOnCheckedChangeListener{buttonView, isChecked -> run {
contact.isSelected = isChecked
}}
My variant with Kotlin extension functions:
fun CheckBox.setCheckedSilently(isChecked: Boolean, onCheckedChangeListener: CompoundButton.OnCheckedChangeListener) {
if (isChecked == this.isChecked) return
this.setOnCheckedChangeListener(null)
this.isChecked = isChecked
this.setOnCheckedChangeListener(onCheckedChangeListener)
}
...unfortunately we need to pass onCheckedChangeListener every time because CheckBox class has not getter for mOnCheckedChangeListener field((
Usage:
checkbox.setCheckedSilently(true, myCheckboxListener)
Create a variable
boolean setByUser = false; // Initially it is set programmatically
private void notSetByUser(boolean value) {
setByUser = value;
}
// If user has changed it will be true, else false
private boolean isNotSetByUser() {
return setByUser;
}
In the application when you change it instead of the user, call notSetByUser(true) so it is not set by the user, else call notSetByUser(false) i.e. it is set by program.
Lastly, in your event listener, after calling isNotSetByUser(), make sure you again change it back to normal.
Call this method whenever you are handling that action either thru user or programmatically. Call the notSetByUser() with appropriate value.
If the view's tag isn't used, you can use it instead of extending the checkbox:
checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
if (buttonView.getTag() != null) {
buttonView.setTag(null);
return;
}
//handle the checking/unchecking
}
each time you call something that checks/unchecks the checkbox, also call this before checking/unchecking :
checkbox.setTag(true);
I have created extension with RxJava's PublishSubject, simple one. Reacts only on "OnClick" events.
/**
* Creates ClickListener and sends switch state on each click
*/
fun CompoundButton.onCheckChangedByUser(): PublishSubject<Boolean> {
val onCheckChangedByUser: PublishSubject<Boolean> = PublishSubject.create()
setOnClickListener {
onCheckChangedByUser.onNext(isChecked)
}
return onCheckChangedByUser
}

Categories

Resources