Как читать / писать логическое значение при реализации интерфейса Parcelable?


404

Я пытаюсь сделать ArrayList Parcelable, чтобы передать деятельности список пользовательских объектов. Я начинаю писать myObjectListкласс, который расширяет ArrayList<myObject>и реализует Parcelable.

Некоторые атрибуты MyObjectесть, booleanно Parcelне имеют никакого метода read/writeBoolean.

Каков наилучший способ справиться с этим?


3
Потому что все прочитанное мною о передаче объекта между действиями предполагает использование parcelable вместо serialisable.
grunk

10
@MisterSquonk Вы не должны использовать Serializableинтерфейс для взаимодействия между действиями. Это медленно.
Октавиан А. Дамиан

1
@grunk: Хорошо, это достаточно справедливо, но пока Intent.putExtra (имя строки, значение Serializable) не будет помечено как устаревшее в документации, я буду рад продолжить его использование, если это облегчит мне жизнь. Просто мысль.
Squonk

3
@Octavian: Но это зависит от конкретного случая и является относительным.
Squonk

2
по моему мнению, команда разработчиков android ленива !!! где логическое значение !!!!!!!!
Матеус

Ответы:


942

Вот как я это сделаю ...

writeToParcel:

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

readFromParcel:

myBoolean = in.readByte() != 0;     //myBoolean == true if byte != 0

38
Нет причин использовать байт над int. байт является более многословным из-за приведения: dest.writeInt (myBoolean? 1: 0);
Мигель

Почему комментарии @SiPlus получили так много голосов? Ни 1, ни 0 вообще не «загружены». Это не имеет абсолютно никакого значения. И с каких это пор Java является слабо типизированным языком?
никто не

13
@sotrh, пишущий байт, просто записывает int:/** * Write a byte value into the parcel at the current dataPosition(), * growing dataCapacity() if needed. */ public final void writeByte(byte val) { writeInt(val); }
Даллас

3
@Dallas это из документации? Если так, то не обращайте внимания на мой комментарий. Кажется неискренним для API иметь функцию, которая говорит writeByte, но на самом деле пишет int.
Sotrh

1
@sotrh - это копия-вставка из исходного кода. Откройте источник android.os.Parcel и убедитесь сами.
Ярослав Мыткалык

66

Вы также можете использовать метод writeValue . На мой взгляд, это самое простое решение.

dst.writeValue( myBool );

После этого вы можете легко получить его с помощью простого приведения к Boolean:

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

Под капотом Android Framework будет обрабатывать его как целое число:

writeInt( (Boolean) v ? 1 : 0 );

Android-источник, показывающий часть «под капотом» (л. 1219). Хотя этот метод немного менее эффективен (из-за вызова метода и переключателя), этот метод выглядит немного более простым.
'18

6
Код, как написано здесь, не будет компилироваться. Для readValue требуется загрузчик классов, но он не нужен для логических значений. Должно быть написано "boolean myBool = (Boolean) source.readValue (null);" Рецензенты, которые понятия не имеют, что они делают, и отклонили мою правку на этот ответ: @hemang, jeeped, DavidRR.
Мигель

1
@miguel Это правда, исправил это :)
Taig

15

ты заявляешь вот так

 private boolean isSelectionRight;

записывать

 out.writeInt(isSelectionRight ? 1 : 0);

читать

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

Булев тип должен быть преобразован во что-то, что поддерживает Parcel, и мы можем преобразовать его в int.


Это вызывает ошибку, несовместимые типы, обязательный логический найденный int?
Пол Океке

12

AndroidStudio (с использованием 2,3 атм), после реализации Parcelable в вашем классе, вы можете просто навести указатель мыши на имя класса, и он попросит вас добавить реализацию Parcelable:

введите описание изображения здесь

Из четырех полей он генерирует следующее:

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

...

Это удобно :)
Dino Tw

8

У меня обычно есть их в массиве и вызов writeBooleanArrayиreadBooleanArray

Если вам нужно упаковать один логический тип, вы можете сделать это:

parcel.writeBooleanArray(new boolean[] {myBool});

Вы когда-нибудь сталкивались с проблемой, когда вам нужно посылать несколько логических значений ... у меня есть. Не могли бы вы помочь мне>? stackoverflow.com/questions/13463727/… . Спасибо.
Роджер Инопланетянин


5

Я предложил вам самый простой способ реализовать Parcelable, если вы используете Android Studio.

Просто зайдите в Файл-> Настройки-> Плагины-> Обзор репозитория и выполните поиск доступных для продажи. См. Изображение

введите описание изображения здесь Это автоматически создаст Parcelable.

И для этого есть веб-сайт. http://www.parcelabler.com/


4

Короткая и простая реализация в Kotlin с поддержкой Nullable:

Добавить методы в 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
    }
}

И использовать это:

parcel.writeBoolean(isUserActive)

parcel.readBoolean()        // For true, false, null
parcel.readBoolean()!!      // For only true and false

@AliKazi, вы не можете сделать это в виде 1 строки в Java с поддержкой необязательных типов. Вы должны сохранить 3 состояния. Я думаю, что вы забыли о нуле.
Павел Шорохов

@ comm1x, IDE не нравится это решение. `Невозможно получить доступ к« readBoolean »до вызова конструктора суперкласса.
Адам Гурвиц

@ comm1x Я решил проблему. Для работы добавленные методы должны находиться внутри объекта-компаньона
Адам Гурвиц,

3

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


+1. Правда, сохранение значения в байте было бы более эффективным. Не могли бы вы редактировать мой вопрос, чтобы представить вашу идею?
Октавиан А. Дамиан

Вы можете, но то, что вы написали, не совсем то, что я имел в виду. Это будет работать, но не самый эффективный. Вы можете упаковать 8 логических значений в байт, используя маски и логическую алгебру
fleetway76

В большинстве случаев я согласен с вами, однако время от времени появляются случаи, когда мне нужны три состояния, с которыми будет работать Boolean. Такой как дополнительный вопрос да / нет. Мне нужно значение NULL, чтобы знать, был ли логический параметр указан явно или нет.
Чад Горшинг

Нет смысла использовать byte над int, поскольку метод Parcel writeByte()вызывает напрямуюwriteInt()
Ярослав Мыткалык

3

Трудно определить реальный вопрос здесь. Я предполагаю, что это как бороться с логическими значениями при реализации Parcelableинтерфейса.

Некоторые атрибуты MyObject являются логическими, но у Parcel нет метода read / writeBoolean.

Вам нужно будет либо сохранить значение в виде строки или байта. Если вы выберете строку, вам придется использовать статический метод Stringкласса, valueOf()который вызывается для анализа логического значения. Это не так эффективно, как сохранить его в жестком байте.

String.valueOf(theBoolean);

Если вы выберете байт, вам придется самостоятельно реализовать логику преобразования.

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

При демонтаже Parcelобъекта вы должны позаботиться о преобразовании в исходный тип.


Нет абсолютно никаких причин использовать String. Нет смысла использовать байт над int, так как writeByte()метод Parcel вызывает напрямую writeInt(). Логика преобразования слишком многословна. Просто сделай writeInt(boolean ? 1 : 0)иboolean = readInt() != 0
Ярослав Мыткалык

1

На этот вопрос уже отлично ответили другие люди, если вы хотите сделать это самостоятельно.

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

Написать посылку так же просто, как:

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

где color - это enum, а isDriving - логическое значение, например.

Чтение с посылки тоже не намного сложнее:

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

Просто взгляните на «ParceldroidExample», который я добавил в проект.

Наконец, он также сокращает инициализатор CREATOR:

public static final Parcelable.Creator<Car> CREATOR =
    Parceldroid.getCreatorForClass(Car.class);

Спасибо за .class.getClassLoader().
CoolMind

0

Есть много примеров в источниках Android (AOSP). Например, PackageInfoкласс имеет логический член requiredForAllUsersи сериализуется следующим образом:

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

private PackageInfo(Parcel source) {
    ...
    requiredForAllUsers = source.readInt() != 0;
    ...
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.