在应用 XML 属性之前恢复视图状态

Posted

技术标签:

【中文标题】在应用 XML 属性之前恢复视图状态【英文标题】:Restore state of view before applying XML attributes 【发布时间】:2017-09-13 13:01:14 【问题描述】:

我有一个自定义视图,假设这是它的代码:

public class CustomView extends View 

    boolean visible;
    boolean enabled;

    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) 
        super(context, attrs, defStyleAttr, defStyleRes);

        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomView, 0, 0);
        try 
            visible = a.getBoolean(R.styleable.CustomView_visible, true);
            enabled = a.getBoolean(R.styleable.CustomView_enabled, true);
         finally 
            a.recycle();
        

        // Apply XML attributes here
    

    @Override
    public Parcelable onSaveInstanceState() 
        // Save instance state
        Bundle bundle = new Bundle();
        bundle.putParcelable("superState", super.onSaveInstanceState());
        bundle.putBoolean("visible", visible);
        bundle.putBoolean("enabled", enabled);

        return bundle;
    

    @Override
    public void onRestoreInstanceState(Parcelable state) 
        // Restore instance state
        // This is called after constructor
        if (state instanceof Bundle) 
            Bundle bundle = (Bundle) state;
            visible = bundle.getBoolean("visible");
            enabled = bundle.getBoolean("enabled");

            state = bundle.getParcelable("superState");
        
        super.onRestoreInstanceState(state);
    

非常简单。我的自定义视图从 XML 读取属性并应用它们。这些属性会在配置更改时保存和恢复。

但如果我有两种不同的布局,例如两个不同的方向:

[layout-port/view.xml]
<CustomView
    custom:visible="true"
    custom:enabled="true"

[layout-land/view.xml]
<CustomView
    custom:visible="false"
    custom:enabled="false"

我的问题是,当更改设备方向时,视图状态被保存为可见并启用,但现在 XML 布局状态视图也不应该有。构造函数在 onRestoreInstanceState 之前被调用,并且 XML 属性被保存的状态覆盖。我不希望这样,XML 优先于保存的状态。

我做错了什么?解决这个问题的最佳方法是什么?

【问题讨论】:

将 xml 值存储在其他变量中,并在恢复后重新应用它们。您也可以不应用恢复,因此值将始终是 xml 中定义的值 @nandsito 这可能就是我最终要做的。我只是想也许有一种更直接的方法可以做到这一点,一种在解析 XML 后恢复状态的方法。我想我可以做的是将 AttributeSet 保存到一个变量中,然后在 onRestoreInstanteState 的末尾解析 XML。但是第一次创建视图时不会调用 onRestoreInstanteState。 android 解析 xml 并将其属性应用到视图构造函数中,因此 xml 始终在恢复状态之前被处理。如果要更改此顺序,则必须手动设置变量值 不要保存和恢复这两个属性。它们总体上反映了某些内容或数据或模型的状态。所以视图的状态应该在运行时设置。 @EugenPechanec 这就是我最终所做的。 【参考方案1】:

在您的情况下,您必须将当前方向与其他属性一起存储在Parcelable 中,并且仅在恢复的方向等于当前方向的情况下应用这些恢复的属性(即活动被操作系统破坏和恢复)。在您的情况下,我会使用 android:tag 来定义当前方向,如下所示:

[layout-port/view.xml]
<CustomView
    android:tag="port"
    custom:visible="true"
    custom:enabled="true"

[layout-land/view.xml]
<CustomView
    android:tag="land"
    custom:visible="false"
    custom:enabled="false"

然后你的自定义视图类会是这样的:

public class ScheduleView extends View 

    String orientation;
    boolean visible;
    boolean enabled;

    public ScheduleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) 
        super(context, attrs, defStyleAttr, defStyleRes);

        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomView, 0, 0);
        try 
            visible = a.getBoolean(R.styleable.CustomView_visible, true);
            enabled = a.getBoolean(R.styleable.CustomView_enabled, true);
         finally 
            a.recycle();
        

        orientation = (String) getTag();
    

    @Override
    public Parcelable onSaveInstanceState() 
        // Save instance state
        Bundle bundle = new Bundle();
        bundle.putParcelable("superState", super.onSaveInstanceState());
        bundle.putBoolean("visible", visible);
        bundle.putBoolean("enabled", enabled);
        bundle.putString("orientation", orientation);

        return bundle;
    

    @Override
    public void onRestoreInstanceState(Parcelable state) 
        // Restore instance state
        // This is called after constructor
        if (state instanceof Bundle) 
            Bundle bundle = (Bundle) state;

            String restoredOrientation = bundle.getString("orientation");
            if (restoredOrientation.equals(orientation)) 
                visible = bundle.getBoolean("visible");
                enabled = bundle.getBoolean("enabled");
            

            state = bundle.getParcelable("superState");
        
        super.onRestoreInstanceState(state);
    

尚未对其进行正确测试,但它应该可以工作。希望对您有所帮助。

【讨论】:

我想应用一些已保存的属性,那些不在 XML 中的属性。同样的事情也适用于 azizbekian 的回答。 对不起,我不明白你的问题。实际上,您可以在 onRestoreInstanceState 方法中的 if 块之外应用这些 xml 外属性。 onRestoreInstanceState 并不总是被称为这是问题所在。 好吧,那么我想你需要更好地解释你的问题。您说 saved 属性 - saved 我知道您想在调用 onRestoreInstanceState 之后应用这些属性。 【参考方案2】:

基本上,您需要将状态分为纵向和横向,这意味着您还必须保存特定的配置状态。

final boolean isPortrait = 
            getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
bundle.putBoolean("isPortrait", isPortrait);

那么当恢复状态时:

final boolean savedOrientation = bundle.getBoolean("isPortrait");
final boolean currentOrientation = 
            getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;

if (savedOrientation == currentOrientation) 
    // now retrieve saved values
 else 
    // do nothing, values are initialized in constructor

【讨论】:

以上是关于在应用 XML 属性之前恢复视图状态的主要内容,如果未能解决你的问题,请参考以下文章

重新启动后恢复视图状态

UIView 动画在转换到新视图之前快速恢复到原始状态

如何在恢复之前更新视图?

iOS 7状态栏在iPhone应用程序中恢复到iOS 6默认样式?

如何使用状态恢复来恢复视图控制器的子视图

编码一个 int 或 NSInteger 用于状态恢复