Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sharedElment transition using fragments not transitioning

AndroidStudio 2.3 Beta 1

I am trying to get transitions work with fragments, how the image I am using doesn't transition. It just pop up as normal.

I have created a simple App to try and get this to work. I have 2 fragments ListMovieFragment and DetailMovieFragment. And 1 MainActivity.

The user will click the image in ListMovieFragment to transition to DetailMovieFragment.

This is my Transition xml change_image_transform:

<?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();
        }
    }
}

The 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>

The string name for the transition name:

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

The implementation seems simple so I think my mistake is something i'm overlooking.

Many thanks for any suggestions,

like image 824
ant2009 Avatar asked Dec 22 '16 17:12

ant2009


Video Answer


1 Answers

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

  1. You're creating a new instance of ListMovieFragment and then apply transition logic to it. But you forgot that you already have the instance of this fragment (you were created it in onCreate method). So you need to retrieve the existing ListMovieFragment object from FragmentManager, and apply your transitions to it.
  2. You're applying transition logic to one instance of DetailMovieFragment, but then suddenly replacing the ListMoveFragment with the 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.

P.S. I've used your code and run it, and noticed you have some strange transitions, so if you will have some issues, check this great article, how to make smooth and user-friendly transitions with fragments:)

like image 57
romtsn Avatar answered Oct 24 '22 17:10

romtsn