Change Switch state without animation

前端 未结 7 1908
醉酒成梦
醉酒成梦 2020-12-09 07:54

In my Android project, I have a ListView with rows containing SwitchCompat items (AppCompat for Switch widget).

My problem occ

相关标签:
7条回答
  • 2020-12-09 08:15

    Call jumpDrawablesToCurrentState() to skip the animation

    switchCompat.setChecked(true);
    switchCompat.jumpDrawablesToCurrentState();
    
    0 讨论(0)
  • 2020-12-09 08:15

    I had the same problem and I managed to solved it using some minimal reflection.

    Usage:

    To change the switch state without animation call the setChecked(boolean checked, boolean animate) method with false for the animate parameter. If the switch already is animating at the moment this method is being called the animation will be stopped and the switch jumps to the desired position.

    SwitchCompatFix.java

    import android.content.Context;
    import android.support.v7.widget.SwitchCompat;
    import android.util.AttributeSet;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     * Work around for: http://stackoverflow.com/questions/27139262/change-switch-state-without-animation
     * Possible fix for bug 101107: https://code.google.com/p/android/issues/detail?id=101107
     *
     * Version 0.2
     * @author Rolf Smit
     */
    public class SwitchCompatFix extends SwitchCompat {
    
        public SwitchCompatFix(Context context) {
            super(context);
            initHack();
        }
    
        public SwitchCompatFix(Context context, AttributeSet attrs) {
            super(context, attrs);
            initHack();
        }
    
        public SwitchCompatFix(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initHack();
        }
    
        private Method methodCancelPositionAnimator = null;
        private Method methodSetThumbPosition = null;
    
        private void initHack(){
            try {
                methodCancelPositionAnimator = SwitchCompat.class.getDeclaredMethod("cancelPositionAnimator");
                methodSetThumbPosition = SwitchCompat.class.getDeclaredMethod("setThumbPosition", float.class);
                methodCancelPositionAnimator.setAccessible(true);
                methodSetThumbPosition.setAccessible(true);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    
        public void setChecked(boolean checked, boolean animate){
            // Java does not support super.super.xxx calls, a call to the SwitchCompat default setChecked method is needed.
            super.setChecked(checked);
            if(!animate) {
    
                // See original SwitchCompat source:
                // Calling the super method may result in setChecked() getting called
                // recursively with a different value, so load the REAL value...
                checked = isChecked();
    
                // Cancel any running animations (started by super.setChecked()) and immediately move the thumb to the new position
                try {
                    if(methodCancelPositionAnimator != null && methodSetThumbPosition != null) {
                        methodCancelPositionAnimator.invoke(this);
                        methodSetThumbPosition.invoke(this, checked ? 1 : 0);
                    }
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    Note for proguard users:

    Because this method uses reflection an additional proguard rule might be needed (if not yet present).

    -keep class android.support.v7.widget.SwitchCompat {
        private void cancelPositionAnimator();
        private void setThumbPosition(float);
    }
    

    This additional rule is not needed when you're using one of the following proguard rules (or similar ones):

    -keep class android.support.v7.widget.** { *; }
    -keep class android.support.v7.** { *; }
    
    0 讨论(0)
  • 2020-12-09 08:15

    In my case, I am using the new material library:

    implementation 'com.google.android.material:material:1.1.0-alpha07'
    

    and in the setChecked method of this class there is this condition:

    if (getWindowToken() != null && ViewCompat.isLaidOut(this))
    

    So what I did was to create a class that extends from this SwitchMaterial, and deal with "isLaidOut". The code is the next one (omitting constructors):

    class SwitchCustomView : SwitchMaterial {
    
      private var laidOutForAnimation = false
    
      fun setChecked(checked: Boolean, animate: Boolean) {
        if (!animate) {
          laidOutForAnimation = true
        }
        super.setChecked(checked)
        laidOutForAnimation = false
      }
    
      override fun isLaidOut(): Boolean {
        return if (laidOutForAnimation) {
          return false
        } else {
          super.isLaidOut()
        }
      }
    }
    

    Then just use this class in your xml and call programatically

    setChecked(checked: Boolean, animate: Boolean)
    
    0 讨论(0)
  • 2020-12-09 08:18

    For Kotlin developer:

    fun SwitchCompat.setCheckedWithoutAnimation(checked: Boolean) {
        val beforeVisibility = visibility
        visibility = View.INVISIBLE
        isChecked = checked
        visibility = beforeVisibility
    }
    

    And the usage:

    mySwitch.setCheckedWithoutAnimation(true)
    
    0 讨论(0)
  • 2020-12-09 08:21

    The issue in with animation playing in the list can be present if you use Android Databinding.

    To resolve it, run binding.executePendingBindings() method after you set data – it will refresh binding state for the component in current frame and will not wait for the next one to come.

    As you have probably guessed already – next frame is the animation

    0 讨论(0)
  • 2020-12-09 08:30

    Using SwitchCompat and DataBinding

    @BindingAdapter({"bind:checkedState"})
    public static void setCheckedState(SwitchCompat switchView, boolean checked) {
        int visibility = switchView.getVisibility();
        switchView.setVisibility(View.INVISIBLE);
        switchView.setChecked(checked);
        switchView.setVisibility(visibility);
    }
    

    Then in xml:

    <android.support.v7.widget.SwitchCompat
        android:id="@+id/my_switch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:checkedState="@{my_data.checked}"/>
    

    And don't forget to call executePendingBindings() (thanks AAverin)

    0 讨论(0)
提交回复
热议问题