インテントを介して列挙型またはオブジェクトを渡す(最善の解決策)


221

起動時に2つの異なるArrayListにアクセスする必要があるアクティビティがあります。両方のリストは、私が自分で作成した異なるオブジェクトです。

基本的に、これらのオブジェクトをインテントからアクティビティに渡す方法が必要です。addExtras()を使用できますが、これにはParceable互換クラスが必要です。クラスをシリアル化可能にすることもできますが、理解するとプログラムの速度が低下します。

私のオプションは何ですか?

Enumを渡すことはできますか?

余談ですが、インテントからアクティビティコンストラクターにパラメーターを渡す方法はありますか?


多分私は何かが足りないのですが、列挙型はどのようにArrayListに関連していますか?
Martin Konecny、2015

回答:


558

これは古い質問ですが、列挙型は実際にSerializableあり、したがって、エクステントとしてインテントに完全に追加できることは誰もが言及していません。このような:

public enum AwesomeEnum {
  SOMETHING, OTHER;
}

intent.putExtra("AwesomeEnum", AwesomeEnum.SOMETHING);

AwesomeEnum result = (AwesomeEnum) intent.getSerializableExtra("AwesomeEnum");

静的変数またはアプリケーション全体の変数を使用するという提案は、非常に悪い考えです。これは実際にあなたの活動を状態管理システムに結びつけます、そして維持し、デバッグし、問題を束縛することは困難です。


代替案:

Oderikによって提供されたソリューションがエラーを与えるという事実についてtedzycは良い点を指摘しました。ただし、提供される代替案は、使用するのが少し面倒です(ジェネリックを使用する場合でも)。

インテントに列挙型を追加することのパフォーマンスが本当に心配な場合は、代わりに次の代替案を提案します。

オプション1:

public enum AwesomeEnum {
  SOMETHING, OTHER;
  private static final String name = AwesomeEnum.class.getName();
  public void attachTo(Intent intent) {
    intent.putExtra(name, ordinal());
  }
  public static AwesomeEnum detachFrom(Intent intent) {
    if(!intent.hasExtra(name)) throw new IllegalStateException();
    return values()[intent.getIntExtra(name, -1)];
  }
}

使用法:

// Sender usage
AwesomeEnum.SOMETHING.attachTo(intent);
// Receiver usage
AwesomeEnum result = AwesomeEnum.detachFrom(intent);

オプション2:( 一般的、再利用可能、列挙型から分離)

public final class EnumUtil {
    public static class Serializer<T extends Enum<T>> extends Deserializer<T> {
        private T victim;
        @SuppressWarnings("unchecked") 
        public Serializer(T victim) {
            super((Class<T>) victim.getClass());
            this.victim = victim;
        }
        public void to(Intent intent) {
            intent.putExtra(name, victim.ordinal());
        }
    }
    public static class Deserializer<T extends Enum<T>> {
        protected Class<T> victimType;
        protected String name;
        public Deserializer(Class<T> victimType) {
            this.victimType = victimType;
            this.name = victimType.getName();
        }
        public T from(Intent intent) {
            if (!intent.hasExtra(name)) throw new IllegalStateException();
            return victimType.getEnumConstants()[intent.getIntExtra(name, -1)];
        }
    }
    public static <T extends Enum<T>> Deserializer<T> deserialize(Class<T> victim) {
        return new Deserializer<T>(victim);
    }
    public static <T extends Enum<T>> Serializer<T> serialize(T victim) {
        return new Serializer<T>(victim);
    }
}

使用法:

// Sender usage
EnumUtil.serialize(AwesomeEnum.Something).to(intent);
// Receiver usage
AwesomeEnum result = 
EnumUtil.deserialize(AwesomeEnum.class).from(intent);

オプション3(Kotlinを使用):

久しぶりにKotlinが登場してから、新しいパラダイムに別のオプションを追加するつもりでした。ここでは、拡張関数と具体化された型(コンパイル時に型を保持する)を利用できます。

inline fun <reified T : Enum<T>> Intent.putExtra(victim: T): Intent =
    putExtra(T::class.java.name, victim.ordinal)

inline fun <reified T: Enum<T>> Intent.getEnumExtra(): T? =
    getIntExtra(T::class.java.name, -1)
        .takeUnless { it == -1 }
        ?.let { T::class.java.enumConstants[it] }

このようにすると、いくつかの利点があります。

  • シリアル化を実行するために中間オブジェクトの「オーバーヘッド」を必要としません。inlineこれは、呼び出しが関数内のコードに置き換えられるため、すべてが適切に行われるためです。
  • これらの関数は、SDKの関数に類似しているため、より使い慣れています。
  • IDEはこれらの関数をオートコンプリートします。つまり、ユーティリティクラスの事前の知識は必要ありません。

欠点の1つは、エミュムの順序を変更すると、古い参照が機能しなくなることです。保留中のインテント内のインテントのようなものは、更新を生き残る可能性があるため、これが問題になる可能性があります。ただし、それ以外の時間は問題ありません。

位置の代わりに名前を使用するなど、他のソリューションでも値の名前を変更すると失敗することに注意することが重要です。ただし、これらの場合、不正なEnum値の代わりに例外が発生します。

使用法:

// Sender usage
intent.putExtra(AwesomeEnum.SOMETHING)
// Receiver usage
val result = intent.getEnumExtra<AwesomeEnum>()

14
アプリケーションを広くすることは「本当の悪い考え」だと指摘するための+1。
bugfixr

3
私は実際には、シリアル化またはオブジェクトのバンドル(多くの変数を含む多くのオブジェクト)を扱いたくなかったプロジェクトで作業しましたが、静的グローバル変数の使用は問題ありませんでした...チームメイトが事業。それらのグローバルの使用を調整しようとするコストは、私に「それをねじ込みます。私はいくつかのParcelableを作るためのコードジェネレーターを書いています」。バグの数は大幅に減少しました
Joe Plante 2013年

2
@Coeffectええ、それは理解できる提案ですが、Nexus 4で数千の列挙型(本来、それらは状態の処理に使用されるため、ほんの数個である必要があります)を解析しない限り、ほとんどの場合、これは時期尚早の最適化と見なすことができます。 1msの改善(developerphil.com/parcelable-vs-serializable)が得られますが、追加の脚の作業に値するかどうかは
わかり

1
@rgv内部でKotlinはenum class型をプレーンJavaにクロスコンパイルしますenum。私はより簡単な回避策は道具を作ることだと思いenum classますSerializableenum class AwesomeEnum : Serializable { A, B, C }理想的ではありませんが、うまくいくはずです。
pablisco

1
@Pierreは、他の良い答えと同様に、「状況によって異なります」というものがあります。Serialisableを拡張する必要はありません。最初の答えは有効です。これらの追加オプションは、何百万ものレコードをデシリアライズする場合(そうではない場合)など、ボトルネックが発生する可能性がある場合に備えています。
思い通りの

114

列挙型にParcelableを実装させることができます。これは列挙型にとって非常に簡単です。

public enum MyEnum implements Parcelable {
    VALUE;


    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(final Parcel dest, final int flags) {
        dest.writeInt(ordinal());
    }

    public static final Creator<MyEnum> CREATOR = new Creator<MyEnum>() {
        @Override
        public MyEnum createFromParcel(final Parcel source) {
            return MyEnum.values()[source.readInt()];
        }

        @Override
        public MyEnum[] newArray(final int size) {
            return new MyEnum[size];
        }
    };
}

その後、Intent.putExtra(String、Parcelable)を使用できます。

更新:enum.values()呼び出しごとに新しい配列を割り当てるwreckgarのコメントに注意してください。

更新:Android Studioは、ParcelableEnumこのソリューションを実装するライブテンプレートを備えています。(WindowsではCtrl+を使用J


3
序数の代わりに、列挙型のtoString()およびvalueOf()メソッドを使用することもできます。
Natix 2012年

2
開発者が新しい列挙型メンバーを挿入すると、ordinal()の使用が失敗する場合があります。もちろん、列挙型メンバーの名前を変更すると、name()も壊れます。ただし、名前を変更するとプロジェクト全体をリファクタリングする必要があるため、開発者は名前を変更する代わりに新しいメンバーを挿入する可能性が高くなります。
Cheok Yan Cheng

2
追加の(または並べ替えられた)enum値が名前が変更された値よりも可能性が高いことに同意しません。IntelliJ IDEAリファクタリングのような洗練されたIDEを使用することは大したことではありません。しかし、あなたの要点は依然として良好です。シリアル化が、共存する実装全体で一貫していることを確認する必要があります。それはどんな種類のシリアライゼーションにも当てはまります。たいていの場合、1つのアプリしか実装されていない1つのアプリ内で小包が渡されるため、問題にはなりません。
Oderik 2013年

2
values()は呼び出しごとに新しい配列を作成するため、たとえば、プライベート静的配列
wreckgar23 2014年

2
一般的なケース(db storageなど)では、ordinal()が安全でないという事実は、Androidのパーセルには関係ありません。小包は長期(永続的)保管用ではありません。彼らはアプリで死ぬ。したがって、列挙型を追加/名前変更すると、新しい区画が取得されます。
noamtm 2016年

24

enumを文字列として渡すことができます。

public enum CountType {
    ONE,
    TWO,
    THREE
}

private CountType count;
count = ONE;

String countString = count.name();

CountType countToo = CountType.valueOf(countString);

与えられた文字列がサポートされていれば、問題なくenumの値を渡すことができるはずです。


3
それらすべての最も単純な実装。
Avi Cohen

22

列挙型を意図的に渡す場合、列挙型を整数に変換できます。

例:

public enum Num{A ,B}

送信(列挙から整数):

Num send = Num.A;
intent.putExtra("TEST", send.ordinal());

受け取り(整数から列挙):

Num rev;
int temp = intent.getIntExtra("TEST", -1);
if(temp >= 0 && temp < Num.values().length)
    rev = Num.values()[temp];

宜しくお願いします。:)


8
または、Num.A.name()を使用して文字列として(読みやすいように)送信し、Num.ValueOf(intent.getStringExtra( "TEST")を使用してそれを取得できます)
Benoit Jadinon

1
temp.ordinal()はordinal()値が変更される可能性があるため実際には推奨されないため、Benoitの方がより安全であると思います。この記事を参照してください。stackoverflow.com/questions/2836256/...
Shnkc

15

本当に必要な場合は、次のようにname()and を使用してvalueOf(String)、列挙型を文字列としてシリアル化できます。

 class Example implements Parcelable { 
   public enum Foo { BAR, BAZ }

   public Foo fooValue;

   public void writeToParcel(Parcel dest, int flags) {
      parcel.writeString(fooValue == null ? null : fooValue.name());
   }

   public static final Creator<Example> CREATOR = new Creator<Example>() {
     public Example createFromParcel(Parcel source) {        
       Example e = new Example();
       String s = source.readString(); 
       if (s != null) e.fooValue = Foo.valueOf(s);
       return e;
     }
   }
 }

列挙型に変更可能な状態がある場合、これは明らかに機能しません(実際にはそうするべきではありません)。


4

EnumにSerializableを実装させ、Intentを介して渡すことができる場合があります。これは、EnumをSerializableとして渡す方法があるためです。列挙型の代わりにintを使用するというアドバイスは偽です。列挙型は、コードを読みやすく、保守しやすくするために使用されます。Enumを使用できないようにするには、暗黒時代に大きく後退する必要があります。


2
すべてのEnumタイプは、デフォルトでEnumスーパークラスを拡張します。これはすでにSerializableを実装しています。
Arcao

2

Oderikの投稿について:

列挙型にParcelableを実装させることができます。これは列挙型にとって非常に簡単です。

public enum MyEnumはParcelable {...}を実装します。その後、Intent.putExtra(String、Parcelable)を使用できます。

MyEnum変数myEnumを定義してから、intent.putExtra( "Parcelable1"、myEnum)を実行すると、「メソッドputExtra(String、Parcelable)is ambiguous for the type Intent」エラーメッセージが表示されます。Intent.putExtra(String、Parcelable)メソッドもあり、元の 'Enum'タイプ自体がSerializableインターフェースを実装するため、コンパイラーはどのメソッドを選択するのかわかりません(intent.putExtra(String、Parcelable / or Serializable))。

MyEnumからParcelableインターフェースを削除し、次のようにコアコードをラップクラスのParcelable実装に移動することを提案します(Father2はParcelableであり、enumフィールドを含みます)。

public class Father2 implements Parcelable {

AnotherEnum mAnotherEnum;
int mField;

public Father2(AnotherEnum myEnum, int field) {
    mAnotherEnum = myEnum;
    mField = field;
}

private Father2(Parcel in) {
    mField = in.readInt();
    mAnotherEnum = AnotherEnum.values()[in.readInt()];
}

public static final Parcelable.Creator<Father2> CREATOR = new Parcelable.Creator<Father2>() {

    public Father2 createFromParcel(Parcel in) {
        return new Father2(in);
    }

    @Override
    public Father2[] newArray(int size) {
        return new Father2[size];
    }

};

@Override
public int describeContents() {
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(mField);
    dest.writeInt(mAnotherEnum.ordinal());
}

}

その後、次のことができます。

AnotherEnum anotherEnum = AnotherEnum.Z;
intent.putExtra("Serializable2", AnotherEnum.X);   
intent.putExtra("Parcelable2", new Father2(AnotherEnum.X, 7));

5
あなたは明示的に、たとえば、引数を「キャスト」で正しい署名を選ぶことができますintent.putExtra("myEnum", (Parcelable) enumValue);
Oderik

序数の使用は完璧です!
スロット2015

これは非常に複雑な言い方ですbundle.putExtra("key", AnotherEnum.X.ordinal())
TWiStErRob

2

enumのプリミティブデータ型を使用するには、enumコンストラクターを使用できます。

public enum DaysOfWeek {
    MONDAY(1),
    TUESDAY(2),
    WEDNESDAY(3),
    THURSDAY(4),
    FRIDAY(5),
    SATURDAY(6),
    SUNDAY(7);

    private int value;
    private DaysOfWeek(int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }

    private static final SparseArray<DaysOfWeek> map = new SparseArray<DaysOfWeek>();

    static
    {
         for (DaysOfWeek daysOfWeek : DaysOfWeek.values())
              map.put(daysOfWeek.value, daysOfWeek);
    }

    public static DaysOfWeek from(int value) {
        return map.get(value);
    }
}

intをエクストラとして渡し、その値を使用してenumからプルするために使用できます。


2

ここでParcelableの概念を使用しているほとんどの答えはJavaコードです。Kotlinで行う方が簡単です。

列挙型クラスに@Parcelizeアノテーションを付け、 Parcelableインターフェースを実装するだけです。

@Parcelize
enum class ViewTypes : Parcelable {
TITLE, PRICES, COLORS, SIZES
}

1

シンプルが好きです。

  • FredアクティビティにはHAPPY、およびの2つのモードがありSADます。
  • IntentFactoryあなたのIntentためにあなたを作成する静的を作成します。Modeあなたが欲しいものを渡します。
  • IntentFactory名称使用するMode余分の名前としてクラスを。
  • IntentFactory変換ModeString使用しましたname()
  • に入ると、onCreateこの情報はに変換されますMode
  • ordinal()Mode.values()同様に使用できます。デバッガでそれらを見ることができるので、文字列が好きです。

    public class Fred extends Activity {
    
        public static enum Mode {
            HAPPY,
            SAD,
            ;
        }
    
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.betting);
            Intent intent = getIntent();
            Mode mode = Mode.valueOf(getIntent().getStringExtra(Mode.class.getName()));
            Toast.makeText(this, "mode="+mode.toString(), Toast.LENGTH_LONG).show();
        }
    
        public static Intent IntentFactory(Context context, Mode mode){
            Intent intent = new Intent();
            intent.setClass(context,Fred.class);
            intent.putExtra(Mode.class.getName(),mode.name());
    
            return intent;
        }
    }

好奇心が強い..誰がIntentFactoryを呼び出すのか?別のアクティビティがフレッドを呼び出す方法と、フレッドがモードが渡されたことをどのように保証できるかについて詳しく説明できますか?
erik

0

あなたの最善の策は、それらのリストを文字列(またはマップ?)などのパーセル可能なものに変換して、それをアクティビティに取得することです。次に、アクティビティはそれを配列に変換する必要があります。

カスタムのパーセルを実装することは、私見で首が痛いので、できれば避けたいと思います。


0

enumのフォローを検討してください::

public static  enum MyEnum {
    ValueA,
    ValueB
}

合格のために::

 Intent mainIntent = new Intent(this,MyActivity.class);
 mainIntent.putExtra("ENUM_CONST", MyEnum.ValueA);
 this.startActivity(mainIntent);

intent / bundle / argumentsから取得するには::

 MyEnum myEnum = (MyEnum) intent.getSerializableExtra("ENUM_CONST");

0

列挙型を送信するだけの場合は、次のようなことができます。

まず、何らかの値を含む列挙型を宣言します(これはインテントを介して渡すことができます)。

 public enum MyEnum {
    ENUM_ZERO(0),
    ENUM_ONE(1),
    ENUM_TWO(2),
    ENUM_THREE(3);
    private int intValue;

    MyEnum(int intValue) {
        this.intValue = intValue;
    }

    public int getIntValue() {
        return intValue;
    }

    public static MyEnum getEnumByValue(int intValue) {
        switch (intValue) {
            case 0:
                return ENUM_ZERO;
            case 1:
                return ENUM_ONE;
            case 2:
                return ENUM_TWO;
            case 3:
                return ENUM_THREE;
            default:
                return null;
        }
    }
}

次に:

  intent.putExtra("EnumValue", MyEnum.ENUM_THREE.getIntValue());

そして、あなたがそれを取得したいとき:

  NotificationController.MyEnum myEnum = NotificationController.MyEnum.getEnumByValue(intent.getIntExtra("EnumValue",-1);

ケーキ!


0

Kotlin拡張関数を使用する

inline fun <reified T : Enum<T>> Intent.putExtra(enumVal: T, key: String? = T::class.qualifiedName): Intent =
    putExtra(key, enumVal.ordinal)

inline fun <reified T: Enum<T>> Intent.getEnumExtra(key: String? = T::class.qualifiedName): T? =
    getIntExtra(key, -1)
        .takeUnless { it == -1 }
        ?.let { T::class.java.enumConstants[it] }

これにより、同じ列挙型の複数を渡すか、デフォルトでクラス名を使用する柔軟性が得られます。

// Add to gradle
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

// Import the extension functions
import path.to.my.kotlin.script.putExtra
import path.to.my.kotlin.script.getEnumExtra

// To Send
intent.putExtra(MyEnumClass.VALUE)

// To Receive
val result = intent.getEnumExtra<MyEnumClass>()

-2

列挙型を使用しないでください。列挙型を使用しない理由#78。:)バンドルとパーセルを介して簡単にリモートできる整数を使用します。


8
@hackbod-他の77の理由は何ですか?;)しかし真剣に-列挙することには多くの利点があるようであり、それらを「リモート」にすることは厳密には難しいことではありません-それらに対するあなたの理由を拡大できる可能性はありますか?
ostergaard 2013

3
@hackbod詳しく説明してください。列挙型を使用しない場合は、APIから削除してください。
dcow 2013

2
列挙型はJava言語仕様の一部であるため、削除するのは少し難しく、Javaに準拠した実装があります:)
tad

1
どうmEnum.ordinal()ですか?要素の位置を返します
Saif Hamed 2014年

3
の値はEnum.ordinal()コンパイル時に固定されます。コメントが適用されるのは、列挙のバージョンが異なるアプリ間、または列挙内の要素の順序を変更するアプリの更新間でデータを渡す場合のみです。そのようなことは、プリミティブでないものを使用するときはいつでも危険です。1つのアプリ内のアクティビティ間でインテントを渡すにEnum.ordinal()は、完全に安全である必要があります。
Ian McLaird 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.