Android CheckBox - восстановление состояния после поворота экрана - PullRequest
28 голосов
/ 25 марта 2010

Я столкнулся с некоторыми очень неожиданными (и невероятно разочаровывающими) функциями, пытаясь восстановить состояние списка CheckBox после поворота экрана. Я решил, что сначала попытаюсь дать текстовое объяснение без кода, на случай, если кто-то сможет найти решение без всех кровавых подробностей. Если кому-то понадобится дополнительная информация, я могу выложить код.

У меня есть прокручиваемый список сложных View s, которые содержат CheckBox es. Мне не удалось восстановить состояние этих флажков после поворота экрана. Я реализовал onSaveInstanceState и успешно перенес список выбранных флажков в метод onCreate. Это выполняется путем передачи long[] идентификаторов базы данных в Bundle.

В onCreate() Я проверяю Bundle для массива идентификаторов. Если массив есть, я использую его, чтобы определить, какие флажки проверять при построении списка. Я создал несколько методов тестирования и подтвердил, что флажки устанавливаются правильно, на основе массива id. В качестве последней проверки я проверяю состояния всех флажков в самом конце onCreate(). Все выглядит хорошо ... если я не поверну экран.

Когда я поворачиваю экран, происходит одно из двух: 1) Если выбрано любое количество флажков, кроме последнего, все флажки отключаются после поворота. 2) Если последний флажок установлен перед вращением, то все флажки отмечены после вращения.

Как я уже сказал, я проверяю состояние ящиков в самом конце моего onCreate(). Дело в том, что состояние ящиков в конце onCreate является правильным в зависимости от того, что я выбрал перед вращением. Однако состояние полей на экране не отражает это.

Кроме того, я внедрил setOnCheckChangedListener() каждого флажка и подтвердил, что состояние моих флажков изменяется после моего onCreate метода, возвращаемого.

Кто-нибудь имеет представление о том, что происходит? Почему состояние моих флажков изменится после возвращения моего метода onCreate?

Заранее спасибо за помощь. Я пытался размазать это в течение пары дней. После того, как я обнаружил, что мои флажки, видимо, менялись где-то за пределами моего собственного кода, я решил, что пришло время спросить.

Ответы [ 8 ]

48 голосов
/ 24 января 2013

У меня была похожая проблема. В приложении было несколько флажков на экране.
После поворота телефона приложение «вручную» установило значение для всех флажков.
Этот код был выполнен в onStart ().
Но на экране все флажки были установлены со значением 'последний флажок' на экране.
Android вызывал onRestoreInstanceState (..) и каким-то образом рассматривал все флажки как «один» [последний с экрана].

Решением было отключить «восстановление состояния экземпляра»:

<RadioButton
    ...
    android:saveEnabled="false" />
8 голосов
/ 04 апреля 2010

все. Похоже, я понял это. Состояние флажков изменяется в onRestoreInstanceState (Bundle). Этот метод вызывается после onCreate () (точнее, после onStart ()) и является еще одним местом, в котором Android рекомендует восстановить состояние.

Теперь я понятия не имею, почему мои флажки изменяются в onRestoreInstanceState, но, по крайней мере, я знаю, что именно здесь возникает проблема. Удивительно, но когда я переопределяю onRestoreInstanceState и абсолютно ничего не делаю (без вызова super.onRestoreInstanceState), вся деятельность работает отлично.

Если кто-нибудь может сказать мне , почему в этом методе изменяется выбранное состояние флажков, я очень хотел бы знать. На мой взгляд, это похоже на ошибку в самом коде Android.

2 голосов
/ 10 июня 2010

Если вы когда-нибудь найдете что-нибудь еще об этой проблеме, пожалуйста, дайте мне знать. По сути, я столкнулся с этой же проблемой, и сработало только переопределение onRestoreInstanceState(). Очень странно.

1 голос
/ 09 декабря 2011

Вы также можете использовать onSaveInstanceState(Bundle savedInstanceState) и onRestoreInstanceState(Bundle savedInstanceState)

Пример

@Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
  // EditTexts text
  savedInstanceState.putString("first_et",((EditText)findViewById(R.id.et_first)).getText().toString());
  // EditTexts text
  savedInstanceState.putInt("first_et_v", ((EditText)findViewById(R.id.et_first)).getVisibility());
  super.onSaveInstanceState(savedInstanceState);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState)
{
  super.onRestoreInstanceState(savedInstanceState);
  // EditTexts text
  ((EditText)findViewById(R.id.et_first)).setText(savedInstanceState.getString("first_et"));
  // EditText visibility
  ((EditText)findViewById(R.id.et_first)).setVisibility(savedInstanceState.getInt("first_et_v"));
}
1 голос
/ 23 апреля 2011

Я тоже столкнулся с этим. Вы должны восстановить состояние флажка в onRestoreInstanceState () вместо onCreate ().

Действие уничтожается при изменении ориентации экрана и вызывается onRestoreInstanceState () после onCreate (). Поскольку родительская / стандартная реализация onRestoreInstanceState () автоматически восстанавливает состояние в представлениях с идентификаторами, она восстанавливает ваши флажки после onCreate () и блокирует их - очевидно, из-за того, что они имеют один и тот же идентификатор (ошибка структуры?).

http://developer.android.com/reference/android/app/Activity.html

http://developer.android.com/reference/android/app/Activity.html#onRestoreInstanceState(android.os.Bundle)

1 голос
/ 25 января 2011

Вероятно, проблема в том, что при вызове onRestoreInstanceState(Bundle) некоторые или все «настройки» вашего приложения сбрасываются при «запуске» по умолчанию. Лучший способ решить эту проблему - вызовы и управление методами жизненного цикла приложения. Кроме того, каждый раз, когда в вашем приложении происходит изменение, которое вы не хотите потерять, сохраните изменение в saveInstanceState(Bundle) или создайте свой собственный Bundle() для временного хранения изменений, пока вы не сможете сохранить изменения в файле или чем-то , Достаточно легко передать связку через намерение между действиями. Как вы сохраняете все, что вам нужно сохранить, в зависимости от того, как долго вам нужно сохранять «настройки».

1 голос
/ 26 марта 2010

Rpond, я не переопределил onResume, поэтому я не думаю, что это проблема. Вот вид деятельности и связанные макеты для всех, чтобы увидеть. В коде вы увидите много операторов Log.d (их было еще больше). С помощью этих операторов Log я смог убедиться, что код работает точно так, как я ожидаю. Также обратите внимание на onCheckChangedListener, который я добавляю в каждый CheckBox. Все, что он делает, это печатает инструкцию Log, сообщающую мне, что состояние одного из моих CheckBoxes изменилось. Именно благодаря этому я смог определить состояние CheckBox, которые менялись после того, как мой onCreate вернулся. Вы увидите, как я вызываю examCheckboxes () в конце моего onCreate. Операторы Log, созданные из этого, не отображаются на моем экране после поворота, и я могу видеть состояние своих блоков, изменяемых впоследствии (из-за onCheckChangedListener.)

SelectItems.java:

открытый класс SelectItems extends Activity {

public static final int SUCCESS = 95485839;

public static final String ITEM_LIST = "item list";
public static final String ITEM_NAME = "item name";

// Save state constants
private final String SAVE_SELECTED = "save selected";

private DbHelper db;

private ArrayList<Long> selectedIDs;

ArrayList<CheckBox> cboxes = new ArrayList<CheckBox>();

@Override
public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.select_items);

    // Create database helper
    db = new DbHelper(this);
    db.open();

    initViews(savedInstanceState);

    examineCheckboxes();

}

private void initViews(Bundle savedState) {
    initButtons();

    initHeading();

    initList(savedState);
}

private void initButtons() {

    // Setup event for done button
    Button doneButton = (Button) findViewById(R.id.done_button);
    doneButton.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            done();
        }
    });

    // Setup event for cancel button
    Button cancelButton = (Button) findViewById(R.id.cancel_button);
    cancelButton.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            cancel();
        }
    });
}

private void initHeading() {

    String itemName = getIntent().getExtras().getString(ITEM_NAME);

    TextView headingField = (TextView) findViewById(R.id.heading_field);

    if (itemName.equals("")) {
        headingField.setText("No item name!");
    } else {
        headingField.setText("Item name: " + itemName);
    }
}

private void initList(Bundle savedState) {

    // Init selected id list
    if (savedState != null && savedState.containsKey(SAVE_SELECTED)) {
        long[] array = savedState.getLongArray(SAVE_SELECTED);
        selectedIDs = new ArrayList<Long>();

        for (long id : array) {
            selectedIDs.add(id);
        }

        Log.d("initList", "restoring from saved state");
        logIDList();
    } else {
        selectedIDs = (ArrayList<Long>) getIntent().getExtras().get(
                ITEM_LIST);

        Log.d("initList", "using database values");
        logIDList();
    }

    // Begin building item list
    LayoutInflater li = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    LinearLayout scrollContent = (LinearLayout) findViewById(R.id.scroll_content);

    Cursor cursor = db.getAllItems();
    startManagingCursor(cursor);
    cursor.moveToFirst();

    // For each Item entry, create a list element
    while (!cursor.isAfterLast()) {

        View view = li.inflate(R.layout.item_element, null);
        TextView name = (TextView) view.findViewById(R.id.item_name);
        TextView id = (TextView) view.findViewById(R.id.item_id);
        CheckBox cbox = (CheckBox) view.findViewById(R.id.checkbox);

        name.setText(cursor.getString(cursor
                .getColumnIndexOrThrow(DbHelper.COL_ITEM_NAME)));

        final long itemID = cursor.getLong(cursor
                .getColumnIndexOrThrow(DbHelper.COL_ID));
        id.setText(String.valueOf(itemID));

        // Set check box states based on selectedIDs array
        if (selectedIDs.contains(itemID)) {
            Log.d("set check state", "setting check to true for " + itemID);
            cbox.setChecked(true);
        } else {
            Log.d("set check state", "setting check to false for " + itemID);
            cbox.setChecked(false);
        }

        cbox.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                Log.d("onClick", "id: " + itemID + ". button ref: "
                        + ((CheckBox) v));
                checkChanged(itemID);
            }

        });

        //I implemented this listener just so I could see when my
        //CheckBoxes were changing.  Through this I was able to determine
        //that my CheckBoxes were being altered outside my own code.
        cbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {

            public void onCheckedChanged(CompoundButton arg0, boolean arg1) {

                Log.d("check changed", "button: " + arg0 + " changed to: "
                        + arg1);
            }

        });


        cboxes.add(cbox);

        scrollContent.addView(view);

        cursor.moveToNext();
    }

    cursor.close();

    examineCheckboxes();
}

private void done() {
    Intent i = new Intent();
    i.putExtra(ITEM_LIST, selectedIDs);
    setResult(SUCCESS, i);
    this.finish();
}

private void cancel() {
    db.close();
    finish();
}

private void checkChanged(long itemID) {
    Log.d("checkChaged", "checkChanged for: "+itemID);

    if (selectedIDs.contains(itemID)) {
        selectedIDs.remove(itemID);
    } else {
        selectedIDs.add(itemID);
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    long[] array = new long[selectedIDs.size()];
    for (int i = 0; i < array.length; i++) {
        array[i] = selectedIDs.get(i);
    }

    outState.putLongArray(SAVE_SELECTED, array);
}

//Debugging method used to see what is in selectedIDs at any point in time.
private void logIDList() {
    String list = "";

    for (long id : selectedIDs) {
        list += id + " ";
    }

    Log.d("ID List", list);
}

//Debugging method used to check the state of all CheckBoxes.
private void examineCheckboxes(){
    for(CheckBox cbox : cboxes){
        Log.d("Check Cbox", "obj: "+cbox+" checked: "+cbox.isChecked());
    }
}

}

select_items.xml:

<?xml version="1.0" encoding="utf-8"?>

<TextView android:id="@+id/heading_field"
    android:layout_width="fill_parent" android:layout_height="wrap_content"
    android:layout_marginBottom="10dip" android:textSize="18sp"
    android:textStyle="bold" />

<LinearLayout android:id="@+id/button_layout"
    android:orientation="horizontal" android:layout_width="fill_parent"
    android:layout_height="wrap_content" android:layout_alignParentBottom="true">

    <Button android:id="@+id/done_button" android:layout_weight="1"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="Done" />

    <Button android:id="@+id/cancel_button" android:layout_weight="1"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="Cancel" />

</LinearLayout>

<ScrollView android:orientation="vertical"
    android:layout_below="@id/heading_field" android:layout_above="@id/button_layout"
    android:layout_width="fill_parent" android:layout_height="wrap_content">
    <LinearLayout android:id="@+id/scroll_content"
        android:orientation="vertical" android:layout_width="fill_parent"
        android:layout_height="fill_parent">
    </LinearLayout>
</ScrollView>

item_element.xml:

<?xml version="1.0" encoding="utf-8"?>

<CheckBox android:id="@+id/checkbox" android:layout_width="wrap_content"
    android:layout_height="wrap_content" android:layout_alignParentRight="true"
    android:layout_marginRight="5dip" />

<TextView android:id="@+id/item_name" android:layout_width="wrap_content"
    android:layout_height="wrap_content" android:textSize="18sp"
    android:layout_centerVertical="true" android:layout_toLeftOf="@id/checkbox"
    android:layout_alignParentLeft="true" />

<TextView android:id="@+id/item_id" android:layout_width="wrap_content"
    android:layout_height="wrap_content" android:visibility="invisible" />

0 голосов
/ 09 октября 2016

Есть решение, которое проще объяснить.

ПРОВЕРКИ Android и RADIOBUTTON и RADIOGROUPS ведут себя странно, если для них не установлен атрибут "id".

У меня была точно такая же проблема в моем коде, и после того, как я установил идентификаторы на флажки, он начал работать, не отключая ни один из методов суперкласса.

...