MaterialContainerTransform 转换在返回时不起作用

2024-03-14

我的 MaterialContainerTransform 转换是从源 -> 目标进行的,但反之则不然。我的情况非常标准——我试图提供从 RecyclerView 项目(源片段)到“详细信息”片段(目标片段)的简单转换。 RecyclerView 中的项目是 MaterialCardView,每个项目都有一个在目标 Fragment 中共享的 ImageView。

遵循这些文档https://material.io/develop/android/theming/motion https://material.io/develop/android/theming/motion这似乎相当简单,尽管文档是用 Kotlin 编写的,而我使用的是 Java,所以也许我遗漏了一些东西?我在每个 RecyclerView 项中动态设置 ImageView transitionName,并将其传递给目标 Fragment,然后目标 Fragment 将 TransitionName 复制到其自己的 ImageView。通过日志记录,我可以确认每个片段中的共享transitionName 匹配。

从源 -> 目标的转换效果很好,但是当我点击后退按钮时,没有转换回来。这很奇怪,因为即使文档也声明:

片段能够定义进入和返回共享元素转换。当只设置了一个进入共享元素transition时,当Fragment弹出(返回)时会重新使用它。

有什么想法为什么返回转换(目标 -> 源)不起作用?这是相关代码:

RecyclerView Item (Source) **ImageView 是共享元素

<com.google.android.material.card.MaterialCardView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/result_layout_card"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardCornerRadius="5dp"
    app:cardElevation="4dp"
    android:layout_margin="8dp"
    android:clickable="true"
    android:focusable="true"
    android:checkable="true"
    app:checkedIconTint="@color/checkedYellow"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_gravity="center">


        <ImageView
            android:id="@+id/result_image_thumbnail"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:scaleType="fitCenter"
            android:contentDescription="@string/thumbnail_content_description"
            android:src="@drawable/corolla_preview"
            />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:orientation="vertical">

            <TextView
                android:id="@+id/result_text_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="2018 Toyota Corolla"
                style="@style/TextAppearance.MaterialComponents.Subtitle1"
                />

            <TextView
                android:id="@+id/result_text_stock"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="#626546"
                style="@style/TextAppearance.MaterialComponents.Body2"
                />

            <TextView
                android:id="@+id/result_text_price"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="$28,998"
                style="@style/TextAppearance.MaterialComponents.Overline"
                />

        </LinearLayout>

    </LinearLayout>

</com.google.android.material.card.MaterialCardView>

详细信息片段(目标)** ImageView 再次是共享元素

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="8dp">

        <ImageView
            android:id="@+id/detail_image_thumbnail"
            android:layout_width="match_parent"
            android:layout_height="225dp"
            android:contentDescription="@string/thumbnail_content_description"/>
        
    </LinearLayout>
</ScrollView>

RecyclerView Holder(源代码) ** viewResults 方法是我启动片段事务的地方

public class ResultsHolder extends RecyclerView.ViewHolder {

    private static final String TAG = ResultsHolder.class.getSimpleName();
    private final FragmentManager fragmentManager;
    private ResultModel resultModel;

    private final MaterialCardView cardView;
    private final ImageView thumbnail;
    private String searchId;

    private ResultsFragment resultsFragment;
    private int position;
    private View thumbnailView;

    public ResultsHolder(View itemView, FragmentManager fragmentManager,
                         String searchId, ResultsFragment resultsFragment) {
        super(itemView);

        cardView = itemView.findViewById(R.id.result_layout_card);
        thumbnail = itemView.findViewById(R.id.result_image_thumbnail);

        this.fragmentManager = fragmentManager;
        this.searchId = searchId;

        this.resultsFragment = resultsFragment;

    }

    public void bindResult(ResultModel result, int position) {
        resultModel = result;

        Picasso.get().load(result.getImageUrl()).into(thumbnail);

        cardView.setChecked(result.isChecked());
        cardView.setOnClickListener(cardViewClickListener);

        this.position = position;
        thumbnail.setTransitionName(result.getVin()); // transition name is unique for each recyclerview 
                                                      // item

    }

    private final View.OnClickListener cardViewClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            viewDetails(resultModel, searchId);
        }
    };


    // This is the method where I start the destination fragment
    public void viewDetails(ResultModel result, String searchId) {
        Bundle args = new Bundle();
        args.putParcelable("RESULT", Parcels.wrap(result));
        args.putString("SEARCH_ID", searchId);

        DetailFragment fragment = new DetailFragment();

        // Destination fragment enter transition!
        fragment.setSharedElementEnterTransition(new MaterialContainerTransform());
        fragment.setArguments(args);

        fragmentManager
                .beginTransaction()
                .setReorderingAllowed(true)
                .addSharedElement(thumbnail, thumbnail.getTransitionName()) // Shared element!
                .replace(R.id.main_fragment_container,
                        fragment,
                        DetailFragment.class.getSimpleName())
                .addToBackStack(null)
                .commit();
    }

详细信息片段(目标)

public class DetailFragment extends Fragment {

    @SuppressWarnings("unused")
    private static final String TAG = "DetailFragment";
    private ResultModel result;
    private String searchId;

    ImageView image;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {

            // Get result and search ID
            result = Parcels.unwrap(getArguments().getParcelable("RESULT"));
            searchId = getArguments().getString("SEARCH_ID");

        }

    }


    private void instantiateUI(View v) {
        
        TextView vin = v.findViewById(R.id.tv_details_vin);
        vin.setText(result.getVin());
        
        image = v.findViewById(R.id.detail_image_thumbnail);
        Picasso.get().load(result.getImageUrl()).fit().into(image);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View v = inflater.inflate(R.layout.fragment_detail, container, false);

        // Set transitionName exactly same as the recyclerview item which was clicked
        v.findViewById(R.id.detail_image_thumbnail).setTransitionName(result.getVin());
        return v;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        if (((AppCompatActivity) getActivity()) != null
                && ((AppCompatActivity) getActivity()).getSupportActionBar() != null) {

            Objects.requireNonNull(((AppCompatActivity) getActivity())
                    .getSupportActionBar()).setTitle("Details");
        }

        instantiateUI(view);
    }
}

我已经在我的旧项目中实现了这一点。 检查以下内容中您缺少什么:

1.在您的 SourceFragment 中添加以下代码行。这里的关键点是在 onViewCreated() 方法中,您必须使用推迟EnterTransition() and startPostponedEnterTransition()当用户返回到源片段时,需要正确地设置动画。另外,在 onCreate() 方法中设置 Exit Transition 和 ReenterTransition 以使项目列表在退出时横向扩展并在重新进入时返回:

public class SourceFragment extends Fragment {

    private RecyclerView recyclerView;
    private LinearLayoutManager mLayoutManager;

    public static int index = -1;
    public static int top = -1;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //set the below transitions so as the source list scale out when exiting and back in when reentering
        setExitTransition(new MaterialElevationScale(false));
        setReenterTransition(new MaterialElevationScale(true));
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_source, container, false);
        recyclerView = view.findViewById(R.id.recycler_view);
        mLayoutManager = new LinearLayoutManager(getContext());
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setAdapter(new SourceAdapter(this, getFragmentManager()));
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        //the below code is required to animate correctly when the user returns to the source fragment
        //gives a chance for the layout to be fully laid out before animating it
        postponeEnterTransition();
        ((ViewGroup) view.getParent()).getViewTreeObserver()
                .addOnPreDrawListener(() -> {
                    startPostponedEnterTransition();
                    return true;
                });
    }

    @Override
    public void onPause() {
        super.onPause();
        //Save the current state of recycle view position
        index = mLayoutManager.findFirstVisibleItemPosition();
        View startView = recyclerView.getChildAt(0);
        top = (startView == null) ? 0 : (startView.getTop() - recyclerView.getPaddingTop());
    }

    @Override
    public void onResume()
    {
        super.onResume();
        //Scrolls the recycler view to the clicked item position when navigating back
        if(index != -1) {
            mLayoutManager.scrollToPositionWithOffset(index, top);
        }
    }
}

2.设置转换名称以映射共享元素,例如:图像项(开始视图)到其目标片段图像(结束视图)。在 SourceAdapter 的 onBindViewHolder() 中为每个图像项设置唯一标识符。根据你的代码将是:

thumbnail.setTransitionName(result.getVin());

3.将此唯一标识符传递到您的 DestinationFragment (DetailFragment) 并在 onCreateView() 中完成映射。根据你的代码将是:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View v = inflater.inflate(R.layout.fragment_detail, container, false);

        // Set transitionName exactly same as the recyclerview item which was clicked
        v.findViewById(R.id.detail_image_thumbnail).setTransitionName(result.getVin());
        return v;
}

4.在 DestinationFragment (DetailFragment) 的 onCreate() 方法中添加 Enter Transition,如下所示:

@Override
public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //prepare the EnterTransition
        MaterialContainerTransform transform = new MaterialContainerTransform();
        transform.setScrimColor(Color.TRANSPARENT);
        setSharedElementEnterTransition(transform);
        //get your arguments according to your code
        if (getArguments() != null) {
            result = Parcels.unwrap(getArguments().getParcelable("RESULT"));
            searchId = getArguments().getString("SEARCH_ID");
        }
    }

5.最后根据您的代码从SourceFragment切换到DestinationFragment:

        Bundle args = new Bundle();
        args.putParcelable("RESULT", Parcels.wrap(result));
        args.putString("SEARCH_ID", searchId);

        DetailFragment fragment = new DetailFragment();
        fragment.setArguments(args);

        fragmentManager
                .beginTransaction()
                .setReorderingAllowed(true)
                .addSharedElement(thumbnail, thumbnail.getTransitionName()) // Shared element!
                .replace(R.id.main_fragment_container,
                        fragment,
                        DetailFragment.class.getSimpleName())
                .addToBackStack(null)
                .commit();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MaterialContainerTransform 转换在返回时不起作用 的相关文章

  • 发送 OneSignal 推送通知时 Android 应用程序崩溃

    Android 应用程序在发送信号推送通知后立即崩溃 我收到这个错误 尝试查看其他 stackoverflow 答案 但没有帮助 请检查下面的代码 build gradle 和错误 不明白为什么它不起作用 P 请检查下面的代码 build
  • android 从图库中选择图像

    我想从图库创建一个图片选择器 我使用代码 intent new Intent Intent ACTION PICK android provider MediaStore Images Media EXTERNAL CONTENT URI
  • 在 Android 版 Glide 中离线时加载已获取的图像

    我正在使用 Glide 版本 4 8 0 为了加载图像我这样做 GlideApp with HomePageFragment this load remoteURL diskCacheStrategy DiskCacheStrategy A
  • 如何在 Fragment 中使用 onNewIntent(Intent Intent) 方法?

    我正在尝试从我的设备使用 NFC 硬件 但是 问题是当我注册 Activity 来接收 Intent 时 PendingIntent pendingIntent PendingIntent getActivity this 0 new In
  • 使用 IntelliJ / Android Studio 调试基于 gradle 的单元测试

    我正在使用robolectric gradle 插件 https github com robolectric robolectric gradle plugin为 Android 编写单元测试 到目前为止 除了能够使用 Android S
  • 字节码和位码有什么区别[重复]

    这个问题在这里已经有答案了 可能的重复 LLVM 和 java 字节码有什么区别 https stackoverflow com questions 454720 what are the differences between llvm
  • 哪种 Java DOM 包装器是最好或最受欢迎的? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 是什么原因导致“对象不是声明类的实例”? [复制]

    这个问题在这里已经有答案了 可能的重复 使用反射调用方法时 为什么会出现 对象不是声明类的实例 https stackoverflow com questions 7202988 why do i get object is not an
  • Mule/码头设置

    我有一个正在运行的 Mule 应用程序 我想在其上设置 Jetty 来响应 http 请求 以下配置
  • 将字符串中的字符向左移动

    我是 Stack Overflow 的新手 有一道编程课的实验室问题一直困扰着我 该问题要求我们将字符串 s 的元素向左移动 k 次 例如 如果输入是 Hello World 和3 它将输出 lo WorldHel 对于非常大的 k 值 它
  • Scala repl 抛出错误

    当我打字时scala在终端上启动 repl 它会抛出此错误 scala gt init error error while loading AnnotatedElement class file usr lib jvm java 8 ora
  • 防止 Firebase 中的待处理写入事务不起作用

    我的目标是在单击按钮时将名称插入 Cloud Firestore 中 但如果用户未连接到互联网 我不希望保存处于挂起状态 我不喜欢 Firebase 保存待处理写入的行为 即使互联网连接已恢复 我研究发现Firebase 开发人员建议使用事
  • Android同步onSensorChanged?

    这是我的问题的后续 Android线程可运行性能 https stackoverflow com questions 36395440 android thread runnable performance 我在理解应用程序的同步方法时遇到
  • Java SE + Spring Data + Hibernate

    我正在尝试使用 Spring Data Hibernate 启动 Java SE 应用程序 并且到目前为止已经完成了以下操作 配置文件 Configuration PropertySource classpath hibernate pro
  • ActionBar 中的进度条

    我正在使用一个应用程序 它将在应用程序内部加载一个网站 现在我想在操作栏中添加一个进度栏 而无需向上滑动来抑制功能 像那样 我在用Fragment在我的应用程序中 WebviewFragment public class WebviewFr
  • 如何在android中获取当前一周的所有天数?

    我想在字符串数组中获取本周的所有日期 我怎样才能做到这一点 提前致谢 I think你想要这样的东西 假设你总是想要从星期一开始的几周 以及 MM dd yyyy 的日期格式 DateFormat format new SimpleDate
  • 如何在Java中跨类共享变量,我尝试了静态不起作用

    类 Testclass1 有一个变量 有一些执行会改变变量的值 现在在同一个包中有类 Testclass2 我将如何访问 Testclass2 中变量的更新值 由 Testclass1 更新 试过这个没用 注意 Testclass1和Tes
  • 从 Android 中的过渡动画中排除 BottomNavigation

    我一直在四处寻找 但找不到有助于解决这个特定问题的答案 我的应用程序有一个自定义滑入 滑出效果 如下所示 Intent intent new Intent getApplicationContext MyActivity class sta
  • 将隐藏(生物识别)数据附加到 pdf 上的数字签名

    我想知道是否可以使用 iText 我用于签名 或 Java 中的其他工具在 pdf 上添加生物识别数据 我会更好地解释一下 在手写板上签名时 我会收集签名信息 例如笔压 签名速度等 我想将这些信息 java中的变量 与pdf上的签名一起存储
  • 如何使用 Spring AOP 建议静态方法?

    在执行类的静态方法之前和之后需要完成一些日志记录 我尝试使用 Spring AOP 来实现这一点 但它不起作用 而对于正常方法来说它起作用 请帮助我理解如何实现这一点 如果可以使用注释来完成 那就太好了 也许您应该在使用 Spring AO

随机推荐

  • Angular 2 动画/过渡仅适用于 chrome?

    正如标题所示 我一直在使用 Angular2 构建一个 Web 应用程序 并决定测试跨浏览器 结果发现漂亮的动画只能在 Chrome 中运行 这是我的一个组件的样子 如果这可能会有所不同 Component selector contact
  • 如何在 Plone 中定义默认视图

    我已经有一个用于 Plone 站点 主页 的默认视图的页面模板 如何定义文件夹的默认视图 此默认视图应使用页面模板 尝试这个 导航到所需的文件夹 在网址末尾添加 manage propertiesForm in the resulting
  • Python scikit-learn KMeans 在计算轮廓分数时被杀死 (9)

    我目前正在研究一个图像数据集 250 000 张图像 因此与特征向量一样多 每个图像都由 132 个特征组成 并尝试使用 sklearn 提供的 KMeans 函数 我在 Mac OS X 10 10 Python 2 7 和 sklear
  • 在 MATLAB 中绘图时循环颜色

    当我使用以下方法在同一个图上绘制多条曲线时hold on 每条曲线默认为相同的颜色 蓝色 我希望它们有不同的颜色 我见过的一种解决方案是制作颜色矢量 例如c k g r 并循环它 但我不喜欢这个解决方案 如果我的绘图数量大于颜色向量的长度
  • PHP 设置 MySQL 套接字位置

    我以前遇到过这样的问题 我的 C 应用程序在 tmp mysql sock 中寻找套接字 但我的套接字位置已设置为 var run mysqld sock 我现在更改了 my cnf 中的设置 始终将套接字放入 tmp 现在 当我尝试使用
  • 使用 GDI+ 创建的图像未显示在 VB6 LeadTools 中

    我们有使用 VB6 构建的遗留应用程序 这些应用程序正在使用引导工具 一切都很顺利 我们有另一个 NET 进程来优化图像 并做一些水印 并将其保存为 tiff 格式 这是 NET 代码的一瞥 using var bitmap new Bit
  • 使用 Android TabLayout 时单击选项卡不会切换当前选项卡

    我应该自己设置 onClicks 吗 如果是的话 我应该在哪里设置
  • OpenGL在另一个线程中绘图

    我为 Windows 创建了一个简单的 OpenGL 应用程序 它创建一个窗口 然后使用 OpenGL 命令在其上绘制一个三角形 这按预期工作 后来我想将我的绘图代码封装到一个DLL中 以便可以在C WinForms应用程序中使用它来绘制W
  • 包含聚合的更新语句在 SQL Server 中不起作用

    我希望有人可以在这里帮助我的语法 我有两张桌子ansicache encounters and ansicache x refclaim Table The encounters表有一个encounter匹配的列patacctnumber栏
  • 寻找序列中的零岛

    想象一下你有一个很长的序列 找到序列全为零的间隔 或更准确地说 序列下降到接近零值的间隔 的最有效方法是什么abs X
  • 将 Java Applet 迁移到什么/哪里?

    我目前正在审查一个中等规模的代码库 大约 30K LOC 它使用一个巨大的 Applet 并与其他系统交互 它是一个创建自定义标签的工具 因此我们需要拖放和其他相关的 UI 组件 鉴于代码库是 Java 语言 您将迁移到哪种技术 小程序 否
  • 使用 Selenium 登录页面可以工作 - 使用 BS4 进行解析可以工作 - 但不能同时使用两者

    从 Wordpress 论坛获取一些数据需要登录和解析 两个部分 两者作为独立部分都可以很好地工作 我可以使用 selenium 登录 完美 并且我可以使用 BS4 解析 抓取 数据 但是当我将这两个部分结合起来时 我遇到了会话问题 我无法
  • 了解 Scala 类型系统中的 Aux 模式

    这个问题之前可能被问过并回答过 但我想通过一个例子来理解这一点 但我无法推理出 Aux 模式可能有帮助的地方 所以这是特点 trait Foo A type B def value B 为什么我的类型与值函数的返回类型绑定 这样做我能取得什
  • IEnumerable.ToList() 的影响

    我只是想知道打电话时发生了什么 ToList on an IEnumerable在 C 中 这些项目实际上是否被复制到堆上全新的重复项目 或者新列表只是引用堆上的原始项目 我很想知道 因为有人告诉我调用 ToList 的成本很高 而如果只是
  • c openmp并行用于并行区域内

    我的问题是这样的one https stackoverflow com questions 11493265 for loop inside parallel region 但我想做一些不同的事情 例如 在我的并行区域内 我想在 4 个线程
  • Java:非静态嵌套类和instance.super()

    我很难理解 Java 中的非静态嵌套类 考虑以下示例 它先打印 Inner 然后打印 Child class Outer class Inner Inner System out println Inner public class Chi
  • 在 SQL Server 2005 中使用 TABLOCK 和 HOLDLOCK 提示是否会完全阻止插入直到事务结束?

    我需要检索 SQL Server 2005 生成的标识字段 通常我只使用 SCOPE IDENTITY 或向插入添加 OUTPUT CLAUSE 但是这些方法在这种情况下都没有帮助 因为表附加了 INSTEAD OF 触发器 接下来我考虑了
  • C++20 中概念的语法

    在这个问题中我们如何转换需求link https stackoverflow com questions 67133581 check if type has certain value types and the keyword valu
  • 如何管理扩展方法的名称空间?

    您是否为所有扩展方法使用全局的 包罗万象的命名空间 或者是否将扩展方法放在与其扩展的类相同的命名空间中 或者您是否使用其他方法 例如应用程序或特定于库的命名空间 我问是因为我需要延长System Security Principal IId
  • MaterialContainerTransform 转换在返回时不起作用

    我的 MaterialContainerTransform 转换是从源 gt 目标进行的 但反之则不然 我的情况非常标准 我试图提供从 RecyclerView 项目 源片段 到 详细信息 片段 目标片段 的简单转换 RecyclerVie