web-dev-qa-db-fra.com

La transition d'élément partagé ne fonctionne pas

J'ai fait un projet github avec juste le problème. Vous pouvez le voir/le cloner/le construire à partir d’ici: https://git.io/vMPqb


J'essaie de faire en sorte que des éléments partagés fonctionnent pour une transition de fragment.

Le projet comporte deux FAB: Feather et Plane. Plume et Avion sont des éléments partagés. Lorsque l'utilisateur clique sur Feather, la feuille de dialogue s'ouvre et Feather devrait s'animer dans la boîte de dialogue Plan. Cela ne fait pas cela pour le moment, et j'essaie de déterminer pourquoi.

Il vaut peut-être la peine de noter que j'exécute cette application sur l'API 24, de sorte que le problème ne concerne pas les transitions non prises en charge par la version 21.

Quelqu'un peut-il me dire pourquoi les transitions d'éléments partagés ne fonctionnent pas?

Pour faire écho à ce qui est dans le repo, il y a quatre fichiers importants:

Activité principale

package test.example.fabpop;

import Android.os.Bundle;
import Android.support.design.widget.FloatingActionButton;
import Android.support.transition.ChangeBounds;
import Android.support.transition.Fade;
import Android.support.v4.app.FragmentTransaction;
import Android.support.v7.app.AppCompatActivity;
import Android.view.View;

public class MainActivity extends AppCompatActivity {

    FloatingActionButton fab_feather;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fab_feather = (FloatingActionButton) findViewById(R.id.initial_fab_feather);
    }

    public void fabClick(View view) {
        SheetDialog dialogFragment = new SheetDialog();

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

        // This seemingly has no effect. I am trying to get it to work.
        transaction.addSharedElement(fab_feather, "transition_name_plane");

        dialogFragment.setSharedElementEnterTransition(new ChangeBounds());
        dialogFragment.setSharedElementReturnTransition(new Fade(Fade.OUT));

        dialogFragment.show(transaction, "frag_tag");
    }
}

activity_main.xml Layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:id="@+id/activity_main"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:paddingBottom="@dimen/activity_vertical_margin"
    Android:paddingLeft="@dimen/activity_horizontal_margin"
    Android:paddingRight="@dimen/activity_horizontal_margin"
    Android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="test.example.fabpop.MainActivity">

    <LinearLayout
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_alignParentEnd="true"
        Android:orientation="vertical">
        <TextView
            Android:layout_width="200dp"
            Android:layout_height="wrap_content"
            Android:text="Transition to a BottomSheetDialogFragment that shares this FAB"
            Android:textAlignment="center"/>

        <!--  Feather FAB  -->
        <Android.support.design.widget.FloatingActionButton
            Android:id="@+id/initial_fab_feather"
            Android:layout_width="52dp"
            Android:layout_height="52dp"
            Android:layout_margin="16dp"
            Android:layout_gravity="center"
            Android:onClick="fabClick"
            Android:transitionName="transition_name_feather"
            Android:src="@drawable/ic_feather"
            />
    </LinearLayout>


</RelativeLayout>

SheetDialog

package test.example.fabpop;

import Android.os.Bundle;
import Android.support.annotation.Nullable;
import Android.support.design.widget.BottomSheetDialogFragment;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;

public class SheetDialog extends BottomSheetDialogFragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater,
                             @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {

        return inflater.inflate(R.layout.dialog_sheet, container, false);
    }
}

dialog_sheet.xml Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:orientation="vertical" Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <!--  Plane FAB  -->
    <Android.support.design.widget.FloatingActionButton
        Android:id="@+id/final_fab_plane"
        Android:layout_width="75dp"
        Android:layout_height="75dp"
        Android:layout_margin="16dp"
        Android:transitionName="transition_name_plane"
        Android:src="@drawable/ic_plane"
        />

</LinearLayout>

N'est-il pas possible d'avoir un élément partagé sur une transition entre une activité et un fragment? Peut-être n’est-ce possible que d’une activité à l’autre ou d’un fragment à l’autre, mais pas entre les deux types? Peut-être que c'est pourquoi je ne peux pas le faire fonctionner?

Mettre à jour:

J'ai maintenant essayé d'ajouter <item name="Android:windowContentTransitions">true</item> au thème de l'application. 

J'ai également essayé maintenant en m'assurant que les deux valeurs transitionName sont les mêmes sur les deux vues.

aucun de ceux-ci n'a aidé à résoudre le problème.

17
atwrk

Avant de plonger dans ...

Commençons par souligner quelques points sur la manière dont la structure Android effectue la transition magique des éléments partagés.

Une transition d'éléments partagés n'est qu'un des mensonges du framework Android. Lorsque vous effectuez une transition d'élément partagé, vous ne partagez aucune vue entre vos activités. Par exemple, chaque activité possède une arborescence de vues indépendante.

Supposons que vous essayez de faire passer une element identifiée par element1 de Activity1 à un element2 dans Activity2

Le cadre fait en sorte qu'il recherche certaines informations telles que la taille (width, height) et la position (x, y) de votre element1 dans le Activity1. Il transmet ensuite ces informations à Activity2, les applique sur le element2 et lance la transition d'activité en inversant l'animation de votre element2, ce qui crée cette illusion d'élément partagé.

Cependant, ceci nécessite la création de Activity2, la view correspondant à element2 avant de pouvoir démarrer une animation.

Dans le cas où vous utilisez une Fragment, appeler FragmentTransaction.commit()servera simplement planifier votre transaction de fragment (le fragment ne sera pas créé immédiatement). Ainsi, lorsque votre Activity2 sera créé, votre element2 sera manquant (comme expliqué, son fragment contenant n'a pas encore été créé ).

Solution

Assurez-vous que votre element2 est créé avant le début de l'animation. Vous devrez donc trouver un moyen de dire au framework de ne pas faire la chose habituelle, mais d'attendre votre signal avant de créer l'animation.

Une façon de faire est de modifier votre code pour qu’il ressemble à celui-ci:

Activité2

class Activity2 extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        // Tell the framework to wait.
        postponeEnterTransition();
    }
}

Fragment2

class Fragment2 extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View element2 = getView().findViewById(R.id.element);
    sharedview.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
                // Tell the framework to start.
                sharedview.getViewTreeObserver().removeOnPreDrawListener(this);
                getActivity().startPostponedEnterTransition(); 
                return true;
         }
       });
       ...
    }
}

Lectures complémentaires

Mise en route avec les transitions d'activité et de fragment

8
Anix PasBesoin

Ce que vous recherchez, c’est cet exemple: https://github.com/hujiaweibujidao/FabDialogMorph . Notez que ce que vous essayez d’obtenir n’est pas une transition Android standard, vous devez créer votre propre transition de morphing, basée sur la transition ChangeBounds. A l'origine, il a été montré dans Plaid , grâce à Nick Butcher pour ça, vous pouvez donc le vérifier pour plus de conseils et de fond.

1
rom4ek

Je viens de citer le Android Docs

Démarrer une activité avec un élément partagé

  1. Attribuez un nom commun aux éléments partagés dans les deux mises en page avec l'attribut Android: transitionName.

D'après ce que je vois, vos deux fichiers XML ne partagent pas le même Android: nom de transition.

Essayez de remplacer les deux Android: transitionName dans la présentation XML (activity_main.xml & dialog_sheet.xml) par une même chaîne, par exemple: transition_plane_feather

Je n'ai pas testé les codes moi-même, mais je pense que cela pourrait être un bon point de départ.

Astuce Bonus

Si vous envisagez de mettre pleinement en œuvre Material Design sans compatibilité avec les niveaux d'API inférieurs (pré-Lollipop), je vous recommande vivement de consulter un exemple créé par un concepteur Google UI/UX: https://github.com/nickbutcher/ plaid

Découvrez également l'excellente vidéo YouTube complémentaire à cet exemple: https://www.youtube.com/watch?v=EjTJIDKT72M

Il vous montre quelques-unes des meilleures astuces et pratiques que vous pouvez utiliser pour implémenter entièrement la conception de matériaux ainsi que les transitions d'éléments partagés.

1
Nicholas Lie