更改没有动画的切换状态

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了更改没有动画的切换状态相关的知识,希望对你有一定的参考价值。

在我的android项目中,我有一个包含ListView项目的SwitchCompat(适用于Switch小部件的AppCompat)。

当我滚动到列表并且使用getView(...)视图调用MyAdapterrecycled方法时,我的问题就出现了。我重新定义了正确的Switch状态,但动画是可见的。

在这种情况下有一个防止动画的解决方案吗?

答案

调用jumpDrawablesToCurrentState()跳过动画

switchCompat.setChecked(true);
switchCompat.jumpDrawablesToCurrentState();
另一答案

我终于找到了一个解决方案,但似乎并不是很干净

ViewGroup viewGroup = (ViewGroup) view; // the recycled view
viewGroup.removeView(switch);
switch.setChecked(states[index]);
viewGroup.addView(switch);

如果存在更好的解决方案,请分享。

另一答案

我有同样的问题,我设法使用一些最小的反射解决它。

用法:

要在没有动画的情况下更改开关状态,请为animate参数调用带有false的setChecked(boolean checked, boolean animate)方法。如果此方法在调用此方法时动画已经动画,则动画将停止并且开关跳转到所需位置。

switch com pat fix.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();
            }
        }
    }
}

对于proguard用户的注意事项:

由于此方法使用反射,因此可能需要额外的proguard规则(如果尚未存在)。

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

当您使用以下proguard规则之一(或类似规则)时,不需要此附加规则:

-keep class android.support.v7.widget.** { *; }
-keep class android.support.v7.** { *; }
另一答案

使用SwitchCompat和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);
}

然后在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}"/>

别忘了打电话给executePendingBindings()(感谢AAverin

另一答案

如果您使用Android数据绑定,则可能会出现列表中播放动画的问题。

要解决此问题,请在设置数据后运行binding.executePendingBindings()方法 - 它将刷新当前帧中组件的绑定状态,并且不会等待下一个组件的绑定状态。

你可能已经猜到了 - 下一帧是动画

另一答案

对于Kotlin开发者:

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

用法:

mySwitch.setCheckedWithoutAnimation(true)
另一答案

就我而言,我正在使用新材料库:

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

在这个类的setChecked方法中有这样的条件:

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

所以我做的是创建一个从这个SwitchMaterial扩展的类,并处理“isLaidOut”。代码是下一个(省略构造函数):

class SwitchCustomView : SwitchMaterial {

  private var laidOutForAnimation: Boolean

  init {
    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()
    }
  }
}

然后在xml中使用此类并以编程方式调用

setChecked(checked: Boolean, animate: Boolean)

以上是关于更改没有动画的切换状态的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI视图状态更改使用withAnimation()方法无动画效果的解决

配置更改后片段丢失过渡动画

LayaBox---多状态---时间轴动画

LayaBox---多状态---时间轴动画

Unity笔记一些动画系统状态机细节

切换片段时如何维护子视图的状态?