I have search the FragmentActivity source.I find these facts.
- when fragment call startActivityForResult(), it actually call his parent FragmentActivity's startAcitivityFromFragment().
- when fragment start activity for result,the requestCode must below 65536(16bit).
- in FragmentActivity's startActivityFromFragment(),it call Activity.startActivityForResult(), this function also need a requestCode,but this requestCode is not equal to the origin requestCode.
- actually the requestCode in FragmentActivity has two part. the higher 16bit value is (the fragment's index in FragmentManager) + 1.the lower 16bit is equal to the origin requestCode. that's why the requestCode must below 65536.
watch the code in 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));
}
when result back.FragmentActivity override the onActivityResult function,and check if the requestCode's higher 16bit is not 0,than pass the onActivityResult to his children fragment.
@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);
}
so that's how FragmentActivity dispatch onActivityResult event.
when your have a fragmentActivity, it contains fragment1, and fragment1 contains fragment2.
So onActivityResult can only pass to fragment1.
And than I find a way to solve this problem.
First create a NestedFragment extend Fragment override the startActivityForResult function.
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);
}
}
}
}
than create MyFragment extend Fragment override onActivityResult function.
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);
}
}
}
}
That's all!
Usage:
- fragment1 extend MyFragment.
- fragment2 extend NestedFragment.
- No more different.Just call startActivityForResult() on fragment2, and override the onActivityResult() function.
Waring!!!!!!!!
The requestCode must bellow 512(8bit),because we spilt the requestCode in 3 part
16bit | 8bit | 8bit
the higher 16bit Android already used,the middle 8bit is to help fragment1 find the fragment2 in his fragmentManager array.the lowest 8bit is the origin requestCode.