我的首页架构是以activity中一个viewpager,viewpager以activity填充。
ViewPager非常适合用于实现多页面的滑动切换效果
相同的多页面切换可以是TabHost,但是tabhost标题栏需要重写,稍微麻烦一点所以采用ViewPager来实现滑动切换,网上很多都是使用ViewPager来加载View,做一些静态的页面展示,像导航,图片展示,使用教程等等
具体请看 http://www.cnblogs.com/skiz/archive/2012/10/26/2741649.html
现在定义一下NavActivity中有四个acitivity,其中一个是userActivity,
userActivity中有一个fragment,fragment需要上传头像,所以肯定要跳到onActivityResult()方法
关于onActivityResult()写到哪会调到的问题,相信大家都知道,这个case中fragment中startActivityForResult调onActivityResult()是不行的,(具体参考:http://www.tuicool.com/articles/2eM32a)
Case :当我们从一个Activity启动了一个Fragment,然后在这个Fragment中又去实例化了一些子Fragment,在子Fragment中去有返回的启动了另外一个Activity,即通过startActivityForResult方式去启动,这时候造成的现象会是,子Fragment接收不到OnActivityResult,如果在子Fragment中是以getActivity.startActivityForResult方式启动,那么只有Activity会接收到OnActivityResult,如果是以getParentFragment.startActivityForResult方式启动,那么只有父Fragment能接收(此时Activity也能接收),但无论如何子Fragment接收不到OnActivityResult。
这是一个非常奇怪的现象,按理说,应该是让子Fragment接收到OnActivityResult才对,究竟是什么造成的呢?这是由于某位写代码的员工抱怨没发奖金,稍稍偷懒了,少写了一部分代码,没有考虑到Fragment再去嵌套Fragment的情况。
我们来看看FragmentActivity中的代码:
<span style="font-size:18px;">protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
this.mFragments.noteStateNotSaved();
int index = requestCode >> 16;
if (index != 0) {
index--;
if ((this.mFragments.mActive == null) || (index < 0) || (index >= this.mFragments.mActive.size())) {
Log.w("FragmentActivity", "Activity result fragment index out of range: 0x" + Integer.toHexString(requestCode));
return;
}
Fragment frag = (Fragment)this.mFragments.mActive.get(index);
if (frag == null) {
Log.w("FragmentActivity", "Activity result no fragment exists for index: 0x" + Integer.toHexString(requestCode));
}
else {
frag.onActivityResult(requestCode & 0xFFFF, resultCode, data);
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}</span>
很显然,设计者把Fragment的下标+1左移16位来标记这个request是不是Fragment的,拿到result再解码出下标,直接取对应的Fragment,这样并没有去考虑对Fragment嵌套Fragment做一个Map映射,所以出现了这种BUG。
但是如果我们需要在OnActivityResult的时候处理一些事情的话,我们可以通过在子Fragment中以getParentFragment.startActivityForResult的方式来启动,然后在父Fragment中去接收数据,我们需要在子Fragment中提供一个方法,如:getResultData(Object obj),通过父Fragment中的子Fragment实例去调用这个方法,把相应的数据传过去,然后去更新子Fragment。
但是我这里是不是可以这样解决呢?
NO,事情远没有你想象的那么简单,不过上边的案例对于部分人还是有用的吧!
鉴于这种办法不行,于是我开始调用getActivity().startActivityForResult,
查看UserActicity中的OnActivityResult是不是会执行呢?
NO,WHY?
我还是不死心,于是那我想是不是在navActivity中的OnActivityResult会执行,如果他都不执行的话,没天理啊。
NO,真实的情况就是没天理,还是没有执行,天啊,他难道被丢弃了?
接下里,已经遍体鳞伤的我,带着一颗破碎的心,查看了startActivityForResult的源码:
<span style="font-size:18px;">public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
final View decor = mWindow != null ? mWindow.peekDecorView() : null;
if (decor != null) {
decor.cancelPendingInputEvents();
}
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}</span>
结果发现他竟然走了这:
注意我们要为现有的应用程序,可以通过这种方法去覆盖它的兼容性
<span style="font-size:18px;"> // Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);</span>
接着我看了startActivityFromChild,
<span style="font-size:18px;"> public void startActivityFromChild(Activity child, Intent intent,
int requestCode, Bundle options) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, child,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, child.mEmbeddedID, requestCode,
ar.getResultCode(), ar.getResultData());
}
}</span>
走到这里,ar为null,你的请求真的就被丢弃了,
但是为什么?为什么会走这条路呢?
我们回到启动的时候,getActivity(). startActivityForResult
这里的getActivity()是USerActivity,把他带进来,
<span style="font-size:18px;">if (mParent == null) {。。。。。。</span>
<span style="font-size:18px;"> } else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}</span>
这里的
<span style="font-size:18px;">mParent 不是null啊,他外边还有NavActivity啊。
于是走了下边这条路,然后到了
startActivityFromChild这里,
他去找主线程里的UserActivity,但是没找到,ar=null,(这里我不是很理解,也不知道理解的对不对,如果不对,请大神指正。)
</span>
如果是因为这样,那我直接传NavActivity,应该是没有问题了吧,
于是我((Activity) getActivity().getParent()).startActivityForResult,
他直接走了
<span style="font-size:18px;">mParent==null 这条路,于是onActivityResult终于执行了。后边的问题,
不过是广播或者观察者模式更新view的问题了,大家有什么问题,可以留言,有时间一定和大家相互学习。</span>
有问题,请指正,谢谢!