在我的 Android 项目中,我有一个ListView
包含SwitchCompat
项目的行(小部件的 AppCompat Switch
)。
当我滚动到列表中并使用视图调用getView(...)
方法时,就会出现我的问题。我重新定义了正确的状态,但动画是可见的。MyAdapter
recycled
Switch
在这种情况下有防止动画的解决方案吗?
在我的 Android 项目中,我有一个ListView
包含SwitchCompat
项目的行(小部件的 AppCompat Switch
)。
当我滚动到列表中并使用视图调用getView(...)
方法时,就会出现我的问题。我重新定义了正确的状态,但动画是可见的。MyAdapter
recycled
Switch
在这种情况下有防止动画的解决方案吗?
调用jumpDrawablesToCurrentState()
跳过动画
switchCompat.setChecked(true);
switchCompat.jumpDrawablesToCurrentState();
我终于找到了一个解决方案,但似乎不是很干净:
ViewGroup viewGroup = (ViewGroup) view; // the recycled view
viewGroup.removeView(switch);
switch.setChecked(states[index]);
viewGroup.addView(switch);
如果有更好的解决方案,请分享。
我有同样的问题,我设法用一些最小的反射来解决它。
用法:
要在没有动画的情况下更改开关状态,请setChecked(boolean checked, boolean animate)
为 animate 参数调用带有 false 的方法。如果在调用此方法时开关已经开始动画,则动画将停止并且开关跳转到所需位置。
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();
}
}
}
}
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.** { *; }
如果您使用 Android 数据绑定,则可能会出现在列表中播放动画的问题。
要解决它,binding.executePendingBindings()
请在设置数据后运行方法——它将刷新当前帧中组件的绑定状态,并且不会等待下一个到来。
你可能已经猜到了——下一帧是动画
使用 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)
对于 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 = 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)