onActivityResult() не вызывается в новом вложенном фрагменте API
я использую новый вложенные фрагмент API, который Android включает в библиотеку поддержки.
проблема, что я столкнулся с вложенными фрагментами заключается в том, что, если вложенный фрагмент (то есть фрагмент, который был добавлен к другому фрагменту с помощью FragmentManager
возвращено getChildFragmentManager()
) называет 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>
7 ответов:
да
onActivityResult()
во вложенном фрагменте не будет вызываться таким образом.вызывающая последовательность onActivityResult (в библиотеке поддержки Android) является
Activity.dispatchActivityResult()
.FragmentActivity.onActivityResult()
.Fragment.onActivityResult()
.на 3-м шаге фрагмент находится в
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.
также, как отметитьЭрик Brynsvold в аналогичном вопросе вложенный фрагмент должен начать действие, используя его Родительский фрагмент, а не простой вызов 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); } } }
эта проблема была исправлена в Библиотека Поддержки Android 23.2 далее. Нам больше не нужно делать ничего лишнего с
Fragment
в библиотеке поддержки Android 23.2+.onActivityResult()
теперь вызывается во вложенномFragment
как и ожидалось.Я только что протестировал его с помощью библиотеки поддержки Android 23.2.1, и он работает. Наконец-то я могу иметь более чистый код!
Итак, решение состоит в том, чтобы начать использовать последнюю библиотеку поддержки Android.
у меня есть поиск источника FragmentActivity.Я нахожу эти факты.
- когда фрагмент вызывает startActivityForResult (), он фактически вызывает его Родительский FragmentActivity startAcitivityFromFragment ().
- когда фрагмент запускает активность для результата, код запроса должен быть ниже 65536 (16 бит).
- в FragmentActivity startActivityFromFragment () он вызывает Activity.startActivityForResult (), эта функция также нуждается в requestCode,но этот requestCode не равен к исходному коду запроса.
- на самом деле requestCode в FragmentActivity имеет две части. более высокое значение 16 бит (индекс фрагмента в FragmentManager) + 1.Нижний 16 бит равен исходному коду запроса. вот почему код запроса должен быть ниже 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-битный код запроса 0, чем pass в метод 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, он содержит fragment1, а fragment1 содержит fragment2. Поэтому onActivityResult может только перейти к fragment1.
и чем я найду способ решить эту проблему. Сначала создайте фрагмент расширения 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 расширить фрагмент переопределить функцию 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 расширить MyFragment.
- фрагмент2 расширить NestedFragment.
- больше не отличается.Просто вызовите startActivityForResult () на fragment2 и переопределите функцию onActivityResult ().
Варинг!!!!!!!! Код запроса должен ниже 512(8 бит), потому что мы пролили код запроса в 3 части
16 бит | 8 бит | 8 бит
чем выше 16bit Android уже используется, средний 8bit должен помочь fragment1 найти fragment2 в его массиве fragmentManager.самый низкий 8 бит-это исходный код запроса.
для основной деятельности вы пишете OnActivityForResult с суперклассом вроде как
@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); }