16

Próbuję przenieść aplikację na system Android do nowej biblioteki obsługi (obsługa -4: 21.0.0) i mam problemy z uruchomieniem działań z fragmentów z przejściem .Rozpocznij aktywność od fragmentu za pomocą przejścia (obsługa interfejsu API 21)

W mojej działalności, Robiłem coś takiego:

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

które działa prawidłowo dla Działania. Jednak, gdy próbuję zrobić coś podobnego z fragmentów, takich jak:

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

okazuje się, że nie nazywa się onActivityResult() dla fragmentu, ale tylko aktywny zakrywające. Nie znalazłem niczego w bibliotece pomocy technicznej, aby przekazać pakiet opcji jako parametr do startActivityForResult() w rzeczywistym fragmencie i mieć połączenie z numerem onActivityResult() w tym fragmencie. czy to możliwe?

Najprostszym rozwiązaniem byłoby obsłużenie wszystkich wywołań onActivityResult() w samym działaniu, ale wolałbym tego nie robić, ponieważ mam mnóstwo możliwych fragmentów, które mogą odbierać to wywołanie zwrotne.

Pomoc jest doceniana. Dzięki!

Odpowiedz

14

Niestety, ActivityCompat.startActivityForResult() nie działa poprawnie w Fragments (patrz odpowiedź Alexa Lockwooda). Przez kilka tygodni zdumiewało mnie, jak Google nigdy nie dał nam metody równoważnej implementacji fragmentu startActivityForResult() Fragmenta. Co oni sobie myśleli?! Ale wtedy wpadłem na pomysł: przyjrzyjmy się, jak metoda jest faktycznie zaimplementowana.

W rzeczywistości startActivityForResult() w Fragment różni się od jednej aktywności (patrz 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); 
} 

Teraz startActivityFromFragment() wygląda następująco (patrz 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 używa jakiejś nieparzystej zmiany bajtu na kodzie żądania, aby upewnić się, że tylko wywołujący Fragment jest później wywoływany. Teraz, ponieważ ActivityCompat nie zapewnia żadnego startActivityFromFragment(), jedyną opcją jest samodzielne zaimplementowanie. Odbicie jest wymagane, aby uzyskać dostęp do prywatnego pola paczki 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); 
    } 
} 

Skopiuj tę metodę w dowolnym miejscu i użyj jej ze swojego Fragmentu. Jego numer onActivityResult() zostanie wywołany tak, jak powinien.

UPDATE: biblioteka Pomoc v23.2 został zwolniony i wydaje startActivityFromFragment(Fragment fragment, Intent intent, int requestCode, Bundle options) spełnia swoje zadanie teraz :)

+0

Rozpoczęcie działania dla wyniku przy użyciu ostatniej z podanych przeze mnie metod było dla mnie bardzo ważne - bardzo dziękuję za rozwiązanie, mimo że nie uważam go za całkowicie czysty, więc miejmy nadzieję, że Google nie zmieni nic w kwestii zmiany dziwnego bajtu. – user2302510

+2

Sądzę, że byłoby czysto wykonać połączenie z działania, a następnie przekazać wynik działania do Fragmentu, ale mając mnóstwo fragmentów załączonych do mojej aktywności (z powodu viewpagera), to sprawia, że ​​trudno jest obsłużyć – user2302510

+0

@ user2302510 Dokładnie, Staje się to bardzo mylące, aby poradzić sobie z tym samodzielnie. Cieszę się, że pomogło! – 0101100101

2

Metoda ActivityCompat#startActivityForResult() jest tylko proxy dla metody działania startActivityForResult(Intent, Bundle). Wywołanie metody z klasy fragmentów nie oznacza, że ​​Fragment zostanie ostatecznie wywołane, ponieważ jestem pewien, że się dowiedziałeś. Struktura ma wiedzę na temat tego, z której klasy pochodzi połączenie ... jedynym prawidłowym zachowaniem byłoby w tym przypadku wywołanie metody Activity 's onActivityResult().

Wygląda na to, że najlepszą opcją byłoby obsłużenie wszystkiego w metodzie aktywności onActivityResult(), zgodnie z sugestią zawartą w poście.

+0

rozumiem dlaczego tak się dzieje, ale to nie czyni tego mniej niewygodnym. Czy jest to coś, czego nie ma tylko w bibliotece Pomocy, czy też nie można tego zrobić z natywnymi fragmentami? Mam nadzieję, że dodadzą tę funkcję w przyszłości. –

+1

Czy próbowałeś po prostu wywołać metodę 'Fragment'' startActivityForResult (Intent, int, Bundle) '? –

0

Możesz utworzyć interfejs detektora lub po prostu funkcję publiczną w swoim fragmencie i przekazać argumenty, z których otrzymujesz onActivityResult(), aktywności do odbiornika lub publicznej metody fragmentu i wykonać pożądaną pracę tam.

0

Prosty sposób:

w Fragment:

ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(); 

this.startActivityFromFragment(this, intent, requestCode, options); 
Powiązane problemy