android - 不同 Activity fragment 之间的共享元素转换

标签 android android-fragments animation material-design shared-element-transition

我正在尝试在不同 Activite 的 fragment 之间实现共享元素转换,我已经实现了共享元素进入转换,但无法在反压时管理返回转换。

fragment A 托管在 Activity A 中,单击按钮时会添加一个图像作为共享元素,然后启动 Activity B,其中托管包含共享元素目标 View 的 fragment B。

Activity A:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_a);

    getSupportFragmentManager()
            .beginTransaction()
            .addToBackStack(null)
            .replace(R.id.content, FragmentA.newInstance())
            .commit();
}

@Override
public void onBackPressed() {
    super.onBackPressed();
    finishAfterTransition();
}

fragment A:

 @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    postponeEnterTransition();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
        setSharedElementReturnTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
    }
}


@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        final ImageView imageView = (ImageView) view.findViewById(R.id.simple_activity_a_imageView);

        Button button = (Button) view.findViewById(R.id.simple_activity_a_btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getActivity(), ActivityB.class);
                ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
                        getActivity(),
                        imageView,
                        ViewCompat.getTransitionName(imageView));
                startActivity(intent, options.toBundle());
            }
        });

    }

Activity B:

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_fragment_to_fragment);

    getSupportFragmentManager()
            .beginTransaction()
            .addToBackStack(null)
            .replace(R.id.content, FragmentB.newInstance())
            .commit();
}

@Override
public void onBackPressed() {
    super.onBackPressed();
    finishAfterTransition();
}

fragment B:

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    postponeEnterTransition();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
        setSharedElementReturnTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
    }
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    TextView detailTextView = (TextView) view.findViewById(R.id.simple_activity_b_text);
    detailTextView.setText("detail");

    ImageView imageView = (ImageView) view.findViewById(R.id.simple_activity_b_image);
    imageView.setVisibility(View.VISIBLE);
    view.findViewById(R.id.activity_simple_two).setVisibility(View.VISIBLE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        imageView.setTransitionName(getString(R.string.simple_activity_transition));
    }

    Glide.with(this)
            .load(GlideFragmentA.ARMADILLO_PIC_URL)
            .centerCrop()
            .dontAnimate()
            .listener(new RequestListener<String, GlideDrawable>() {
                @Override
                public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
                    startPostponedEnterTransition();
                    return false;
                }

                @Override
                public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
                    startPostponedEnterTransition();
                    return false;
                }
            })
            .into(imageView);
}

最佳答案

高度简化后,Fragments 基本上是具有自己的生命周期和逻辑的奇特 ViewGroup,它们是 Activity View 树的一部分,就像任何其他 ViewGroup 一样。因此,当查看两个 Activity 之间的转换时,您实际上并不是在执行 fragment 级共享元素转换,而是在 Activity 级别执行转换。

有必要确保在目标 Activity 中,如果目标 View (共享元素)是添加到顶部的 fragment 的一部分,那么当转换框架开始捕获转换的结束值时,这个目标 View 就准备好了,以便可以在 View 层次结构中找到它。

FragmentManager 的 commit() 不会立即执行事务和布局更改,而是将它们安排在不久之后的某个时间点,这在您的情况下导致上述情况并非如此。将目标 Activity 中的输入转换推迟到 fragment 的 View 准备就绪(例如,首先调用其根布局的 onPreDraw() )应该修复该部分。这意味着,您必须在 Activity B 中调用 postponeEnterTransition(),而不是 Fragment B,和 startPostponedEnterTransition()(Glide 加载监听器的一部分)在 Activity 引用上 ,而不是 fragment B 本身。

此外,您需要在 Activity B 上设置共享元素转换(进入和返回),因为它是实际运行它们的组件。设置好这些先决条件后,您所需的转换就会生效。

关于android - 不同 Activity fragment 之间的共享元素转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48879331/

相关文章:

java - 输入长文本后,具有自定义 View 的 AlertDialog 消失在软键盘后面

android - 在 Jetpack compose 中绘制动画圆

android - 替换 ViewPager 中的 fragment 位置

android - 单击 View 寻呼机第二个 fragment 中的按钮时返回 View 寻呼机的第一个 fragment

android - 使用一组图像和声音创建动画

android - 将点击监听器添加到通用 RecyclerView 适配器

java - 当我尝试将 gridview 放入 fragment 中时,适配器中的 getView() 出现错误

python - Matplotlib 动画 - 保存文件的速度出现问题

Swift:运行代码会阻止其他代码直到完成

java - ViewModelProviders#of(Fragment) 类型错误