onActivityResult () не вызывается в новом вложенном fragmentе API

Я использую новый API вложенного fragmentа, который Android включает в библиотеку поддержки.

Проблема, с которой я сталкиваюсь с вложенными fragmentами, заключается в том, что если вложенный fragment (то есть fragment, который был добавлен в другой fragment через FragmentManager возвращенный startActivityForResult() getChildFragmentManager() ), вызывает startActivityForResult() , метод startActivityForResult() вложенного fragmentа не называется. Тем не менее, onActivityResult() как родительский fragment onActivityResult() и onActivityResult() активности.

Я не знаю, не хватает ли я чего-то о вложенных fragmentах, но я не ожидал описанного поведения. Ниже приведен код, воспроизводящий эту проблему. Я был бы очень признателен, если кто-то может указать мне в правильном направлении и объяснить мне, что я делаю неправильно:

 package com.example.nestedfragmentactivityresult; import android.media.RingtoneManager; import android.os.Bundle; import android.content.Intent; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.Toast; public class MainActivity extends FragmentActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.getSupportFragmentManager() .beginTransaction() .add(android.R.id.content, new ContainerFragment()) .commit(); } public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // This is called Toast.makeText(getApplication(), "Consumed by activity", Toast.LENGTH_SHORT).show(); } public static class ContainerFragment extends Fragment { public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View result = inflater.inflate(R.layout.test_nested_fragment_container, container, false); return result; } public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getChildFragmentManager().beginTransaction() .add(R.id.content, new NestedFragment()) .commit(); } public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // This is called Toast.makeText(getActivity(), "Consumed by parent fragment", Toast.LENGTH_SHORT).show(); } } public static class NestedFragment extends Fragment { public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Button button = new Button(getActivity()); button.setText("Click me!"); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); startActivityForResult(intent, 0); } }); return button; } public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // This is NOT called Toast.makeText(getActivity(), "Consumed by nested fragment", Toast.LENGTH_SHORT).show(); } } } 

test_nested_fragment_container.xml:

    

7 Solutions collect form web for “onActivityResult () не вызывается в новом вложенном fragmentе API”

Да, во onActivityResult() fragmentе onActivityResult() не будет вызван таким образом.

Вызывающая последовательность onActivityResult (в библиотеке поддержки Android)

  1. Activity.dispatchActivityResult() .
  2. FragmentActivity.onActivityResult() .
  3. Fragment.onActivityResult() .

На третьем этапе fragment находится в FragmentMananger родительской Activity . Таким образом, в вашем примере это fragment контейнера, который обнаружен для отправки onActivityResult() , вложенный fragment никогда не сможет получить событие.

Я думаю, вам нужно реализовать свою собственную отправку в ContainerFragment.onActivityResult() , найти вложенный fragment и вызвать передачу результата и данных на него.

Я решил эту проблему со следующим кодом (используется библиотека поддержки):

В fragmentе контейнера переопределите onActivityResult таким образом:

 @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); List fragments = getChildFragmentManager().getFragments(); if (fragments != null) { for (Fragment fragment : fragments) { fragment.onActivityResult(requestCode, resultCode, data); } } } 

Теперь вложенный fragment получит вызов метода onActivityResult.

Кроме того, как отметил Эрик Бринсволд в аналогичном вопросе, вложенный fragment должен начинать активность с использованием его родительского fragmentа, а не простого вызова startActivityForResult (). Таким образом, во вложенной операции начала fragmentа с:

 getParentFragment().startActivityForResult(intent, requestCode); 

Вот как я это решил.

В работе:

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); List frags = getSupportFragmentManager().getFragments(); if (frags != null) { for (Fragment f : frags) { if (f != null) handleResult(f, requestCode, resultCode, data); } } } private void handleResult(Fragment frag, int requestCode, int resultCode, Intent data) { if (frag instanceof IHandleActivityResult) { // custom interface with no signitures frag.onActivityResult(requestCode, resultCode, data); } List frags = frag.getChildFragmentManager().getFragments(); if (frags != null) { for (Fragment f : frags) { if (f != null) handleResult(f, requestCode, resultCode, data); } } } 

Эта проблема была исправлена ​​в Android Support Library 23.2 и далее. Нам больше не нужно делать что-либо лишнее с помощью Fragment в Android Support Library 23.2+. onActivityResult() теперь вызывается во вложенном Fragment как ожидалось.

Я только что протестировал его, используя Android Support Library 23.2.1, и он работает. Наконец, я могу получить более чистый код!

Итак, решение – начать использовать последнюю библиотеку поддержки Android.

У меня есть поиск источника FragmentActivity. Я нахожу эти факты.

  1. когда fragment вызывает startActivityForResult (), он фактически вызывает его родительский FragmentActivity’s startAcitivityFromFragment ().
  2. когда операция начала fragmentации для результата, код запроса должен быть ниже 65536 (16 бит).
  3. в startActivityFromFragment () FragmentActivity, он вызывает Activity.startActivityForResult (), эта функция также нуждается в requestCode, но этот код запроса не равен исходному запросуCode.
  4. на самом деле requestCode в FragmentActivity имеет две части. более высокое 16-битное значение (индекс fragmentа в FragmentManager) + 1. нижний 16-бит равен коду запроса origin. поэтому requestCode должен быть ниже 65536.

просмотрите код в FragmentActivity.

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

при возврате результата. FragmentActivity переопределяет функцию onActivityResult и проверяет, не превышает ли высший 16 бит requestCode 0, чем передать onActivityResult его fragmentу.

  @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { mFragments.noteStateNotSaved(); int index = requestCode>>16; if (index != 0) { index--; final int activeFragmentsCount = mFragments.getActiveFragmentsCount(); if (activeFragmentsCount == 0 || index < 0 || index >= activeFragmentsCount) { Log.w(TAG, "Activity result fragment index out of range: 0x" + Integer.toHexString(requestCode)); return; } final List activeFragments = mFragments.getActiveFragments(new ArrayList(activeFragmentsCount)); Fragment frag = activeFragments.get(index); if (frag == null) { Log.w(TAG, "Activity result no fragment exists for index: 0x" + Integer.toHexString(requestCode)); } else { frag.onActivityResult(requestCode&0xffff, resultCode, data); } return; } super.onActivityResult(requestCode, resultCode, data); } 

так вот как FragmentActivity отправляет событие onActivityResult.

когда у вас есть функция fragmentActivity, она содержит fragment1, а fragment1 содержит fragment2. Так что onActivityResult может перейти только к fragmentу1.

И чем я нахожу способ решить эту проблему. Сначала создайте fragment расширенного fragmentа NestedFragment, переопределите функцию startActivityForResult.

 public abstract class NestedFragment extends Fragment { @Override public void startActivityForResult(Intent intent, int requestCode) { List fragments = getFragmentManager().getFragments(); int index = fragments.indexOf(this); if (index >= 0) { if (getParentFragment() != null) { requestCode = ((index + 1) < < 8) + requestCode; getParentFragment().startActivityForResult(intent, requestCode); } else { super.startActivityForResult(intent, requestCode); } } } 

}

чем создание MyFragment, расширение Fragment переопределяет функцию onActivityResult.

 public abstract class MyFragment extends Fragment { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { int index = requestCode >> 8; if (index > 0) { //from nested fragment index--; List fragments = getChildFragmentManager().getFragments(); if (fragments != null && fragments.size() > index) { fragments.get(index).onActivityResult(requestCode & 0x00ff, resultCode, data); } } } 

}

Это все! Применение:

  1. fragment1 расширяет MyFragment.
  2. fragment2 расширяет NestedFragment.
  3. Больше не нужно. Просто вызовите startActivityForResult () на fragmentе2 и переопределите функцию onActivityResult ().

Уоринг !!!!!!!! Код запроса должен соответствовать 512 (8 бит), потому что мы пролили requestCode на 3 части

16bit | 8bit | 8bit

более высокий 16-битный Android уже используется, средний 8 бит - помочь fragmentу1 найти fragment2 в его массиве arrayManager. Самый низкий 8 бит - это код запроса originCode.

Для основной деятельности вы пишете OnActivityForResult с classом Super, например, как

  @Override public void onActivityResult(int requestCode, int resultCode, Intent result) { super.onActivityResult(requestCode, resultCode, result); if (requestCode == Crop.REQUEST_PICK && resultCode == RESULT_OK) { beginCrop(result.getData()); } } 

Для активности Напиши намерение без getActivity (), например, как

  Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts")); pickContactIntent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers startActivityForResult(pickContactIntent, 103); 

OnActivityResult для fragmentа

  @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 101 && resultCode == getActivity().RESULT_OK && null != data) { 

}}

Я столкнулся с той же проблемой! Я решил это, используя статический ChildFragment в ParentFragment , например:

 private static ChildFragment mChildFragment; protected void onActivityResult(int requestCode, int resultCode, Intent data) { mChildFragment.onActivityResult(int requestCode, int resultCode, Intent data); } 
Давайте будем гением компьютера.