16

Sto provando a portare un'app per Android nella nuova libreria di supporto (support-v4: 21.0.0) e sto riscontrando problemi nell'avviare Attività da Frammenti con una transizione .Avvia attività da Fragment usando Transition (supporto API 21)

nelle mie attività, ho fatto qualcosa di simile:

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

che funziona bene per le Attività. Tuttavia, se cerco di fare qualcosa di simile con Fragments, come:

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

si scopre che onActivityResult() non si chiama per il frammento, ma solo la racchiude attività. Non ho trovato nulla nella libreria di supporto per passare il pacchetto di opzioni come parametro a startActivityForResult() su un frammento effettivo e chiamarlo a onActivityResult() in quel frammento. È possibile?

La soluzione più semplice sarebbe gestire tutte le chiamate onActivityResult() nell'attività stessa, ma preferirei non farlo perché ho un sacco di possibili frammenti che potrebbero ricevere quella richiamata.

L'aiuto è apprezzato. Grazie!

risposta

14

Purtroppo, ActivityCompat.startActivityForResult() non funziona perfettamente nel Fragments (vedere la risposta di Alex Lockwood). Per diverse settimane mi sono meravigliato di come Google non ci abbia mai fornito un metodo ActivityCompat equivalente all'implementazione di Fragment di startActivityForResult(). Cosa stavano pensando?! Ma poi ho avuto un'idea: diamo un'occhiata a come il metodo è effettivamente implementato.

È un dato di fatto, startActivityForResult() nel frammento è diverso da quello di attività (vedi 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); 
} 

Ora startActivityFromFragment() si presenta così (vedi here):

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 utilizza alcuni byte dispari che cambiano sul codice di richiesta per assicurarsi che solo il onActivityResult() di Fragment chiamante venga chiamato in seguito. Ora dal ActivityCompat non viene fornito alcun startActivityFromFragment(), l'unica opzione rimasta è quella di implementarlo da soli. È richiesto Reflection per accedere al campo privato del pacchetto mIndex.

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

copia che il metodo da nessuna parte che ti piace e usarlo dal Fragment. Il suo numero onActivityResult() verrà chiamato come dovrebbe.

UPDATE: biblioteca di sostegno v23.2 è stato liberato e sembra startActivityFromFragment(Fragment fragment, Intent intent, int requestCode, Bundle options) fa il lavoro ora :)

+0

avviare un'attività per risultato utilizzando l'ultimo metodo che hai fornito ha funzionato per me - grazie mille per la soluzione anche se non la trovo completamente pulita, quindi speriamo solo che Google non cambi nulla riguardo lo strano cambio di byte. – user2302510

+2

Suppongo che sarebbe più pulito effettuare la chiamata da Activity e quindi passare il risultato dall'attività al Fragment ma avendo un sacco di frammenti collegati alla mia attività (a causa di viewpager) rende piuttosto difficile gestire – user2302510

+0

@ user2302510 Esattamente, diventa davvero difficile gestirlo da solo. Sono contento che ci abbia aiutato! – 0101100101

2

Il metodo ActivityCompat#startActivityForResult() è solo un proxy per il metodo startActivityForResult(Intent, Bundle) dell'attività. Chiamare il metodo dall'interno di una classe di frammenti non significa che il onActivityResult()Fragment verrà chiamato come sicuro che tu abbia scoperto. Il framework ha modo di sapere da quale classe è stata originata la chiamata ... l'unico comportamento corretto sarebbe chiamare in questo caso il metodo onActivityResult() .

Sembra che l'opzione migliore sarebbe gestire tutto nel metodo onActivityResult() dell'attività come suggerito nel post.

+0

capisco perché questo sta accadendo, ma questo non lo rende meno problematico. È qualcosa che è solo assente nella libreria di supporto, o non può essere fatto con frammenti nativi o? Spero che aggiungano questa funzionalità in futuro. –

+1

Hai provato a chiamare il metodo 'startActivityForResult (Intent, int, Bundle)' 'Fragment'? –

0

È possibile creare un'interfaccia listener o semplicemente una funzione pubblica nel proprio frammento e passare gli argomenti da cui si ottiene onActivityResult() dell'attività al listener o al metodo pubblico del frammento e eseguire il lavoro desiderato su Là.

0

Un modo semplice:

in Frammento:

ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(); 

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