共享元素向后过渡不适用于片段中的 recyclerview 和 cardviews

Shared element back transition not working with recyclerview and cardviews in fragments

我正在尝试在具有卡片视图片段的回收器视图与仅包含 1 张卡片的片段之间创建过渡。问题是后向过渡不起作用,而进入过渡是。如果我删除 setReorderingAllowed(true); 则向后过渡正常,但进入过渡停止工作。 这就是我的。

带有 recyclerview 和 cardviews 的片段

public class OrdersFragment extends Fragment implements OrderAdapter.OnOrderListener {

    private OrderAdapter mAdapter;

    private TextView bonnenTextView;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        postponeEnterTransition();
    }

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_bonnen, container, false);
        bonnenTextView = view.findViewById(R.id.bonnen_text_view);
        RecyclerView orderRecyclerView = view.findViewById(R.id.order_recycler_view);
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
        orderRecyclerView.setLayoutManager(layoutManager);
        if (mAdapter == null) {
            mAdapter = new OrderAdapter(this);
            fetchOrders();
        }
        orderRecyclerView.setAdapter(mAdapter);
        return view;
    }

    private void fetchOrders() {
        new OrderFetcher().fetch(new Callback() {
            @Override
            public void onComplete(Result result) {
                if (result instanceof Result.Success) {
                    mAdapter.setOrders((Order[]) ((Result.Success<?>)result).data);
                    mAdapter.notifyDataSetChanged();
                } else {
                    Toast.makeText(getContext(), "Could not load orders", Toast.LENGTH_SHORT).show();
                }
                startPostponedEnterTransition();
            }
        });
    }

    @Override
    public void onOrderClick(int position, View view, Order order) {
        Log.i("OrderClick", "Transition name " + view.getTransitionName());

        View carrierTextView = view.findViewById(R.id.carrier_text_view);
        View numberTextView = view.findViewById(R.id.id_text_view);
        View pickerTextView = view.findViewById(R.id.picker_text_view);
        View locationTextView = view.findViewById(R.id.location_text_view);

        FragmentTransaction transaction = getParentFragmentManager().beginTransaction();
        transaction.addSharedElement(view, view.getTransitionName());
        transaction.addSharedElement(carrierTextView, carrierTextView.getTransitionName());
        transaction.addSharedElement(numberTextView, numberTextView.getTransitionName());
        transaction.addSharedElement(pickerTextView, pickerTextView.getTransitionName());
        transaction.addSharedElement(locationTextView, locationTextView.getTransitionName());
        transaction.addSharedElement(bonnenTextView, bonnenTextView.getTransitionName());
        transaction.replace(R.id.nav_host_fragment, BonFragment.newInstance(view.getTransitionName(), order));
        transaction.addToBackStack(null);
        transaction.setReorderingAllowed(true);
        transaction.commit();
    }
}

xml 上面的片段

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".ui.orders.OrdersFragment">

    <TextView
        android:id="@+id/bonnen_text_view"
        android:transitionName="bonnen_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginTop="10dp"
        android:text="@string/orders"
        android:textColor="@color/secondary"
        android:textSize="40sp"
        android:textStyle="bold"
        android:typeface="normal" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/order_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="20dp"/>
</LinearLayout>

具有单个卡片视图的片段

public static BonFragment newInstance(String cardTransitionName, Order order) {
        BonFragment bonFragment = new BonFragment();

        Bundle args = new Bundle();
        args.putParcelable("orderParcel", order);
        args.putString("cardTransitionName", cardTransitionName);
        bonFragment.setArguments(args);

        return bonFragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        final View root = inflater.inflate(R.layout.fragment_bon, container, false);

        CardView orderCard = root.findViewById(R.id.order_card);
        TextView carrierTextView = root.findViewById(R.id.carrier_text_view);
        TextView numberTextView = root.findViewById(R.id.id_text_view);
        TextView pickerTextView = root.findViewById(R.id.picker_text_view);
        TextView locationTextView = root.findViewById(R.id.location_text_view);

        if (getArguments() != null && getArguments().getParcelable("orderParcel") != null) {
            Order order = getArguments().getParcelable("orderParcel");
            orderCard.setTransitionName(getArguments().getString("cardTransitionName"));
            if (order != null) {
                carrierTextView.setTransitionName("carrier" + order.getIndex());
                carrierTextView.setText(order.getCarrier());
                numberTextView.setTransitionName("number" + order.getIndex());
                numberTextView.setText(String.valueOf(order.getNumber()));
                pickerTextView.setTransitionName("picker" + order.getIndex());
                pickerTextView.setText(order.getPicker());
                locationTextView.setTransitionName("location" + order.getIndex());
                locationTextView.setText(order.getPosition());

                carrierTextView.setText("Lorem Ipsum");
                numberTextView.setText("Dolor sit amet");
                pickerTextView.setText("consectetur adipiscing elit");
                locationTextView.setText("Mauris semper");

            }
        }

        orderCard.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getParentFragmentManager().popBackStack();
            }
        });

        Transition transition = TransitionInflater.from(getContext()).inflateTransition(R.transition.card_transition);
        setSharedElementEnterTransition(transition);
        setSharedElementReturnTransition(transition);
        return root;
    }

xml 上面的片段

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".ui.orders.OrdersFragment">

    <TextView
        android:transitionName="bonnen_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginTop="10dp"
        android:text="@string/orders"
        android:textColor="@color/secondary"
        android:textSize="40sp"
        android:textStyle="bold"
        android:typeface="normal" />

    <androidx.cardview.widget.CardView
        android:id="@+id/order_card"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:cardBackgroundColor="@color/primaryLight"
        android:foreground="?android:attr/selectableItemBackground"
        android:clickable="true"
        android:layout_margin="25dp">

        <LinearLayout
            android:transitionName="order_card_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_margin="10dp">
            <TextView
                android:id="@+id/carrier_text_view"
                android:transitionName="carrier_text_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/secondary"
                android:textSize="20sp"
                android:textStyle="bold"/>
            <TextView
                android:id="@+id/id_text_view"
                android:transitionName="id_text_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/secondary" />
            <TextView
                android:id="@+id/picker_text_view"
                android:transitionName="picker_text_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/secondary" />
            <TextView
                android:id="@+id/location_text_view"
                android:transitionName="location_text_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/secondary" />
        </LinearLayout>
    </androidx.cardview.widget.CardView>
</LinearLayout>

这是 setReorderingAllowed(true);

的样子

这是没有 setReorderingAllowed(true);

的样子

编辑 在应用了 ianhanniballake 给出的答案后,我开始工作了。 这是我所做的以供将来参考。 我所做的唯一更改是 OrdersFragment class。 我删除了 onCreate() 方法的覆盖。 我从 fetchOrders() 方法中删除了 startPostponedEnterTransition(); 并添加了

@Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        postponeEnterTransition();
        final ViewGroup parentView = (ViewGroup) view.getParent();
        parentView.getViewTreeObserver()
                .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        parentView.getViewTreeObserver().removeOnPreDrawListener(this);
                        startPostponedEnterTransition();
                        return true;
                    }
                });
        super.onViewCreated(view, savedInstanceState);
    }

就是这样

根据 Use shared element transitions with a RecyclerView guide,您调用 startPostponedEnterTransition() 为时过早 - 在设置数据后(并调用 notifyDataSetChanged() 通知适配器),您需要等到RecyclerView实际测量布局。这要求您添加一个 OnPreDrawListener 并且只在触发后调用 startPostponedEnterTransition()

在 Kotlin 中,您需要在 recyclerView 的 doOnPreDraw 回调中开始转换,如下所示:

recyclerView.doOnPreDraw {
    startPostponedEnterTransition()
}