使用转场效果(API 21支持),从Fragment启动Activity

17

我想将一个Android应用程序移植到新的支持库(support-v4:21.0.0),但是我在Fragments中使用转换启动Activities时遇到了麻烦。

在我的Activities中,我一直在做这样的事情:

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

这对于Activities起作用得很好。但是,如果我尝试对Fragment执行类似的操作,比如:

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

事实证明,onActivityResult() 方法不仅适用于 Fragment,而是只适用于包含 Fragment 的 Activity。我没有在支持库中找到任何传递选项 Bundle 作为参数到一个实际的 Fragment 中,并让它回调到该 Fragment 中的 onActivityResult() 的方法。这有可能吗?

最简单的解决方案是在 Activity 自身中处理所有的 onActivityResult() 调用,但我不想这样做,因为可能会有很多 Fragments 收到该回调。

感谢帮助。谢谢!

4个回答

14

遗憾的是,ActivityCompat.startActivityForResult()Fragments中并不能正常工作(请参见Alex Lockwood的回答)。几周来我一直感叹Google从未给我们提供与Fragment实现的startActivityForResult()等效的ActivityCompat方法。他们在想什么?!但后来我有了一个想法:让我们看看这种方法实际上是如何实现的。

事实上,在Fragment中的startActivityForResult()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));
}

谷歌对请求代码进行了一些奇怪的字节移位,以确保仅在调用片段之后调用 onActivityResult() 。现在,由于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) 可以胜任这项工作:)


使用您提供的最后一种方法启动活动对我很有效-非常感谢解决方案,尽管我认为它并不完全干净,所以让我们希望Google不会改变任何关于奇怪字节移位的内容。 - user1071762
2
我想从Activity中进行调用并将结果从Activity传递到Fragment会更加清晰,但是由于ViewPager附加了大量的Fragment到我的Activity中,这使得处理变得非常困难。 - user1071762
@user2302510 没错,自己处理这个确实很令人困惑。很高兴能帮到你! - 0101100101

2
ActivityCompat#startActivityForResult()方法只是活动的startActivityForResult(Intent, Bundle)方法的代理。从片段类中调用该方法并不意味着片段的onActivityResult()最终将被调用,我相信您已经发现了这一点。框架无法知道调用来源的类...在这种情况下,唯一正确的行为是调用ActivityonActivityResult()方法。

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


我理解为什么会发生这种情况,但这并不意味着它不方便。这是仅在支持库中缺失的功能吗?还是原生片段也无法实现?我希望他们将来能够添加此功能。 - Damian Wieczorek
1
你尝试过直接调用FragmentstartActivityForResult(Intent, int, Bundle)方法吗? - Alex Lockwood

0
一个简单的方法:
在Fragment中:
 ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation();

 this.startActivityFromFragment(this, intent, requestCode, options);

0
您可以在片段中创建一个监听器接口或者一个公共函数,并将从活动的onActivityResult()方法中获取的参数传递给监听器或片段的公共方法,然后在那里执行所需的操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接