Восстановить состояние просмотра перед применением атрибутов XML

У меня есть собственное представление, допустим, это его код:

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 имеет приоритет над сохраненным состоянием.

Я что-то не так делаю? Как лучше всего решить эту проблему?


person Nicolas    schedule 17.04.2017    source источник
comment
сохраните значения xml в других переменных и повторно примените их после восстановления. Вы также можете не применять восстановление, поэтому значения всегда будут такими, как определено в xml.   -  person nandsito    schedule 22.04.2017
comment
@nandsito Наверное, этим я и займусь. Я просто подумал, может быть, есть более прямой способ сделать это, способ восстановить состояние после синтаксического анализа XML. Я думал, что могу сохранить AttributeSet в переменной, а затем проанализировать XML в конце onRestoreInstanteState. Но onRestoreInstanteState не вызывается при первом создании представления.   -  person Nicolas    schedule 23.04.2017
comment
android анализирует xml и применяет его атрибуты в конструкторе представления, поэтому xml всегда обрабатывается до состояния восстановления. Если вы хотите изменить этот порядок, вам придется вручную установить значения переменных.   -  person nandsito    schedule 23.04.2017
comment
Не сохраняйте и не восстанавливайте именно эти два атрибута. Они отражают состояние некоторого контента, части данных или модели в целом. Таким образом, состояние просмотра должно быть установлено во время выполнения.   -  person Eugen Pechanec    schedule 26.04.2017
comment
@EugenPechanec Вот чем я закончил.   -  person Nicolas    schedule 27.04.2017


Ответы (2)


В вашем случае вы должны сохранить текущую ориентацию в 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);
    }
}

Не тестировал должным образом, но он должен работать. Надеюсь, это будет полезно.

person romtsn    schedule 22.04.2017
comment
Я хочу применить некоторые сохраненные атрибуты, которых нет в XML. То же самое относится и к ответу Азизбекяна. - person Nicolas; 23.04.2017
comment
Извините, но я не понимаю вас. Фактически вы можете применить эти атрибуты вне xml вне блока if в методе onRestoreInstanceState. - person romtsn; 23.04.2017
comment
onRestoreInstanceState не всегда называется в этом проблема. - person Nicolas; 23.04.2017
comment
Что ж, тогда, думаю, тебе нужно лучше объяснить свою проблему. Вы сказали сохраненные атрибуты - по сохраненные. Я понимаю, что вы хотите применить эти атрибуты после вызова onRestoreInstanceState. - person romtsn; 23.04.2017

По сути, вам нужно разделить состояния на портретную и ландшафтную, что означает, что вам также нужно сохранить определенное состояние конфигурации.

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
}
person azizbekian    schedule 22.04.2017