Case À Cocher Android-Restauration De L'État Après La Rotation De L'Écran

j'ai rencontré des fonctionnalités très inattendues (et incroyablement frustrantes) en essayant de restaurer l'état d'une liste de CheckBoxes-après une rotation de l'écran. Je me suis dit que j'essaierais d'abord de donner une explication textuelle sans le code, au cas où quelqu'un serait capable de trouver une solution sans tous les détails sanglants. Si quelqu'un a besoin de plus de détails je peux poster le code.

j'ai une liste déroulante des complexes Views CheckBox es. J'ai succombé en rétablissant l'état de ces cases à cocher après une rotation de l'écran. J'ai mis en œuvre onSaveInstanceState et ont transféré avec succès la liste des cases à cocher sélectionnées vers le onCreate méthode. Ceci est géré en passant un long[] des identifiants de la base de données vers le Bundle.

onCreate() j'ai vérifier l' Bundle pour le tableau d'id. Si le tableau est là, je l'utilise pour déterminer quelles cases à cocher Quand la liste est construite. J'ai créé un certain nombre de méthodes d'essai et ont confirmé que les cases à cocher sont positionnées correctement, sur la base du tableau id. Comme dernière vérification, je vérifie les états de toutes les cases à la toute fin de onCreate(). Tout semble bon... à moins que j'ai de faire pivoter l'écran.

quand je tourne l'écran, l'une des deux choses se produit: 1) si un nombre quelconque des cases à cocher est sélectionné, sauf la dernière, toutes les cases à cocher sont désactivées après une rotation. 2) Si la dernière case est cochée avant la rotation, puis toutes les cases sont coché après la rotation.

Comme je l'ai dit, je vérifie l'état des cases à la fin de mon onCreate(). La chose est, l'état des boîtes à la fin de onCreate correct basé sur ce que j'ai sélectionné avant la rotation. Cependant, l'état des cases à l'écran ne reflète pas cela.

en outre, j'ai implémenté chaque case setOnCheckChangedListener() et j'ai confirmé que mes cases à cocher' état sont altérés après mon onCreate retourne la méthode.

quelqu'un a une idée de ce qui se passe? Pourquoi l'état de mes cases à cocher changer après mon onCreate retour de la méthode?

Merci d'avance pour votre aide. J'ai essayé de degub cela pour un couple de jours maintenant. Après avoir découvert que mes cases à cocher changeaient apparemment quelque part en dehors de mon propre code, j'ai pensé qu'il était temps de me renseigner.

28
demandé sur fawaad 2010-03-25 01:52:36

8 réponses

j'ai eu le même problème. App avait plusieurs cases à cocher à l'écran.

Après l'application de téléphone tournant a été "manuellement" valeur de réglage pour toutes les cases à cocher.

Ce code a été exécuté dans onStart().

Mais à l'écran toutes les cases à cocher ont été placées avec la valeur de 'dernière case à cocher' à l'écran.

Android appelait sur restoreinstancestate (..) et d'une manière ou d'une autre traitait toutes les cases à cocher comme 'une' [dernière de l'écran].

<RadioButton
    ...
    android:saveEnabled="false" />
47
répondu Grzegorz Dev 2013-01-24 11:15:44

tout le monde. On dirait que je compris cela. L'état des cases à cocher est modifié dans onRestoreInstanceState(Bundle). Cette méthode est appelée après onCreate() (plus précisément, après onStart()), et est un autre endroit où Android recommande de restaurer l'état.

maintenant, je n'ai aucune idée de la raison pour laquelle mes cases à cocher sont modifiées dans onRestoreInstanceState, mais au moins je sais que c'est là que le problème se produit. Étonnamment, quand j'outrepasse onRestoreInstanceState et do absolument rien (aucun appel à super.onRestoreInstanceState) l'ensemble de L'activité fonctionne parfaitement.

Si quelqu'un peut me dire pourquoi l'état sélectionné des cases à cocher est modifié dans cette méthode que j'aimerais beaucoup connaître. À mon avis, cela ressemble à un bug dans le code Android lui-même.

8
répondu Jared M 2010-04-04 00:12:33

Si jamais vous trouvez quelque chose d'autre de plus à propos de ce problème, s'il vous plaît laissez-moi savoir. J'ai affronté essentiellement ce même problème, et j'ai seulement dépassé onRestoreInstanceState() travaillé. Très bizarre.

2
répondu ExistentialEnso 2012-04-05 13:04:33

Rpond, Je n'ai pas outrepassé onResume, donc je ne pense pas que ce soit le problème. Voici l'Activité et les dispositions associées à chacun de voir. Dans le code, vous verrez beaucoup de Log.d consolidés (il y avait encore plus à un moment donné. Avec ces instructions de journalisation j'ai été en mesure de vérifier que le code fonctionne exactement comme je l'attends. Aussi, notez le listener modifié oncheck que j'ajoute à chaque case à cocher. Tout ce qu'il fait est d'imprimer une déclaration de journal me disant l'état de l'une de mes cases à cocher changé. Il a été grâce à cela, j'ai pu déterminer l'état des cases à cocher ont été modifiés après mes retours onCreate. Vous verrez comment j'appelle examineCheckboxes() à la fin de mon onCreate. Les déclarations de Log produites à partir de cela ne sont pas ce qui est montré sur mon écran après une rotation, et je peux voir l'état de mes boîtes étant modifié par la suite (en raison de l'onCheckChangedListener.)

Sélectitems.java:

public class SelectItems s'étend de l'Activité {

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" />

1
répondu Jared M 2010-03-26 18:35:43

probablement le problème est que chaque fois que onRestoreInstanceState(Bundle) est appelée, elle réinitialise certains ou tous les "paramètres" de votre application à la start-up "par défaut". La meilleure façon de résoudre ce problème est d'utiliser la méthode app lifecycle calls et la gestion. En outre, chaque fois que quelque chose est changé dans votre application que vous ne voulez pas perdre, enregistrez le changement dans le saveInstanceState(Bundle) ou créer votre propre Bundle() pour maintenir les changements Temporairement jusqu'à ce que vous puissiez persister les changements dans un fichier ou quelque chose, C'est assez facile de passer un paquet grâce à une Intention entre les Activités. Comment sauvegarder tout ce que vous devez sauvegarder en fonction de la durée nécessaire pour conserver les "paramètres".

1
répondu Kingsolmn 2011-01-25 18:21:13

j'ai exécuté ce trop. Vous devez restaurer l'état checkbox dans onRestoreInstanceState() au lieu de onCreate().

l'activité est détruite lorsque l'orientation de l'écran change et onRestoreInstanceState() est appelé après onCreate(). Depuis que l'implémentation parent/default d'onRestoreInstanceState () restaure automatiquement l'état dans les vues avec des IDs, elle restaure vos cases à cocher après onCreate () et les cogne -- apparemment parce qu'ils ont le même ID (framework bug?).

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

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

1
répondu JustinC 2011-04-23 00:27:38

vous pouvez aussi utiliser onSaveInstanceState(Bundle savedInstanceState) et onRestoreInstanceState(Bundle savedInstanceState)

exemple

@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
répondu bdely 2011-12-09 03:45:01

Il y a une solution plus facile à expliquer.

cases à cocher Android et RADIOBUTTON et les RADIOGROUPES se comportent bizarrement si l'attribut " id " n'est pas défini sur eux.

j'ai eu exactement le même problème dans mon code, et après avoir placé des ID sur les cases à cocher, il a commencé à fonctionner, sans avoir à désactiver aucune des méthodes superclass.

0
répondu Waqas Ahmed 2016-10-09 04:45:47