Я использую новый 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>
Да, onActivityResult()
во вложенном фрагменте не будет вызван таким образом.
Вызывающая последовательность onActivityResult (в библиотеке поддержки Android)
Activity.dispatchActivityResult()
. FragmentActivity.onActivityResult()
. 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. Я нахожу эти факты.
Просмотрите код в 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); } } }
}
Это все! Применение:
Уоринг !!!!!!!! Код запроса должен соответствовать 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); }