web-dev-qa-db-fra.com

Comment démarrer la transition d'élément partagé à l'aide de Fragments?

J'essaie d'implémenter des transitions entre des fragments qui ont des "éléments partagés" comme décrit dans les nouvelles spécifications de conception matérielle . La seule méthode que je puisse trouver est le ActivityOptionsCompat.makeSceneTransitionAnimation , qui, je crois, ne fonctionne que sur Activity. J'ai recherché cette même fonctionnalité mais avec/pour des fragments.

54
unchosen

J'ai eu le même problème mais cela fonctionnait en ajoutant un nouveau fragment à partir d'un autre fragment . Le lien suivant est très utile pour commencer à utiliser ceci: https://developer.Android.com/training/material/animations .html # Transitions

Voici mon code qui fonctionne. J'anime une ImageView d'un fragment à l'autre .. Assurez-vous que la View que vous souhaitez animer a le même Android:transitionName dans les deux fragments . L'autre contenu n'a pas d'importance.

En guise de test, vous pouvez le copier dans vos fichiers XML de mise en page. Assurez-vous que l'image existe.

<ImageView
Android:transitionName="MyTransition"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:scaleType="centerCrop"
Android:src="@drawable/test_image" />

Ensuite, j'ai 1 fichier dans mon dossier res/transition, nommé change_image_transform.xml.

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

Maintenant, vous pouvez commencer. Disons que vous avez le fragment A contenant l'image et que vous souhaitez ajouter le fragment B.

Exécutez ceci dans le fragment A:

@Override
public void onClick(View v) {
    switch(v.getId()) {
        case R.id.product_detail_image_click_area:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
                setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
                setExitTransition(TransitionInflater.from(getActivity()).inflateTransition(Android.R.transition.explode));

                // Create new fragment to add (Fragment B)
                Fragment fragment = new ImageFragment();
                fragment.setSharedElementEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));
                fragment.setEnterTransition(TransitionInflater.from(getActivity()).inflateTransition(Android.R.transition.explode));

                // Our shared element (in Fragment A)
                mProductImage   = (ImageView) mLayout.findViewById(R.id.product_detail_image);

                // Add Fragment B
                FragmentTransaction ft = getFragmentManager().beginTransaction()
                        .replace(R.id.container, fragment)
                        .addToBackStack("transaction")
                        .addSharedElement(mProductImage, "MyTransition");
                ft.commit();
            }
            else {
                // Code to run on older devices
            }
            break;
    }
}
42
ar34z

Je poste ceci comme réponse, puisque je suis nouveau ici et incapable de commenter.

Les transitions de fragment d'élément partagé do fonctionnent avec ListViews, tant que les vues source et cible ont le même (et unique) nom de transition.

Si vous faites en sorte que votre adaptateur de vue de liste définisse des noms de transition uniques sur les vues souhaitées (par exemple, un identifiant d'élément constant + spécifique) et également modifie votre fragment de détail afin de définir les mêmes noms de transition sur les vues cibles au moment de l'exécution (onCreateView) les transitions fonctionnent réellement!

21
Dimitris Kazakos

Les éléments partagés fonctionnent avec les fragments, mais il y a certaines choses à garder à l'esprit:

  1. N'essayez pas de définir la sharedElementsTransition dans la onCreateView de votre fragment. Vous devez les définir lors de la création d'une instance de votre fragment ou dans onCreate.

  2. Prenez note de la documentation officielle sur les animations possibles pour les transitions entrée/sortie et sharedElementTransition. Ils ne sont pas les mêmes.

  3. Essai et erreur :)

11
Jordy

Cela devrait être un commentaire sur la réponse acceptée, car je suis incapable de le commenter.

La réponse acceptée (par WindsurferOak et ar34z) fonctionne, sauf pour un problème "mineur" qui a provoqué une exception de pointeur null lors de la navigation avec le backStack. Il semble que setSharedElementReturnTransition() devrait être appelé sur le fragment cible au lieu du fragment d'origine.

Donc au lieu de:

setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));

cA devrait etre

fragment.setSharedElementReturnTransition(TransitionInflater.from(getActivity()).inflateTransition(R.transition.change_image_transform));

https://github.com/tevjef/Rutgers-Course-Tracker/issues/8

3
Paul L

La clé consiste à utiliser une transaction personnalisée avec

transaction.addSharedElement(sharedElement, "sharedImage");

Transition d'éléments partagés entre deux fragments

Dans cet exemple, l’un des deux ImageViews différents doit être traduit de ChooserFragment en DetailFragment.

Dans la présentation ChooserFragment, nous avons besoin des attributs uniques transitionName:

<ImageView
    Android:id="@+id/image_first"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:src="@drawable/ic_first"
    Android:transitionName="fistImage" />

<ImageView
    Android:id="@+id/image_second"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:src="@drawable/ic_second"
    Android:transitionName="secondImage" />

Dans la classe ChooserFragments, nous devons passer la View sur laquelle un utilisateur a cliqué et un ID au parent Activity qui gère le remplacement des fragments (nous avons besoin de l'ID pour savoir quelle ressource d'image afficher dans la DetailFragment). Comment passer des informations en détail à une activité parent est sûrement couvert dans une autre documentation.

view.findViewById(R.id.image_first).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (mCallback != null) {
            mCallback.showDetailFragment(view, 1);
        }
    }
});

view.findViewById(R.id.image_second).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (mCallback != null) {
            mCallback.showDetailFragment(view, 2);
        }
     }
});

Dans DetailFragment, la ImageView de l'élément partagé a également besoin de l'attribut unique transitionName.

<ImageView
    Android:id="@+id/image_shared"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_gravity="center"
    Android:transitionName="sharedImage" />

Dans la méthode onCreateView() de la DetailFragment, nous devons décider quelle ressource image doit être affichée (si nous ne le faisons pas, l'élément partagé disparaîtra après la transition).

public static DetailFragment newInstance(Bundle args) {
    DetailFragment fragment = new DetailFragment();
    fragment.setArguments(args);
    return fragment;
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    View view = inflater.inflate(R.layout.fragment_detail, container, false);

    ImageView sharedImage = (ImageView) view.findViewById(R.id.image_shared);

    // Check which resource should be shown.
    int type = getArguments().getInt("type");

    // Show image based on the type.
    switch (type) {
        case 1:
            sharedImage.setBackgroundResource(R.drawable.ic_first);
            break;

        case 2:
            sharedImage.setBackgroundResource(R.drawable.ic_second);
            break;
    }

    return view;
}

Le parent Activity reçoit les rappels et gère le remplacement des fragments.

@Override
public void showDetailFragment(View sharedElement, int type) {
    // Get the chooser fragment, which is shown in the moment.
    Fragment chooserFragment = getFragmentManager().findFragmentById(R.id.fragment_container);

    // Set up the DetailFragment and put the type as argument.
    Bundle args = new Bundle();
    args.putInt("type", type);
    Fragment fragment = DetailFragment.newInstance(args);

    // Set up the transaction.
    FragmentTransaction transaction = getFragmentManager().beginTransaction();

    // Define the shared element transition.
    fragment.setSharedElementEnterTransition(new DetailsTransition());
    fragment.setSharedElementReturnTransition(new DetailsTransition());

    // The rest of the views are just fading in/out.
    fragment.setEnterTransition(new Fade());
    chooserFragment.setExitTransition(new Fade());

    // Now use the image's view and the target transitionName to define the shared element.
    transaction.addSharedElement(sharedElement, "sharedImage");

    // Replace the fragment.
    transaction.replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName());

    // Enable back navigation with shared element transitions.
    transaction.addToBackStack(fragment.getClass().getSimpleName());

    // Finally press play.
    transaction.commit();
}

Ne pas oublier - la Transition elle-même. Cet exemple déplace et met à l'échelle l'élément partagé.

@TargetApi(Build.VERSION_CODES.Lollipop)
public class DetailsTransition extends TransitionSet {

    public DetailsTransition() {
        setOrdering(ORDERING_TOGETHER);
        addTransition(new ChangeBounds()).
            addTransition(new ChangeTransform()).
            addTransition(new ChangeImageTransform());
    }

}
0
Marius
0
Lym Zoy

J'ai cherché SharedElement par fragments et je trouve le code source très utile sur GitHub.

1. D'abord, vous devez définir nom de transition pour vos objets (comme ImageView) dans la mise en page Fragments (nous ajoutons un bouton dans le fragment A pour gérer l'événement de clic):

fragment A :

  <ImageView
    Android:id="@+id/fragment_a_imageView"
    Android:layout_width="128dp"
    Android:layout_height="96dp"
    Android:layout_alignParentBottom="true"
    Android:layout_centerHorizontal="true"
    Android:layout_marginBottom="80dp"
    Android:scaleType="centerCrop"
    Android:src="@drawable/gorilla"
    Android:transitionName="@string/simple_fragment_transition />

<Button
    Android:id="@+id/fragment_a_btn"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_alignParentBottom="true"
    Android:layout_centerHorizontal="true"
    Android:layout_marginBottom="24dp"
    Android:text="@string/gorilla" />

fragment B:

    <ImageView
    Android:id="@+id/fragment_b_image"
    Android:layout_width="match_parent"
    Android:layout_height="250dp"
    Android:scaleType="centerCrop"
    Android:src="@drawable/gorilla"
    Android:transitionName="@string/simple_fragment_transition" />
  1. Ensuite, vous devez écrire ce code dans votre fichier de transition dans le répertoire de transition (si vous ne l'avez pas encore créé, créez One: res> new> Android répertoire de ressources> type de ressource = transition> name = change_image_transform):

change_image_transform.xml:

 <?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:Android="http://schemas.Android.com/apk/res/Android">
  <changeBounds/>
  <changeTransform/>
  <changeClipBounds/>
  <changeImageTransform/>
</transitionSet>
  1. Dans la dernière étape, vous devez compléter les codes en Java:

fragment A:

public class FragmentA extends Fragment {

    public static final String TAG = FragmentA.class.getSimpleName();


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_a, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        final ImageView imageView = (ImageView) view.findViewById(R.id.fragment_a_imageView);
        Button button = (Button) view.findViewById(R.id.fragment_a_btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getFragmentManager()
                        .beginTransaction()
                        .addSharedElement(imageView, ViewCompat.getTransitionName(imageView))
                        .addToBackStack(TAG)
                        .replace(R.id.content, new FragmentB())
                        .commit();
            }
        });
    }
}

fragment B:

public class FragmentB extends Fragment {

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
            setSharedElementEnterTransition(TransitionInflater.from(getContext()).inflateTransition(Android.R.transition.move));

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_b, container, false);
    }
}

n'oubliez pas de montrer votre fragment "A" dans votre activité:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.content, new SimpleFragmentA())
                .commit();
    }

source: https://github.com/mikescamell/shared-element-transitions

0
yasin