My MaterialContainerTransform transition is working from source -> destination, but not the other way around. My situation is pretty standard -- I am attempting to provide a simple transition from a RecyclerView item (source Fragment) to a "details" Fragment (destination Fragment). The items within the RecyclerView are MaterialCardViews, each with an ImageView which is shared in the destination Fragment.
Following these docs https://material.io/develop/android/theming/motion it seems to be fairly straightforward, though the docs are written in Kotlin and I am using Java so maybe I am missing something? I dynamically set the ImageView transitionName in each RecyclerView item, and pass that to the destination Fragment which then copies the transitionName to its own ImageView. Through logging, I can confirm the shared transitionName matches in each fragment.
The transition from source -> destination works great, but when I hit the back button there is no transition back. This is strange because even the docs state:
Fragments are able to define enter and return shared element transitions. When only an enter shared element transition is set, it will be re-used when the Fragment is popped (returns).
Any ideas why the return transition (destination -> source) is not functioning? Here is the relevant code:
RecyclerView Item (Source) **the ImageView is the shared element
<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>
Detail Fragment (destination) ** again the ImageView is the shared element
<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 (source) ** the viewResults method is where I start the fragment transaction
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();
}
Details Fragment (destination)
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);
}
}