【问题标题】:Start Activity from Fragment using Transition (API 21 support)使用 Transition 从 Fragment 启动 Activity(API 21 支持)
【发布时间】:2015-01-16 06:08:05
【问题描述】:

我正在尝试将 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);

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

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

感谢您的帮助。谢谢!

【问题讨论】:

    标签: android android-support-library android-5.0-lollipop onactivityresult android-transitions


    【解决方案1】:

    可悲的是,ActivityCompat.startActivityForResult()Fragments 中不能正常工作(请参阅 Alex Lockwood 的回答)。几个星期以来,我都惊叹于 Google 从未给我们提供与 Fragment 的 startActivityForResult() 实现等效的 ActivityCompat 方法。他们在想什么?!但是后来我有了一个想法:我们来看看这个方法是怎么实现的。

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

    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() 看起来像这样(参见here):

    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 对请求代码使用了一些奇怪的字节移位,以确保之后仅调用调用 Fragment 的 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 不会改变关于奇怪的字节移位的任何事情。
    • 我想从 Activity 进行调用然后将结果从 Activity 传递给 Fragment 会更干净,但是在我的 Activity 上附加了很多片段(因为 viewpager),这使得它变得非常困难处理
    • @user2302510 确实,自己处理这个问题真的很令人困惑。很高兴它有帮助!
    【解决方案2】:

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

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

    【讨论】:

    • 我理解为什么会发生这种情况,但这并没有减少它带来的不便。这是仅在 Support 库中不存在的东西,还是不能用原生 Fragment 完成?我希望他们将来会添加此功能。
    • 你试过只调用FragmentstartActivityForResult(Intent, int, Bundle)方法吗?
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      一个简单的方法:

      在片段中:

       ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation();
      
       this.startActivityFromFragment(this, intent, requestCode, options);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-12-19
        • 2015-08-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-20
        • 2012-02-03
        相关资源
        最近更新 更多