In the Main Activity, I have BottomNavigationView
where there are 3 different parent fragments. The parent fragment has recyclerview
and on item click of recyclerview, I am launching child fragment for more details about the item. I am trying to implement Shared Element Transition
between my two fragments (parent & child) but it's not happening.
There is no issue with launching child fragment also I have checked the transition name and it's the same in child fragment which I assign to an item in the adapter. I am using Random
class to assign transition name to item as in single parent fragment I have many recyclerviews. Here is my code:
Adapter
final String transition;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
transition = "transition" + new Random().nextInt(9999999);
viewHolder.image.setTransitionName(transition);
}
viewHolder.container.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mCallback.itemClicked(i, book, viewHolder.image, transition);
}
});
Parent Fragment
@Override
public void itemClicked(int pos, Book book, View view, String transition) {
MainActivity activity = (MainActivity) getActivity();
ChildFragment myFragment = new ChildFragment();
Bundle bundle = new Bundle();
bundle.putString(IntentExtraKeys.TRANSITION_NAME, transition);
myFragment.setArguments(bundle);
activity.showFragmentWithTransition(this, myFragment, ChildFragment.class.getName(), view, transition);
}
Activity
public void showFragmentWithTransition(Fragment current, Fragment newFragment, String tag, View sharedView, String sharedElementName) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
current.setSharedElementReturnTransition(TransitionInflater.from(this).inflateTransition(R.transition.default_transition));
current.setExitTransition(TransitionInflater.from(this).inflateTransition(android.R.transition.no_transition));
newFragment.setSharedElementEnterTransition(TransitionInflater.from(this).inflateTransition(R.transition.default_transition));
newFragment.setEnterTransition(TransitionInflater.from(this).inflateTransition(android.R.transition.no_transition));
}
FragmentManager manager = current.getChildFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.child_fragment, newFragment, tag);
transaction.addToBackStack(tag);
transaction.addSharedElement(sharedView, sharedElementName);
transaction.commit();
}
default_transition
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeTransform />
<changeBounds />
</transitionSet>
Child Fragment
Bundle b = getArguments();
if (b != null) {
String transitionName = b.getString(IntentExtraKeys.TRANSITION_NAME);
Logger.info("opening bundle book fragment:" + transitionName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
image.setTransitionName(transitionName);
}
}
Here is the sample project of issue: https://gitlab.com/iskfaisal/transition-issue
At a high level, here's how to make a fragment transition with shared elements: Assign a unique transition name to each shared element view. Add shared element views and transition names to the FragmentTransaction . Set a shared element transition animation.
Fragments represent a behavior or portion of UI in an Activity. Fragments are mostly used in tablets to divide screen and utilize screen space in efficient way. With Android 4.2 nested fragments are introduced, with which now you can embed fragments inside fragments.
The child FragmentManager is the one that handles Fragments contained within only the Fragment that it was added to. The other FragmentManager is contained within the entire Activity .
I am trying to implement Shared Element Transition between my two fragments (parent & child) but it's not happening.
Nothing is happening or at least it looks like nothing is happening because you're using only <changeTransform/>
and <changeBounds />
in your default_transition.xml. If bounds of both Fragments
coincide, there's nothing to "transit".
However, if you add additional animation elements to the file then the transition is actually visible (even if bounds coincide).
I have created a sample project similar to yours - BottomNavigationView
with a NavHostFragment
containing 2
parent Fragments
- DashboardFragment
and HomeFragment
.
Initially, DashboardFragment
loads DashboardListFragment
that consists of a simple RecyclerView
. If any RecyclerView
item is clicked then DashboardFragment
loads DashboardDetailFragment
(bounds in DashboardDetailFragment
and DashboardListFragment
coincide).
Then, I pretty much reused your showFragmentWithTransition(...)
and tried to click each element in the list to check if any transition would be visible - it wasn't.
Hence, I modified the file by adding a simple <slide/>
element as below:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet>
<slide/>
<changeTransform />
<changeBounds />
</transitionSet>
And the sliding transition was right there.
I also tried other elements like <fade/>
or <explode/>
or others - all of them worked just fine too.
Even if a transition is not visible, it doesn't mean it's not happening. You should try other animation elements to see it work.
Since you provided a link to your github code, I peeked into it and brought it to the working condition. I'll leave it up to you to improve it further.
So, basically, your HomeFragment
layout file contained both RecyclerView
and a placeholder for your child Fragment
at the same time. Instead, I split your HomeFragment
into 2
entities - one for your parent Fragment
HomeFragment
containing only the placeholder for any child Fragment
and HomeFragmentList
containing your previous parent logic.
Then, when a picture is selected, HomeFragment
replaces your HomeFragmentList
with a ChildFragment
. And...it works!
This way, you don't need to use your Activity
for creating a child Fragment
and it's more independent.
Below, I provide the relevant code that had to be modified.
public class HomeFragment extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_home, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
HomeFragmentList homeFragmentList = new HomeFragmentList();
FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.child_fragment, homeFragmentList, homeFragmentList.getClass().getName());
fragmentTransaction.commit();
}
}
public class HomeFragmentList extends Fragment implements AdapterListener {
RecyclerView recyclerView;
private ArrayList<String> images = new ArrayList<>();
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_home_list, container, false);
recyclerView = root.findViewById(R.id.recycler_view);
getImages();
LinearLayoutManager manager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
recyclerView = root.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(manager);
AdapterClass adapter = new AdapterClass(this, images);
recyclerView.setAdapter(adapter);
return root;
}
void getImages() {
images.add("https://rukminim1.flixcart.com/image/832/832/book/0/1/9/rich-dad-poor-dad-original-imadat2a4f5vwgzn.jpeg?q=70");
images.add("https://www.seeken.in/wp-content/uploads/2017/06/The-4-Hour-Work-Week.jpeg");
images.add("https://www.seeken.in/wp-content/uploads/2017/06/Managing-Oneself.jpeg");
images.add("https://www.seeken.in/wp-content/uploads/2017/07/How-to-Win-Friends-and-Influence-People.jpeg");
images.add("https://www.seeken.in/wp-content/uploads/2017/07/THINK-LIKE-DA-VINCI-7-Easy-Steps-to-Boosting-your-Everyday-Genius.jpeg");
images.add("https://www.seeken.in/wp-content/uploads/2017/07/How-To-Stop-Worrying-And-Start-Living.jpg");
images.add("https://www.seeken.in/wp-content/uploads/2017/08/THE-INTELLIGENT-INVESTOR.jpeg");
images.add("https://www.seeken.in/wp-content/uploads/2017/08/Awaken-the-Giant-within-How-to-Take-Immediate-Control-of-Your-Mental-Emotional-Physical-and-Financial-Life.jpg");
images.add("https://www.seeken.in/wp-content/uploads/2017/08/E-MYTH-REVISITED.jpeg");
images.add("https://images-na.ssl-images-amazon.com/images/I/41axGE4CehL._SX353_BO1,204,203,200_.jpg");
images.add("https://rukminim1.flixcart.com/image/832/832/book/0/1/9/rich-dad-poor-dad-original-imadat2a4f5vwgzn.jpeg?q=70");
}
@Override
public void itemClicked(int pos, ModelClass object, View view, String transition) {
MainActivity activity = (MainActivity) getActivity();
ChildFragment myFragment = new ChildFragment();
Bundle bundle = new Bundle();
bundle.putString("IMAGE_URL", object.getItem(pos));
bundle.putInt("POSITION", pos);
bundle.putString("TRANSITION_NAME", transition);
myFragment.setArguments(bundle);
Log.i("HOME FRAGMENT-DEBUG", transition + "/" + view.getTransitionName());
showFragmentWithTransition(getParentFragment(), myFragment, ChildFragment.class.getName(), view, transition);
}
public void showFragmentWithTransition(Fragment current, Fragment _new, String tag, View sharedView, String sharedElementName) {
FragmentManager manager = current.getChildFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
if (sharedView != null && sharedElementName != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
current.setSharedElementReturnTransition(TransitionInflater.from(getContext()).inflateTransition(R.transition.default_transition));
current.setExitTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.no_transition));
_new.setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(R.transition.default_transition));
_new.setEnterTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.no_transition));
transaction.addSharedElement(sharedView, sharedElementName);
Log.i("ACTIVITY-DEBUG", sharedElementName + "/" + sharedView.getTransitionName());
}
}
transaction.replace(R.id.child_fragment, _new, tag);
transaction.addToBackStack(tag);
transaction.commit();
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/child_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFF">
<androidx.core.widget.NestedScrollView
android:id="@+id/home_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginLeft="9dp"
android:layout_marginRight="9dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="vertical">
</androidx.recyclerview.widget.RecyclerView>
</RelativeLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFF">
<androidx.appcompat.widget.Toolbar
android:id="@+id/z_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp">
<RelativeLayout
android:id="@+id/header_container"
android:layout_width="match_parent"
android:layout_height="56dp">
<ImageView
android:id="@+id/logo"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="10dp"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:src="@mipmap/ic_launcher_round"
android:contentDescription="@string/app_name" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/logo"
android:textColor="#000"
android:textSize="16sp"
android:text="Home" />
</RelativeLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
Please note that the transition for <changeTransform/>
and <changeBounds />
is visible because bounds of a selected View
in HomeFragmentList
are different from that in the child Fragment
.
Shared element transition does not work properly with FragmentTransaction.add
method because parent fragment is still alive and resumed. Try
transaction.replace(R.id.child_fragment, newFragment, tag);
Check this discussion thread for more information.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With