OnActivityResult () не вызывается в новом вложенном фрагменте API

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

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

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

 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:

 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" > </FrameLayout> 

Solutions Collecting From Web of "OnActivityResult () не вызывается в новом вложенном фрагменте API"

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

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

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

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

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

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

В фрагменте контейнера переопределяют onActivityResult следующим образом:

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

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

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

 getParentFragment().startActivityForResult(intent, requestCode); 

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

В работе:

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); List<Fragment> 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<Fragment> frags = frag.getChildFragmentManager().getFragments(); if (frags != null) { for (Fragment f : frags) { if (f != null) handleResult(f, requestCode, resultCode, data); } } } 

Я искал источник FragmentActivity. Я нахожу эти факты.

  1. Когда фрагмент вызывает startActivityForResult (), он фактически вызывает его родительский FragmentActivity's startAcitivityFromFragment ().
  2. Когда операция начала фрагментации для результата, код запроса должен быть ниже 65536 (16 бит).
  3. В startActivityFromFragment () FragmentActivity, он вызывает Activity.startActivityForResult (), эта функция также нуждается в коде запроса, но этот код запроса не равен исходному запросуCode.
  4. На самом деле requestCode в FragmentActivity имеет две части. Более высокое 16-битное значение (индекс фрагмента в 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)); } 

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

  @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<Fragment> activeFragments = mFragments.getActiveFragments(new ArrayList<Fragment>(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, она содержит фрагмент1, а фрагмент1 содержит фрагмент2. Так что onActivityResult может перейти только к фрагменту1.

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

 public abstract class NestedFragment extends Fragment { @Override public void startActivityForResult(Intent intent, int requestCode) { List<Fragment> 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<Fragment> fragments = getChildFragmentManager().getFragments(); if (fragments != null && fragments.size() > index) { fragments.get(index).onActivityResult(requestCode & 0x00ff, resultCode, data); } } } 

}

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

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

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

16 бит | 8bit | 8bit

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

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

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

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

Для основной деятельности вы пишете OnActivityForResult с классом 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 для фрагмента

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