Como ler / escrever um booleano ao implementar a interface Parcelable?

404

Estou tentando fazer um ArrayList Parcelablepara passar para uma atividade uma lista de objetos personalizados. Começo a escrever uma myObjectListclasse que estende ArrayList<myObject>e implementa Parcelable.

Alguns atributos de MyObjectsão booleanmas Parcelnão têm nenhum método read/writeBoolean.

Qual a melhor forma de lidar com isto?

grunhido
fonte
3
Porque toda a leitura que fiz sobre a passagem de objetos entre atividades preconiza o uso de parcelável em vez de serializável.
GRUNK
10
@MisterSquonk Você não deve usar a Serializableinterface para comunicação entre atividades. Está lento.
Octavian A. Damiean
11
@ grunk: OK, isso é justo o suficiente, mas até Intent.putExtra (nome da string, valor serializável) ser marcado como descontinuado nos documentos, ficarei feliz em continuar usando, se facilitar a vida para mim. Apenas um pensamento.
Squonk
3
@ Octavian: Mas isso depende de um estudo por caso e é relativo.
Squonk
2
na minha opinião, a equipe de arquitetos do android é preguiçosa !!! whereis boolean !!!!!!!!
Mateus

Respostas:

942

Aqui está como eu faria isso ...

writeToParcel:

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

readFromParcel:

myBoolean = in.readByte() != 0;     //myBoolean == true if byte != 0
b_yng
fonte
38
Não há razão para usar byte sobre int. byte é mais detalhado devido ao elenco: dest.writeInt (myBoolean? 1: 0);
miguel
Por que o comentário do @SiPlus recebeu tantos votos? Nem 1 nem 0 são "carregados". Não faz absolutamente nenhuma diferença. E desde quando o Java é uma linguagem de tipo fraco?
ninguém
13
@sotrh escrevendo um byte apenas escreve um int:/** * 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 é isso da documentação? Se sim, desconsidere meu comentário. Parece falso para a API ter uma função que diz writeByte, mas na verdade escreve um int.
sotrh
11
@sotrh que é uma copiar e colar de um código fonte. Abra a fonte android.os.Parcel e veja por si mesmo.
Yaroslav Mytkalyk
66

Você também pode usar o método writeValue . Na minha opinião, essa é a solução mais direta.

dst.writeValue( myBool );

Depois, você pode recuperá-lo facilmente com uma conversão simples para Boolean:

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

Sob o capô, o Android Framework o tratará como um número inteiro:

writeInt( (Boolean) v ? 1 : 0 );
Taig
fonte
fonte android mostrando a parte "under the hood" (l. 1219). Embora um pouco menos eficiente (por causa da chamada do método e da opção), esse método é um pouco mais direto.
desseim 12/02
6
O código conforme escrito aqui não será compilado. O readValue requer um carregador de classes, mas não é necessário para os booleanos. Ele deve ler "booleano myBool = (booleano) source.readValue (null);" Os revisores que não têm ideia do que estão fazendo e rejeitaram minha edição desta resposta: @hemang, jeeped, DavidRR.
miguel
11
@miguel Isso é verdade, fixa-lo :)
Taig
15

você declara assim

 private boolean isSelectionRight;

Escreva

 out.writeInt(isSelectionRight ? 1 : 0);

ler

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

O tipo booleano precisa ser convertido em algo que o Parcel suporta e, portanto, podemos convertê-lo em int.

Shaista Naaz
fonte
Isso causa um erro, tipos incompatíveis, booleano obrigatório encontrado int?
Paul Okeke
12

AndroidStudio (usando 2.3 atm), após implementar o Parcelable na sua classe, você pode simplesmente segurar o ponteiro do mouse sobre o nome da sua classe e solicitar que você adicione a implementação parcelável:

insira a descrição da imagem aqui

Dos quatro campos, ele gera o seguinte:

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
fonte
Isso é útil :)
Dino Tw 17/11
8

Eu normalmente os tenho em uma matriz e chamo writeBooleanArrayereadBooleanArray

Se você precisar compactar um único booleano, faça o seguinte:

parcel.writeBooleanArray(new boolean[] {myBool});
Geobits
fonte
Você já enfrentou problemas quando precisa parcelar alguns booleanos ... eu tenho. Você poderia me ajudar por favor? stackoverflow.com/questions/13463727/… . Obrigado.
23412 Roger Alien
8
out.writeInt(mBool ? 1 : 0); //Write
this.mBool =in.readInt()==1; //Read
Meghna
fonte
5

Sugeri a você a maneira mais fácil de implementar o Parcelable se você estiver usando o Android Studio.

Basta ir em Arquivo-> Configurações-> Plug-ins-> Procurar Repositório e procurar por pacotes.

insira a descrição da imagem aqui Ele criará automaticamente o Parcelable.

E há um webiste também para fazer isso. http://www.parcelabler.com/

Zar E Ahmer
fonte
4

Implementação curta e simples no Kotlin , com suporte anulável:

Adicionar métodos ao pacote

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
    }
}

E use-o:

parcel.writeBoolean(isUserActive)

parcel.readBoolean()        // For true, false, null
parcel.readBoolean()!!      // For only true and false
Pavel Shorokhov
fonte
@AliKazi, você não pode fazer isso em uma instrução de linha em Java com tipos opcionais de suporte. Você deve persistir em 3 estados. Eu acho que você esquece nulo.
Pavel Shorokhov
@ comm1x, o IDE não gosta desta solução. `Não é possível acessar" readBoolean "antes que o construtor da superclasse seja chamado.
Adam Hurwitz
@ comm1x Resolvi o problema. Para trabalhar os métodos adicionados deve estar dentro do objeto companheiro
Adam Hurwitz
3

Você pode agrupar seus valores booleanos em um byte usando mascaramento e deslocamento. Essa seria a maneira mais eficiente de fazer isso e é provavelmente o que eles esperam que você faça.

fleetway76
fonte
+1. É verdade que salvar o valor em um byte seria mais eficiente. Importa-se de editar minha pergunta para representar sua ideia?
Octavian A. Damiean
Você pode, mas o que você escreveu não é exatamente o que eu quis dizer. Funcionará, mas não é o mais eficiente. Você pode embalar 8 booleans em um byte usando máscaras e álgebra booleana
fleetway76
Na maioria dos casos, concordo com você, no entanto, tenho casos que surgem de tempos em tempos em que preciso de três estados com os quais os trabalhos booleanos irão. Como uma pergunta opcional sim / não. Eu preciso do nulo para saber se o Boolean foi explicitamente especificado ou não.
Chad Gorshing 9/11/11
Não há nenhuma razão usando byte sobre int, desde do Parcel writeByte()método chama diretamentewriteInt()
Yaroslav Mytkalyk
3

É difícil identificar a verdadeira questão aqui. Eu acho que é como lidar com booleanos ao implementar a Parcelableinterface.

Alguns atributos do MyObject são booleanos, mas o Parcel não possui nenhum método read / writeBoolean.

Você precisará armazenar o valor como uma sequência ou como um byte. Se você optar por uma string, precisará usar o método estático da Stringclasse chamada valueOf()para analisar o valor booleano. Não é tão eficaz quanto salvá-lo em um byte resistente.

String.valueOf(theBoolean);

Se você optar por um byte, precisará implementar uma lógica de conversão.

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

Ao desselecionar o Parcelobjeto, você deve cuidar da conversão para o tipo original.

Otaviano A. Damiean
fonte
Não há absolutamente nenhuma razão para usar String. Não há razão para usar byte sobre int, pois o writeByte()método Parcel chama diretamente writeInt(). A lógica de conversão é muito detalhada. Apenas faça writeInt(boolean ? 1 : 0)eboolean = readInt() != 0
Yaroslav Mytkalyk
1

Essa pergunta já foi respondida perfeitamente por outras pessoas, se você quiser fazer isso sozinha.

Se você preferir encapsular ou ocultar a maior parte do código de parcelamento de baixo nível, considere usar parte do código que escrevi há algum tempo para simplificar o manuseio de pacotes.

Escrever em um pacote é tão fácil quanto:

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

onde color é uma enum e isDriving é um booleano, por exemplo.

Ler de um pacote também não é muito mais difícil:

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

Basta dar uma olhada no "ParceldroidExample" que adicionei ao projeto.

Por fim, também mantém o inicializador CREATOR curto:

public static final Parcelable.Creator<Car> CREATOR =
    Parceldroid.getCreatorForClass(Car.class);
Michael Geier
fonte
Obrigado por .class.getClassLoader().
CoolMind 21/01/19
0

Existem muitos exemplos nas fontes do Android (AOSP). Por exemplo, a PackageInfoclasse tem um membro booleano requiredForAllUserse é serializado da seguinte maneira:

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

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