Comment lire / écrire un booléen lors de l'implémentation de l'interface Parcelable?

404

J'essaie de faire un ArrayList Parcelableafin de passer à une activité une liste d'objets personnalisés. Je commence à écrire une myObjectListclasse qui étend ArrayList<myObject>et implémente Parcelable.

Certains attributs de MyObjectsont booleanmais Parceln'ont aucune méthode read/writeBoolean.

Quelle est la meilleure façon de gérer cela?

grogner
la source
3
Parce que toutes les lectures que j'ai faites sur le passage d'objet entre les activités préconisent l'utilisation de parcelable au lieu de serialisable.
grunk
10
@MisterSquonk Vous ne devez pas utiliser l' Serializableinterface pour la communication inter-activités. C'est lent.
Octavian A. Damiean
1
@grunk: OK, c'est assez juste mais jusqu'à ce que Intent.putExtra (nom de chaîne, valeur sérialisable) soit marqué comme obsolète dans les documents, je serai heureux de continuer à l'utiliser si cela me facilite la vie. Juste une pensée.
Squonk
3
@Octavian: Mais cela dépend d'une étude de cas et est relatif.
Squonk
2
à mon avis, l'équipe d'architectes d'Android est paresseuse !!! où est booléen !!!!!!!!
Mateus

Réponses:

942

Voici comment je le ferais ...

writeToParcel:

dest.writeByte((byte) (myBoolean ? 1 : 0));     //if myBoolean == true, byte == 1

readFromParcel:

myBoolean = in.readByte() != 0;     //myBoolean == true if byte != 0
b_yng
la source
38
Aucune raison d'utiliser l'octet sur l'int. l'octet est plus verbeux à cause de la distribution: dest.writeInt (myBoolean? 1: 0);
miguel
Pourquoi le commentaire @SiPlus a-t-il reçu autant de votes positifs? Ni 1, ni 0 n'est "chargé" du tout. Cela ne fait absolument aucune différence. Et depuis quand Java est-il un langage faiblement typé?
personne le
13
@sotrh écrire un octet écrit juste un entier:/** * Write a byte value into the parcel at the current dataPosition(), * growing dataCapacity() if needed. */ public final void writeByte(byte val) { writeInt(val); }
Dallas
3
@Dallas est-ce de la documentation? Si c'est le cas, alors ignorez mon commentaire. Semble fallacieux pour l'api d'avoir une fonction qui dit writeByte, mais écrit en fait un int.
sotrh
1
@sotrh qui est un copier-coller à partir d'un code source. Ouvrez la source android.os.Parcel et voyez par vous-même.
Yaroslav Mytkalyk
66

Vous pouvez également utiliser la méthode writeValue . À mon avis, c'est la solution la plus simple.

dst.writeValue( myBool );

Ensuite, vous pouvez facilement le récupérer avec un simple casting pour Boolean:

boolean myBool = (Boolean) source.readValue( null );

Sous le capot, Android Framework le traitera comme un entier:

writeInt( (Boolean) v ? 1 : 0 );
Taig
la source
source android montrant la partie "sous le capot" (l. 1219). Bien qu'elle soit un peu moins efficace (en raison de l'appel de méthode et du commutateur), cette méthode est un peu plus simple.
desseim
6
Le code tel qu'écrit ici ne sera pas compilé. readValue nécessite un chargeur de classe, mais il n'est pas nécessaire pour les booléens. Il doit lire "boolean myBool = (Boolean) source.readValue (null);" Les critiques qui n'ont aucune idée de ce qu'ils font et ont rejeté ma modification de cette réponse: @hemang, jeeped, DavidRR.
miguel
1
@miguel C'est vrai, corrigé :)
Taig
15

vous déclarez comme ça

 private boolean isSelectionRight;

écrire

 out.writeInt(isSelectionRight ? 1 : 0);

lis

isSelectionRight  = (in.readInt() == 0) ? false : true;

Le type booléen doit être converti en quelque chose que Parcel prend en charge et nous pouvons donc le convertir en int.

Shaista Naaz
la source
Cela provoque une erreur, des types incompatibles, un booléen requis trouvé int?
Paul Okeke
12

AndroidStudio (en utilisant 2.3 atm), après avoir implémenté Parcelable sur votre classe, vous pouvez simplement maintenir le pointeur de votre souris sur le nom de votre classe et il vous demande d'ajouter l'implémentation parcelable:

entrez la description de l'image ici

À partir des quatre champs, il génère les éléments suivants:

public class YourClass implements Parcelable{

String someString;
int someInt;
boolean someBoolean;
List<String> someList;

protected YourClass(Parcel in) {
    someString = in.readString();
    someInt = in.readInt();
    someBoolean = in.readByte() != 0;
    someList = in.createStringArrayList();
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(someString);
    dest.writeInt(someInt);
    dest.writeByte((byte) (someBoolean ? 1 : 0));
    dest.writeStringList(someList);
}

...
Henk-Martijn
la source
C'est pratique :)
Dino Tw
8

Je les ai normalement dans un tableau et appeler writeBooleanArrayetreadBooleanArray

S'il s'agit d'un seul booléen que vous devez emballer, vous pouvez le faire:

parcel.writeBooleanArray(new boolean[] {myBool});
Géobits
la source
Avez-vous déjà rencontré un problème lorsque vous avez besoin de regrouper quelques booléens ... je l'ai. Pourriez-vous m'aider>? stackoverflow.com/questions/13463727/… . Merci.
Roger Alien
8
out.writeInt(mBool ? 1 : 0); //Write
this.mBool =in.readInt()==1; //Read
Meghna
la source
5

Je vous ai suggéré la manière la plus simple d'implémenter Parcelable si vous utilisez Android Studio.

Allez simplement dans Fichier-> Paramètres-> Plugins-> Parcourir le référentiel et recherchez parcelable .Voir l'image

entrez la description de l'image ici Il créera automatiquement Parcelable.

Et il y a aussi un webiste pour faire ça. http://www.parcelabler.com/

Zar E Ahmer
la source
4

Implémentation courte et simple dans Kotlin , avec support nullable:

Ajouter des méthodes à Parcel

fun Parcel.writeBoolean(flag: Boolean?) {
    when(flag) {
        true -> writeInt(1)
        false -> writeInt(0)
        else -> writeInt(-1)
    }
}

fun Parcel.readBoolean(): Boolean? {
    return when(readInt()) {
        1 -> true
        0 -> false
        else -> null
    }
}

Et utilisez-le:

parcel.writeBoolean(isUserActive)

parcel.readBoolean()        // For true, false, null
parcel.readBoolean()!!      // For only true and false
Pavel Shorokhov
la source
@AliKazi, vous ne pouvez pas le faire en une seule instruction en ligne avec prise en charge des types facultatifs. Vous devez persister 3 états. Je pense que vous oubliez null.
Pavel Shorokhov
@ comm1x, l'IDE n'aime pas cette solution. `Impossible d'accéder à" readBoolean "avant l'appel du constructeur de la superclasse.
Adam Hurwitz
@ comm1x J'ai résolu le problème. Pour fonctionner, les méthodes ajoutées doivent se trouver dans l' objet compagnon
Adam Hurwitz
3

Vous pouvez regrouper vos valeurs booléennes dans un octet en utilisant le masquage et le décalage. Ce serait la façon la plus efficace de le faire et c'est probablement ce qu'ils attendraient de vous.

fleetway76
la source
+1. Certes, enregistrer la valeur dans un octet serait plus efficace. Pourriez-vous me faire modifier ma question pour représenter votre idée?
Octavian A. Damiean
Vous pouvez, mais ce que vous avez écrit n'est pas vraiment ce que je voulais dire. Cela fonctionnera mais n'est pas le plus efficace. Vous pouvez regrouper 8 booléens dans un octet à l'aide de masques et d'algèbre booléenne
fleetway76
Dans la plupart des cas, je suis d'accord avec vous, mais j'ai des cas qui remontent de temps en temps où j'ai besoin de trois états avec lesquels les travaux booléens fonctionneront. Comme une question facultative oui / non. J'ai besoin du null pour savoir si le booléen a été explicitement spécifié ou non.
Chad Gorshing
Il n'y a aucune raison d'utiliser octet sur entier, car la writeByte()méthode de Parcel appelle directementwriteInt()
Yaroslav Mytkalyk
3

Il est difficile d'identifier la vraie question ici. Je suppose que c'est la façon de traiter les booléens lors de la mise en œuvre de l' Parcelableinterface.

Certains attributs de MyObject sont booléens mais Parcel n'a pas de méthode read / writeBoolean.

Vous devrez soit stocker la valeur sous forme de chaîne, soit sous forme d'octet. Si vous optez pour une chaîne, vous devrez utiliser la méthode statique de la Stringclasse appelée valueOf()pour analyser la valeur booléenne. Ce n'est pas aussi efficace que de l'enregistrer dans un octet difficile.

String.valueOf(theBoolean);

Si vous optez pour un octet, vous devrez implémenter vous-même une logique de conversion.

byte convBool = -1;
if (theBoolean) {
    convBool = 1;
} else {
    convBool = 0;
}

Lorsque vous démasquez l' Parcelobjet, vous devez vous occuper de la conversion vers le type d'origine.

Octavian A. Damiean
la source
Il n'y a absolument aucune raison d'utiliser String. Il n'y a aucune raison d'utiliser byte over int, car la writeByte()méthode Parcel appelle directement writeInt(). La logique de conversion est trop verbeuse. writeInt(boolean ? 1 : 0)boolean = readInt() != 0
Faites
1

Cette question a déjà été parfaitement répondue par d'autres personnes, si vous voulez le faire vous-même.

Si vous préférez encapsuler ou masquer la plupart du code de parcellaire de bas niveau, vous pouvez envisager d'utiliser une partie du code que j'ai écrit il y a quelque temps pour simplifier la gestion des parcellaires.

Écrire sur un colis est aussi simple que:

parcelValues(dest, name, maxSpeed, weight, wheels, color, isDriving);

où couleur est une énumération et isDriving est un booléen, par exemple.

La lecture d'un colis n'est pas non plus beaucoup plus difficile:

color = (CarColor)unparcelValue(CarColor.class.getClassLoader());
isDriving = (Boolean)unparcelValue();

Jetez un œil au "ParceldroidExample" que j'ai ajouté au projet.

Enfin, il conserve également l'initialiseur CREATOR court:

public static final Parcelable.Creator<Car> CREATOR =
    Parceldroid.getCreatorForClass(Car.class);
Michael Geier
la source
Merci pour .class.getClassLoader().
CoolMind
0

Il existe de nombreux exemples dans les sources Android (AOSP). Par exemple, la PackageInfoclasse a un membre booléen requiredForAllUserset elle est sérialisée comme suit:

public void writeToParcel(Parcel dest, int parcelableFlags) {
    ...
    dest.writeInt(requiredForAllUsers ? 1 : 0);
    ...
}

private PackageInfo(Parcel source) {
    ...
    requiredForAllUsers = source.readInt() != 0;
    ...
}
FireAphis
la source