在 Android Lollipop 中扩展 Preference 类 = 丢失动画

Posted

技术标签:

【中文标题】在 Android Lollipop 中扩展 Preference 类 = 丢失动画【英文标题】:Extending Preference classes in Android Lollipop = losing animation 【发布时间】:2015-07-31 22:20:46 【问题描述】:

仅用于在 android Lollipop 上扩展 CheckBoxPreference 或 SwitchPreference,小部件(复选框或开关)将不再有动画。

我想扩展 SwitchPreference 以强制 api

我正在使用新的AppCompatPreferenceActivity 和appcompat-v7:22.1.1,但这似乎不会影响开关。

问题是只扩展这些类,而不添加任何自定义布局或小部件资源布局,动画就消失了。

我知道我可以编写我的preference.xml 的两个实例(在内部值-v21 上)并且它会工作......但我想知道为什么会发生这种情况,如果有人知道没有两个偏好的解决方案.xml。

代码示例:

public class SwitchPreference extends android.preference.SwitchPreference 

    public SwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
    

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) 
        super(context, attrs, defStyleAttr, defStyleRes);
    

    public SwitchPreference(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    public SwitchPreference(Context context) 
        super(context);
    

CheckBoxPreference 或相同,然后使用:

<com.my.package.SwitchPreference />

将使棒棒糖设备中的动画消失。

--

我为 SwitchPreference(我可以使用 CheckBoxPreference)尝试的另一件事是提供一个具有默认 id 的布局,但 @android:id/switchWidgetis not public 而 @android:id/checkbox 是。我也知道我可以使用 <CheckBoxPreference /> 并提供一个实际上是 SwitchCompat 的小部件布局,但我想避免这种情况(混淆名称)。

【问题讨论】:

您找到解决方案了吗? 不,我没有。实际上,我在 Lollipop 之前有一个材料偏好库:github.com/ferrannp/material-preferences,它唯一的错误是这个:github.com/ferrannp/material-preferences/issues/9 code.google.com/p/android/issues/detail?id=85392 是相关的。 有一个很好的答案here 对我有用。 【参考方案1】:

我设法像这样修复它,并且动画在它直接进入没有动画的状态之前工作:

修复:

CustomSwitchCompat.class

public class CustomSwitchCompat extends SwitchCompat 

    public CustomSwitchCompat(Context context) 
        super(context);
    

    public CustomSwitchCompat(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    public CustomSwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
    

    @Override
    public boolean isShown() 
        return getVisibility() == VISIBLE;
    


在你的布局中这样做:preference_switch_layout.xml

<com.example.CustomSwitchCompat
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@android:id/checkbox"
    android:layout_
    android:layout_
    android:background="@null"
    android:clickable="false"
    android:focusable="false"
    app:switchMinWidth="55dp"/>

并在您的preference.xml 中执行以下操作:

<CheckBoxPreference
   android:defaultValue="false"
   android:key=""
   android:widgetLayout="@layout/preference_switch_layout"
   android:summary=""
   android:title="" />

【讨论】:

【参考方案2】:

有时从类扩展并不是最好的解决方案。为了避免丢失动画,您可以改为 Compose 它,我的意思是创建一个具有 SwitchPreference 字段变量的类并将新逻辑应用于它。这就像一个包装。这对我有用。

【讨论】:

【参考方案3】:

看来我找到了解决您问题的方法。

详尽的解释

SwitchCompat中,当切换开关时,它会在播放动画之前测试一些功能:getWindowToken() != null &amp;&amp; ViewCompat.isLaidOut(this) &amp;&amp; isShown()

完整方法:

@Override
public void setChecked(boolean checked) 
    super.setChecked(checked);
    // Calling the super method may result in setChecked() getting called
    // recursively with a different value, so load the REAL value...
    checked = isChecked();
    if (getWindowToken() != null && ViewCompat.isLaidOut(this) && isShown()) 
        animateThumbToCheckedState(checked);
     else 
        // Immediately move the thumb to the new position.
        cancelPositionAnimator();
        setThumbPosition(checked ? 1 : 0);
    

通过使用扩展SwitchCompat 的自定义视图,我发现isShown() 总是返回false,因为while 的第三次迭代parent == null

public boolean isShown() 
    View current = this;
    //noinspection ConstantConditions
    do 
        if ((current.mViewFlags & VISIBILITY_MASK) != VISIBLE) 
            return false;
        
        ViewParent parent = current.mParent;
        if (parent == null) 
            return false; // We are not attached to the view root
        
        if (!(parent instanceof View)) 
            return true;
        
        current = (View) parent;
     while (current != null);

    return false;

有趣的是,第三个父级是 Preference 中传递给 getView(View convertView, ViewGroup parent) 的第二个属性,这意味着 PreferenceGroupAdapter 没有将父级传递给它自己的 getView() .为什么会发生这种情况以及为什么只会发生在自定义首选项类中,我不知道。

出于测试目的,我使用 CheckBoxPreferenceSwitchCompat 作为widgetLayout,我也没有看到动画。

修复

现在解决问题:只需让您自己的视图扩展 SwitchCompat,然后像这样覆盖您的 isShown()

@Override
public boolean isShown() 
    return getVisibility() == VISIBLE;

将此SwitchView 用于您的widgetLayout 样式,并且动画再次起作用:D

样式:

<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
    …
    <item name="android:checkBoxPreferenceStyle">@style/Preference.SwitchView</item>
    …
</style>

<style name="Preference.SwitchView">
    <item name="android:widgetLayout">@layout/preference_switch_view</item>
</style>

小部件布局:

<de.Maxr1998.example.preference.SwitchView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/checkbox"
    android:layout_
    android:layout_
    android:background="@null"
    android:clickable="false"
    android:focusable="false" />

【讨论】:

这听起来很有希望!但是,我确实在我的库(github.com/ferrannp/material-preferences)中尝试了它,但我没有让它工作(在棒棒糖之前没有动画)。你可能有一个工作回购吗? .顺便说一句,我在前棒棒糖上没有得到动画,无论是在开关上还是在复选框上。 动画适用于 Lollipop 及之前的版本。我没有为此设置存储库,无论如何它只是此处列出的代码。顺便说一句,这个解决方案也有一个错误,因为有时会在滚动列表时播放动画。此外,虽然显示正确的颜色,但有时开关的拇指在错误的一侧。..【参考方案4】:

公共类 SwitchPreference 扩展 android.preference.SwitchPreference

public SwitchPreference(Context context) 
    this(context, null);


public SwitchPreference(Context context, AttributeSet attrs) 
    this(context, attrs, android.R.attr.checkBoxPreferenceStyle);


public SwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) 
    this(context, attrs, defStyleAttr, 0);


public SwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) 
    super(context, attrs, defStyleAttr, defStyleRes);

    try 
        Field canRecycleLayoutField = Preference.class.getDeclaredField("mCanRecycleLayout");
        canRecycleLayoutField.setAccessible(true);
        canRecycleLayoutField.setBoolean(this, true);
     catch (NoSuchFieldException e) 
        e.printStackTrace();
     catch (IllegalAccessException e) 
        e.printStackTrace();
    

【讨论】:

您好,您能详细说明一下吗?我试过了,我收到了 java.lang.NoSuchFieldException: mCanRecycleLayout 错误。

以上是关于在 Android Lollipop 中扩展 Preference 类 = 丢失动画的主要内容,如果未能解决你的问题,请参考以下文章

HttpResponseCache 在 Android Lollipop 中不起作用

Android Studio - 如何在 webview Lollipop 中上传文件 (Android 5.0)

在 Android 5.0 Lollipop 上不支持 calendarViewShown

在 Lollipop 崩溃前使用 android vector Drawables

Android onChange 事件未在 contentObserver 中触发,用于 android 5 (Lollipop) 上的 chrome 历史记录

应用程序如何检测状态栏颜色(Android 5.0 Lollipop)?