[Android]嵌套Fragment以及startActivityForResult()

来源:互联网 发布:罗技 无线鼠标 知乎 编辑:程序博客网 时间:2024/04/29 01:50

原文地址:http://blog.shamanland.com/2014/01/nested-fragments-for-result.html

这篇博客专门用于讲解Support Library里的嵌套Fragment以及它们的一个问题(即在嵌套的Fragment中调用startActivityForResult)。

在Fragment里有以下两个可用的方法:startActivityForResult()和onActivityResult()。第一个方法是对于FragmentActivity.startActivityFromFragment的代理调用,第二个方法是在FragmentActivity.onActivityResult()里被调用。

如果这种操作仅仅包裹了Activity,那么结果result是如何传递到Fragment实例中的?

奇妙之处就隐藏在参数requestCode里,对于这个参数,FragmentActivity只允许外部使用低于16位的数字(0~65535),而高于16位的位置将被用于保存FragmentManager中Fragment的索引。

android.support.v4.app.FragmentActivity

FragmentActivity会通过修改来替换掉requestCode。之后,当onActivityResult被调用的时候,FragmentActivity会解析高16位并恢复原始Fragment的索引。见以下流程图:

也就是说在FragmentActivity中,修改requestCode,把Fragment的索引保存到requestCode的大于16位的位置里。然后用这个新的requestCode调用父类的方法。之后回调结果到了FragmentActivity中,再解析这个requestCode,得到索引,找到对应的Fragment,再把结果传给这个Fragment。

这个按照数字位数的方式可以允许我们在Fragment中调用startActivityForResult,并且在Fragment里处理结果。那么,有什么问题呢?

在Activity这一基础层级里有几个并列的Fragment是没有问题的。但是如果Fragment嵌套了,例如在ViewPager里面的Fragment里又包含了几个tab,那么就肯定会遇到这个问题(或者已经遇到了)。

嵌套的Fragment.onActivityResult()不会被调用。

因为只有一个索引会保存到requestCode里。那就是当前Fragment在FragmentManager中的索引。当我们使用嵌套Fragment的时候,就会有child FragmentManager,它会持有一个自己的Fragment列表。所以就很有必要从顶级FragmentManager开始保存全部索引

在嵌套的Fragments里,对于相同索引的Fragment,只有最后一个索引会被保存到发出请求的requestCode中。就像上图中保存到requestCode中的是Level2中第一个Fragment索引,但是结果是从上往下开始传递的,这样就会把结果传递到Level1中的第一个Fragment里。

该怎么改正这个问题呢?因为对于requestCode,我们只能使用16位以下的数字,那么就尝试完全跳过这种限制,使用32位数字。但是这时FragmentActivity会因为我们使用了一个大于16位的数字而抛出一个异常(见图一)。我在问题跟踪上创建了一个issue来描述这种情况。

在这个问题还没有被修复的时候,我们能做的就是:用一个16位的数字来保存自己定义的Fragment索引和requestCode。

前3位表示第一级的fragment,可用于标识7个;之后的3位表示第二级;再往后的3位标识第三集;最后的7位来表示requestCode。

真正的应用中不会有很多的Fragment,典型情况就是一屏中包含1~4个。很少有应用有比较复杂的嵌套结构,只是通过ViewPager嵌套1~2层。由于用高字节为来保存Fragment索引,那么最后的整形变量requestCode就会变得很大(一个整形变量对应一个索引),所以我们应该减少保存自定义索引的位的个数。

例如:3位就可以保存0~7的任意一个数字,代表一级。那么为了预留3级嵌套就需要用整个requestCode中的9位。剩余7位(数字0~127)可以用于其他用途。

可以从github上检出这个解决方案,如果检出的是“Initial commit”这个版本,就会发现以上所描述的问题。在这个代码仓库的HEAD里,这个问题已经被修复了。

当然,这种解决方案会存在更多的限制:

  • requestCode不能大于127。
  • 嵌套不能超过3层。
  • 同一层不能超过7个Fragment。

但是对于真实的应用来说,这些限制不会造成什么影响!

0 0