16

Ich versuche, eine Android-App in die neue Support-Bibliothek zu portieren (support-v4: 21.0.0) und habe Probleme beim Starten von Aktivitäten aus Fragmenten mit einem Übergang .Aktivität von Fragment über Transition starten (API 21-Unterstützung)

In meinen Aktivitäten, ich habe so etwas getan:

Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle(); 
ActivityCompat.startActivityForResult(this, intent, REQUEST_SOMETHING, options); 

, die für Aktivitäten gut funktioniert. Allerdings, wenn ich versuche, etwas ähnliches mit Fragmenten zu tun, wie:

Activity activity = getActivity(); 
Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity).toBundle(); 
ActivityCompat.startActivityForResult(activity, intent, REQUEST_SOMETHING, options); 

es stellt sich heraus, dass onActivityResult() nicht für das Fragment genannt wird, sondern nur die umschließenden Aktivität. Ich habe nichts in der Support-Bibliothek gefunden, um die Optionen Bundle als Parameter an startActivityForResult() auf einem tatsächlichen Fragment zu übergeben und es zurückrufen zu onActivityResult() in diesem Fragment. Ist das möglich?

Die einfachste Lösung wäre, alle onActivityResult() Aufrufe in der Aktivität selbst zu behandeln, aber ich würde lieber nicht tun, weil ich eine Tonne von möglichen Fragmenten habe, die diesen Rückruf erhalten könnten.

Hilfe ist willkommen. Vielen Dank!

Antwort

14

Leider funktioniert ActivityCompat.startActivityForResult() nicht richtig in Fragments (siehe Alex Lockwood Antwort). Seit einigen Wochen staunte ich, wie Google uns nie eine ActivityCompat Methode gab, die Fragments Implementierung von startActivityForResult() entspricht. Was dachten sie?! Aber dann hatte ich eine Idee: Sehen wir uns an, wie die Methode tatsächlich umgesetzt wird.

als eine Angelegenheit der Tatsache, startActivityForResult() in Fragmente von dem in Aktivität unterscheidet (siehe here):

public void startActivityForResult(Intent intent, int requestCode) { 
    if (mActivity == null) { 
     throw new IllegalStateException("Fragment " + this + " not attached to Activity"); 
    } 
    mActivity.startActivityFromFragment(this, intent, requestCode); 
} 

Jetzt startActivityFromFragment() sieht wie folgt aus (here sehen):

public void startActivityFromFragment(Fragment fragment, Intent intent, 
     int requestCode) { 
    if (requestCode == -1) { 
     super.startActivityForResult(intent, -1); 
     return; 
    } 
    if ((requestCode&0xffff0000) != 0) { 
     throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); 
    } 
    super.startActivityForResult(intent, 
           ((fragment.mIndex + 1) << 16) + (requestCode & 0xffff)); 
} 

Google verwendet einige ungerade Byteverschiebungen auf dem Anforderungscode, um sicherzustellen, dass nur das aufrufende Fragment onActivityResult() danach aufgerufen wird. Jetzt, da ActivityCompat keine startActivityFromFragment() bietet, ist die einzige Option übrig, es selbst zu implementieren. Für den Zugriff auf das private Paketfeld mIndex ist eine Reflektion erforderlich.

public static void startActivityForResult(Fragment fragment, Intent intent, 
              int requestCode, Bundle options) { 
    if (Build.VERSION.SDK_INT >= 16) { 
     if ((requestCode & 0xffff0000) != 0) { 
      throw new IllegalArgumentException("Can only use lower 16 bits" + 
               " for requestCode"); 
     } 
     if (requestCode != -1) { 
      try { 
       Field mIndex = Fragment.class.getDeclaredField("mIndex"); 
       mIndex.setAccessible(true); 
       requestCode = ((mIndex.getInt(this) + 1) << 16) + (requestCode & 0xffff); 
      } catch (NoSuchFieldException | IllegalAccessException e) { 
       throw new RuntimeException(e); 
      } 
     } 
     ActivityCompat.startActivityForResult(fragment.getActivity(), intent, 
               requestCode, options); 
    } else { 
     fragment.getActivity().startActivityFromFragment(this, intent, requestCode); 
    } 
} 

Kopieren Sie diese Methode wo immer Sie möchten und verwenden Sie sie von Ihrem Fragment. Seine onActivityResult() wird aufgerufen, wie es sollte.

UPDATE: Unterstützung Bibliothek v23.2 veröffentlicht wurde und es scheint startActivityFromFragment(Fragment fragment, Intent intent, int requestCode, Bundle options) jetzt den Job :)

+0

das Starten einer Aktivität für das Ergebnis mit der letzten von Ihnen bereitgestellten Methode hat für mich funktioniert - vielen Dank für die Lösung, obwohl ich sie nicht komplett sauber finde, also hoffen wir, dass Google nichts an der seltsamen Byteverschiebung ändert. – user2302510

+2

Ich denke, es wäre sauberer, den Aufruf von Activity zu machen und dann das Ergebnis von der Aktivität an das Fragment zu übergeben, aber mit vielen Fragmenten, die an meine Aktivität angehängt sind (wegen viewpager), macht es ziemlich schwierig zu handhaben – user2302510

+0

@ user2302510 es wird wirklich verwirrend, das alleine zu bewältigen. Froh, dass es geholfen hat! – 0101100101

2

Die ActivityCompat#startActivityForResult() Methode ist nur ein Proxy für die startActivityForResult(Intent, Bundle) Methode der Aktivität. Der Aufruf der Methode innerhalb einer Fragmentklasse bedeutet nicht, dass die Fragment 's onActivityResult() schließlich aufgerufen wird, wie Sie sicher herausgefunden haben. Das Framework weiß, aus welcher Klasse der Aufruf stammt ... das einzig korrekte Verhalten wäre, in diesem Fall die Methode Activity aufzurufen.

Es klingt wie die beste Option wäre, alles in der onActivityResult() Methode der Aktivität zu behandeln, wie Sie in Ihrem Beitrag vorgeschlagen.

+0

Ich verstehe, warum Das passiert, aber das macht es nicht weniger unbequem. Ist das etwas, das nur in der Support-Bibliothek fehlt, oder kann es auch nicht mit nativen Fragmenten gemacht werden? Ich hoffe, dass sie dieses Feature in Zukunft hinzufügen. –

+1

Haben Sie versucht, einfach die Methode 'startActivityForResult (Intent, int, Bundle) 'des' Fragment' aufzurufen? –

0

Sie können eine Listener-Schnittstelle oder einfach eine öffentliche Funktion in Ihrem Fragment erstellen und die Argumente, von denen Sie inActivityResult() der Aktivität erhalten, an den Listener oder die öffentliche Methode des Fragments übergeben und die gewünschte Arbeit ausführen Dort.

0

Eine einfache Art und Weise:

in Fragmente:

ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(); 

this.startActivityFromFragment(this, intent, requestCode, options);