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支持库中)的调用顺序是
-
Activity.dispatchActivityResult()
。 -
FragmentActivity.onActivityResult()
。 -
Fragment.onActivityResult()
。
在第三步中,片段在父Activity
的FragmentMananger
中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来源。我发现这些事实。
- 当片段调用startActivityForResult()时,它实际上调用了他的父FragmentActivity的startAcitivityFromFragment()。
- 当片段开始活动结果时,requestCode必须低于65536(16bit)。
- 在FragmentActivity的startActivityFromFragment()中,它调用Activity.startActivityForResult(),这个函数也需要一个requestCode,但是这个requestCode不等于原始requestCode。
- 实际上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); } } }
}
就这样! 用法:
- fragment1扩展MyFragment。
- fragment2扩展NestedFragment。
- 没有更多不同。只需在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); }