共享元素向后过渡不适用于片段中的 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()
}
我正在尝试在具有卡片视图片段的回收器视图与仅包含 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()
}