onActivityResult()不在新的嵌套片段API中调用

我一直在使用支持库中包含的新的嵌套的片段 API。

我面临的嵌套片段的问题是,如果嵌套片段(即,已经通过由getChildFragmentManager()返回的FragmentManager添加到另一个片段的FragmentManager )调用startActivityForResult() ,则嵌套片段的onActivityResult()方法不叫。 但是,父级片段的onActivityResult()和activity的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支持库中)的调用顺序是

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

在第三步中,片段在父ActivityFragmentMananger中find。 所以在你的例子中,它是发现onActivityResult()的容器片段,嵌套的片段永远不会收到事件。

我想你必须在ContainerFragment.onActivityResult()实现你自己的调度,find嵌套的片段,并调用结果和数据传递给它。

我用下面的代码解决了这个问题(使用支持库):

在容器分片覆盖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方法的调用。

此外,正如Eric 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之后解决了。 在Android支持库23.2+中,我们不必再使用Fragment做任何事情。 onActivityResult()现在在嵌套的Fragment按预期调用。

我刚刚使用Android支持库23.2.1testing它,它的工作原理。 最后我可以有更清晰的代码!

所以,解决scheme是开始使用最新的Android支持库。

我有searchFragmentActivity来源。我发现这些事实。

  1. 当片段调用startActivityForResult()时,它实际上调用了他的父FragmentActivity的startAcitivityFromFragment()。
  2. 当片段开始活动结果时,requestCode必须低于65536(16bit)。
  3. 在FragmentActivity的startActivityFromFragment()中,它调用Activity.startActivityForResult(),这个函数也需要一个requestCode,但是这个requestCode不等于原始requestCode。
  4. 实际上FragmentActivity中的requestCode有两个部分。 更高的16位值是(片段pipe理器中的片段索引)+1。低16位等于原始请求代码。 这就是为什么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)); } 

当结果back.FragmentActivity覆盖onActivityResult函数,并检查requestCode的高16位不是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,它包含fragment1,fragment1包含fragment2。 所以onActivityResult只能传递给fragment1。

比我find解决这个问题的办法。 首先创build一个NestedFragment,使用extend Fragment覆盖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); } } } 

}

比创buildMyFragment扩展片段覆盖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. fragment1扩展MyFragment。
  2. fragment2扩展NestedFragment。
  3. 没有更多不同。只需在fragment2上调用startActivityForResult(),并重写onActivityResult()函数。

华林!!!!!!!! requestCode必须低于512(8bit),因为我们把requestCode分成了三部分

16bit | 8bit | 8位

已经使用的更高的16位Android,中间的8位是帮助fragment1在他的fragmentManager数组中findfragment2,最低的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中的静态ChildFragment解决了这个ParentFragment ,如下所示:

 private static ChildFragment mChildFragment; protected void onActivityResult(int requestCode, int resultCode, Intent data) { mChildFragment.onActivityResult(int requestCode, int resultCode, Intent data); }