17

我正在尝试将 Android 应用程序移植到新的支持库 (support-v4:21.0.0) 中,但在从片段启动活动时遇到了问题。

在我的活动中,我一直在做类似的事情:

Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle();
ActivityCompat.startActivityForResult(this, intent, REQUEST_SOMETHING, options);

这适用于活动。但是,如果我尝试对 Fragments 做类似的事情,例如:

Activity activity = getActivity();
Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity).toBundle();
ActivityCompat.startActivityForResult(activity, intent, REQUEST_SOMETHING, options);

事实证明,onActivityResult()不为 Fragment 调用,而只为封闭的 Activity 调用。我在支持库中没有找到任何东西可以将选项 Bundle 作为参数传递给startActivityForResult()实际的 Fragment 并让它在该 Fragment 中回调onActivityResult()。这可能吗?

最简单的解决方案是处理 Activity 本身中的所有onActivityResult()调用,但我宁愿不这样做,因为我有大量可能正在接收该回调的 Fragment。

帮助表示赞赏。谢谢!

4

4 回答 4

14

可悲的是,在(请参阅亚历克斯洛克伍德的回答)ActivityCompat.startActivityForResult()中不太正确。Fragments几个星期以来,我惊叹于 Google 从未给我们提供ActivityCompat与 Fragment 的startActivityForResult(). 他们在想什么?!但是后来我有了一个想法:我们来看看这个方法是怎么实现的。

事实上,startActivityForResult()在 Fragment 中与在 Activity 中的不同(参见此处):

public void startActivityForResult(Intent intent, int requestCode) {
    if (mActivity == null) {
        throw new IllegalStateException("Fragment " + this + " not attached to Activity");
    }
    mActivity.startActivityFromFragment(this, intent, requestCode);
}

现在startActivityFromFragment()看起来像这样(见这里):

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));
}

Google 在请求代码上使用了一些奇怪的字节移位,以确保onActivityResult()之后只调用调用 Fragment。现在由于ActivityCompat不提供任何startActivityFromFragment(),剩下的唯一选择就是自己实现它。访问包私有字段需要反射mIndex

public static void startActivityForResult(Fragment fragment, Intent intent,
                                          int requestCode, Bundle options) {
    if (Build.VERSION.SDK_INT >= 16) {
        if ((requestCode & 0xffff0000) != 0) {
            throw new IllegalArgumentException("Can only use lower 16 bits" +
                                               " for requestCode");
        }
        if (requestCode != -1) {
            try {
                Field mIndex = Fragment.class.getDeclaredField("mIndex");
                mIndex.setAccessible(true);
                requestCode = ((mIndex.getInt(this) + 1) << 16) + (requestCode & 0xffff);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        ActivityCompat.startActivityForResult(fragment.getActivity(), intent,
                                              requestCode, options);
    } else {
        fragment.getActivity().startActivityFromFragment(this, intent, requestCode);
    }
}

在您喜欢的任何地方复制该方法并从您的 Fragment 中使用它。它onActivityResult()应该被调用。

更新:支持库 v23.2 已发布,现在似乎startActivityFromFragment(Fragment fragment, Intent intent, int requestCode, Bundle options)可以完成工作了 :)

于 2015-02-26T14:51:05.637 回答
2

ActivityCompat#startActivityForResult()方法只是活动startActivityForResult(Intent, Bundle)方法的代理。从片段类内部调用该方法并不意味着最终会调用Fragment's onActivityResult(),因为我相信您已经发现了。框架知道知道调用来自哪个类的方法......在这种情况下,唯一正确的行为是调用Activity'onActivityResult()方法。

听起来最好的选择是onActivityResult()按照您在帖子中的建议处理活动方法中的所有内容。

于 2014-11-18T22:07:54.620 回答
0

您可以在片段中创建侦听器接口或简单的公共函数,并将您从中获取活动的 onActivityResult() 的参数传递给侦听器或片段的公共方法,并在那里完成您想要的工作。

于 2014-11-18T22:19:32.377 回答
0

一个简单的方法:

在片段中:

 ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation();

 this.startActivityFromFragment(this, intent, requestCode, options);
于 2016-07-21T01:29:08.747 回答