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

8

I have a custom view, let's say this is its code:

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
@nandsito 这可能是我最终要做的事情。我只是想也许有一种更直接的方法来做到这一点,一种在解析XML后恢复状态的方法。我认为我可以将AttributeSet保存到变量中,然后在onRestoreInstanteState的末尾解析XML。但是当视图首次创建时,onRestoreInstanteState不会被调用。 - Nicolas
Android解析XML并在视图构造函数中应用其属性,因此XML始终在恢复状态之前处理。如果要更改此顺序,您需要手动设置变量值。 - nandsito
不要保存和恢复这两个属性。它们反映了某些内容、数据或模型的状态。因此,视图的状态应在运行时设置。 - Eugen Pechanec
@EugenPechanec 那就是我最终做的。 - Nicolas
2个回答

0
在您的情况下,您需要将当前方向与其他属性一起存储在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的答案也是同样的情况。 - Nicolas
抱歉,我不明白你的问题。实际上,在onRestoreInstanceState方法中,您可以将那些非XML属性应用于if块之外。 - romtsn
onRestoreInstanceState 并不总是被调用,这就是问题所在。 - Nicolas
好的,那么我想你需要更好地解释你的问题。你说“saved attributes” - 从“saved”这个词中,我理解为你希望在onRestoreInstanceState被调用后应用这些属性。 - romtsn

0
基本上,你需要将横向和纵向的状态分开处理,这意味着你还必须保存特定的配置状态。
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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接