SharedElment transition using non-transition fragments

AndroidStudio 2.3 Beta 1 

I am trying to make transitions work with fragments, as the image that I use does not go through. He just appears as usual.

I created a simple application to try to get it to work. I have 2 ListMovieFragment and DetailMovieFragment . And 1 MainActivity .

The user clicks the image in ListMovieFragment to go to DetailMovieFragment .

This is my xml change_image_transform transition:

 <?xml version="1.0" encoding="utf-8"?> <transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> <changeImageTransform/> </transitionSet> 

ListMovieFragment:

 public class ListMovieFragment extends Fragment { public interface MovieSelectedListener { void onMovieSelected(int movieId); } private MovieSelectedListener mMovieSelectedListener; public ListMovieFragment() { } @Override public void onAttach(Context context) { super.onAttach(context); mMovieSelectedListener = (MovieSelectedListener)context; } @Override public void onDetach() { super.onDetach(); mMovieSelectedListener = null; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment final View view = inflater.inflate(R.layout.fragment_list, container, false); final ImageView ivMoviePoster = (ImageView)view.findViewById(R.id.ivMoviePoster); ivMoviePoster.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(mMovieSelectedListener != null) { mMovieSelectedListener.onMovieSelected(12345); } } }); Glide.with(getActivity()) .load("https://image.tmdb.org/t/p/w185/qjiskwlV1qQzRCjpV0cL9pEMF9a.jpg") .placeholder(R.drawable.placeholder_poster) .centerCrop() .crossFade() .into(ivMoviePoster); return view; } } 

DetailMovieFragment

 public class DetailMovieFragment extends Fragment { public DetailMovieFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.fragment_detail, container, false); final ImageView ivMovieDetailPoster = (ImageView)view.findViewById(R.id.ivMovieDetailPoster); Glide.with(getActivity()) .load("https://image.tmdb.org/t/p/w185/qjiskwlV1qQzRCjpV0cL9pEMF9a.jpg") .placeholder(R.drawable.placeholder_poster) .centerCrop() .crossFade() .into(ivMovieDetailPoster); return view; } } 

Mainactivity

 public class MainActivity extends AppCompatActivity implements ListMovieFragment.MovieSelectedListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(savedInstanceState == null) { FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.add(R.id.activity_main, new ListMovieFragment(), "listmoviefragment"); fragmentTransaction.commit(); } } @Override public void onMovieSelected(int movieId) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { /* Get the fragments that will be using the transition */ ListMovieFragment listMovieFragment = new ListMovieFragment(); DetailMovieFragment detailMovieFragment = new DetailMovieFragment(); /* Inflate the transition */ Transition changeTransition = TransitionInflater .from(MainActivity.this) .inflateTransition(R.transition.change_image_transform); Transition explodeTransition = TransitionInflater .from(MainActivity.this) .inflateTransition(android.R.transition.explode); /* Set the exit and return on the source fragment (ListMovieFragment) */ listMovieFragment.setSharedElementReturnTransition(changeTransition); listMovieFragment.setExitTransition(explodeTransition); /* Set the enter on the destination fragment (MovieDetailFragment) */ detailMovieFragment.setSharedElementEnterTransition(changeTransition); detailMovieFragment.setEnterTransition(explodeTransition); /* Get the shared imageview from the source fragment (MovieListFragment) */ final ImageView ivSharedImage = (ImageView)findViewById(R.id.ivMoviePoster); FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.activity_main, new DetailMovieFragment(), "detailmoviefragment"); fragmentTransaction.addToBackStack("detailmoviefragment"); fragmentTransaction.addSharedElement(ivSharedImage, getResources().getString(R.string.transition_poster_image)); fragmentTransaction.commit(); } else { FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.activity_main, new DetailMovieFragment(), "detailmoviefragment"); fragmentTransaction.addToBackStack("detailmoviefragment"); fragmentTransaction.commit(); } } @Override public void onBackPressed() { if(getSupportFragmentManager().getBackStackEntryCount() > 0) { getSupportFragmentManager().popBackStackImmediate(); } else { super.onBackPressed(); } } } 

Layout for ListMovieFragment

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="6dp"> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ivMoviePoster" android:layout_width="match_parent" android:layout_height="300dp" android:src="@drawable/placeholder_poster" android:scaleType="fitXY" android:adjustViewBounds="true" android:transitionName="@string/transition_poster_image"> </ImageView> </LinearLayout> 

Layout for DetailMovieFragment:

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="me.androidbox.fragmenttransitions.detail.DetailMovieFragment"> <ImageView android:id="@+id/ivMovieDetailPoster" android:layout_width="140dp" android:layout_height="160dp" android:layout_marginEnd="16dp" android:layout_marginTop="112dp" android:adjustViewBounds="true" android:scaleType="fitXY" android:layout_gravity="end" android:transitionName="@string/transition_poster_image"/> </FrameLayout> 

String Name for Jump Name:

 <string name="transition_poster_image">imagePoster</string> 

The implementation seems simple, so I think my mistake is what I'm missing.

Thanks so much for any suggestions,

+5
source share
2 answers

You have two problems in your code inside the onMovieSelected method:

  • You create a new instance of ListMovieFragment , and then apply the transition logic to it. But you forgot that you already have an instance of this fragment (you were created in the onCreate method). Therefore, you need to get the existing ListMovieFragment object from the FragmentManager and apply your transitions to it.
  • You apply transition logic to one instance of DetailMovieFragment , but then suddenly replace ListMoveFragment with a new one.

So your fixed onMovieSelected method will be:

 @Override public void onMovieSelected(int movieId) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { /* Get the fragments that will be using the transition */ ListMovieFragment listMovieFragment = (ListMovieFragment) getSupportFragmentManager().findFragmentByTag("listmoviefragment"); DetailMovieFragment detailMovieFragment = new DetailMovieFragment(); /* Inflate the transition */ Transition changeTransition = TransitionInflater .from(MainActivity.this) .inflateTransition(R.transition.change_image_transform); Transition explodeTransition = TransitionInflater .from(MainActivity.this) .inflateTransition(android.R.transition.explode); /* Set the exit and return on the source fragment (ListMovieFragment) */ listMovieFragment.setSharedElementReturnTransition(changeTransition); listMovieFragment.setExitTransition(explodeTransition); /* Set the enter on the destination fragment (MovieDetailFragment) */ detailMovieFragment.setSharedElementEnterTransition(changeTransition); detailMovieFragment.setEnterTransition(explodeTransition); /* Get the shared imageview from the source fragment (MovieListFragment) */ final ImageView ivSharedImage = (ImageView)findViewById(R.id.ivMoviePoster); FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.activity_main, detailMovieFragment, "detailmoviefragment"); fragmentTransaction.addToBackStack("detailmoviefragment"); fragmentTransaction.addSharedElement(ivSharedImage, getResources().getString(R.string.transition_poster_image)); fragmentTransaction.commit(); } else { FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.activity_main, new DetailMovieFragment(), "detailmoviefragment"); fragmentTransaction.addToBackStack("detailmoviefragment"); fragmentTransaction.commit(); } } 

Now this should work.

PS I used your code and ran it, and noticed that you have some strange transitions, so if you have some problems, check out a great article on how to make smooth and user-friendly transitions with fragments :)

+10
source

Do not create a new fragment of the object when adding a fragment transaction, reuse the existing fragment in which you applied the input and output transition.

  public class MainActivity extends AppCompatActivity implements ListMovieFragment.MovieSelectedListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(savedInstanceState == null) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ListMovieFragment listMovieFragment = new ListMovieFragment(); Transition changeTransition = TransitionInflater .from(MainActivity.this) .inflateTransition(R.transition.change_image_transform); Transition explodeTransition = TransitionInflater .from(MainActivity.this) .inflateTransition(android.R.transition.explode); listMovieFragment.setSharedElementReturnTransition(changeTransition); listMovieFragment.setExitTransition(explodeTransition); FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); //don't create new ListMovieFragment use existing listMovieFragment instance fragmentTransaction.add(R.id.activity_main, listMovieFragment, "listmoviefragment"); fragmentTransaction.commit(); }else { FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.add(R.id.activity_main, new ListMovieFragment(), "listmoviefragment"); fragmentTransaction.commit(); } } } @Override public void onMovieSelected(int movieId) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { /* Get the fragments that will be using the transition */ ListMovieFragment listMovieFragment = new ListMovieFragment(); DetailMovieFragment detailMovieFragment = new DetailMovieFragment(); /* Inflate the transition */ Transition changeTransition = TransitionInflater .from(MainActivity.this) .inflateTransition(R.transition.change_image_transform); Transition explodeTransition = TransitionInflater .from(MainActivity.this) .inflateTransition(android.R.transition.explode); /* Set the exit and return on the source fragment (ListMovieFragment) */ listMovieFragment.setSharedElementReturnTransition(changeTransition); listMovieFragment.setExitTransition(explodeTransition); /* Set the enter on the destination fragment (MovieDetailFragment) */ detailMovieFragment.setSharedElementEnterTransition(changeTransition); detailMovieFragment.setEnterTransition(explodeTransition); /* Get the shared imageview from the source fragment (MovieListFragment) */ final ImageView ivSharedImage = (ImageView)findViewById(R.id.ivMoviePoster); FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); //don't create new DetailMovieFragment use existing detailMovieFragment instance fragmentTransaction.replace(R.id.activity_main, detailMovieFragment, "detailmoviefragment"); fragmentTransaction.addToBackStack("detailmoviefragment"); fragmentTransaction.addSharedElement(ivSharedImage, getResources().getString(R.string.transition_poster_image)); fragmentTransaction.commit(); } else { FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.activity_main, new DetailMovieFragment(), "detailmoviefragment"); fragmentTransaction.addToBackStack("detailmoviefragment"); fragmentTransaction.commit(); } } @Override public void onBackPressed() { if(getSupportFragmentManager().getBackStackEntryCount() > 0) { getSupportFragmentManager().popBackStackImmediate(); } else { super.onBackPressed(); } } } 
+3
source

Source: https://habr.com/ru/post/1261736/


All Articles