Afficher le DialogFragment avec une animation croissante à partir d'un point

93

J'affiche un DialogFragmentlorsque l'utilisateur appuie sur une ligne dans un ListView. Je voudrais animer l'affichage du dialogue pour qu'il se développe à partir du centre de la ligne. Un effet similaire peut être observé lors de l'ouverture d'un dossier à partir du lanceur.

Une idée que j'ai eue est une combinaison de TranslateAnimationet ScaleAnimation. Y a-t-il un autre moyen?

Edward Dale
la source
1
Reportez-vous au lien pour les animations sur DialogFragment.
ASH

Réponses:

168

En tant DialogFragmentque wrapper pour la Dialogclasse, vous devez définir un thème sur votre base Dialogpour obtenir l'animation souhaitée:

public class CustomDialogFragment extends DialogFragment implements OnEditorActionListener
{
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) 
    {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) 
    {
        // Set a theme on the dialog builder constructor!
        AlertDialog.Builder builder = 
            new AlertDialog.Builder( getActivity(), R.style.MyCustomTheme );

        builder  
        .setTitle( "Your title" )
        .setMessage( "Your message" )
        .setPositiveButton( "OK" , new DialogInterface.OnClickListener() 
            {      
              @Override
              public void onClick(DialogInterface dialog, int which) {
              dismiss();                  
            }
        });
        return builder.create();
    }
}

Ensuite, il vous suffit de définir le thème qui inclura l'animation souhaitée. Dans styles.xml, ajoutez votre thème personnalisé:

<style name="MyCustomTheme" parent="@android:style/Theme.Panel">
    <item name="android:windowAnimationStyle">@style/MyAnimation.Window</item>
</style>

<style name="MyAnimation.Window" parent="@android:style/Animation.Activity"> 
    <item name="android:windowEnterAnimation">@anim/anim_in</item>
    <item name="android:windowExitAnimation">@anim/anim_out</item>
</style>    

Ajoutez maintenant les fichiers d'animation dans le dossier res / anim :

( android:pivotYc'est la clé)

anim_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:interpolator="@android:anim/linear_interpolator"
        android:fromXScale="0.0"
        android:toXScale="1.0"
        android:fromYScale="0.0"
        android:toYScale="1.0"
        android:fillAfter="false"
        android:startOffset="200"
        android:duration="200" 
        android:pivotX = "50%"
        android:pivotY = "-90%"
    />
    <translate
        android:fromYDelta="50%"
        android:toYDelta="0"
        android:startOffset="200"
        android:duration="200"
    />
</set>

anim_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:interpolator="@android:anim/linear_interpolator"
        android:fromXScale="1.0"
        android:toXScale="0.0"
        android:fromYScale="1.0"
        android:toYScale="0.0"
        android:fillAfter="false"
        android:duration="200" 
        android:pivotX = "50%"        
        android:pivotY = "-90%"        
    />
    <translate
        android:fromYDelta="0"
        android:toYDelta="50%"
        android:duration="200"
    />
</set>

Enfin, le plus délicat ici est de faire croître votre animation à partir du centre de chaque ligne. Je suppose que la ligne remplit l'écran horizontalement, donc, d'une part, la android:pivotXvaleur sera statique. En revanche, vous ne pouvez pas modifier la android:pivotYvaleur par programme.

Ce que je suggère, c'est que vous définissiez plusieurs animations ayant chacune une valeur de pourcentage différente sur l' android:pivotYattribut (et plusieurs thèmes référençant ces animations). Ensuite, lorsque l'utilisateur appuie sur la ligne, calculez la position Y en pourcentage de la ligne à l'écran. Connaissant la position en pourcentage, attribuez à votre boîte de dialogue un thème ayant la android:pivotYvaleur appropriée .

Ce n'est pas une solution parfaite mais pourrait faire l'affaire pour vous. Si vous n'aimez pas le résultat, alors je vous suggère d'oublier DialogFragmentet d'animer une simple Viewcroissance à partir du centre exact de la ligne.

Bonne chance!

Xavi Gil
la source
Bonne idée avec les multiples animations pour différents endroits sur l'écran. C'est un hack, mais cela semble être le seul moyen.
Edward Dale
Cela ne fonctionnera qu'avec> API 11 @Kiran Babu La réponse est une solution
Blundell
Savez-vous s'il existe un moyen possible d'obtenir un rappel lorsque ces animations sont terminées? Je voudrais faire une animation en deux parties, une dans laquelle la boîte de dialogue glisse, puis fondre les vues. Comment attacher un auditeur à windowAnimations?
android_student
Excellent! C'est exactement ce que j'ai recherché!
Aleksey Timoshchenko
2
Le réglage du thème peut être aussi simple que de le faire setStyle(DialogFragment.STYLE_NORMAL, R.style.MyCustomTheme)sur onCreate(bundle)Voir: developer.android.com/reference/android/app/DialogFragment.html
tropicalfish
117

Vérifiez ce code, cela fonctionne pour moi

// Faire glisser l'animation vers le haut

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

    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromYDelta="100%"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toXDelta="0" />

</set>

// Animation de diapositives dowm

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

    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromYDelta="0%p"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:toYDelta="100%p" />

</set>

// Style

<style name="DialogAnimation">
    <item name="android:windowEnterAnimation">@anim/slide_up</item>
    <item name="android:windowExitAnimation">@anim/slide_down</item>
</style>

// Fragment de dialogue intérieur

@Override
public void onActivityCreated(Bundle arg0) {
    super.onActivityCreated(arg0);
    getDialog().getWindow()
    .getAttributes().windowAnimations = R.style.DialogAnimation;
}
Kiran Babu
la source
8
Désolé, cela ne répond pas du tout à ma question.
Edward Dale
3
Ce n'est peut-être pas exactement ce que demande le PO, mais c'est un bon point d'entrée, je pense.
mdelolmo
2
Excellent exemple! Le seul échantillon qui a fonctionné pour moi. L'astuce consiste à définir une animation / un thème dans la méthode onActivityCreated (...)
tomurka
3
Cela peut être utile mais cela ne répond pas du tout à la question.
Olayinka
Savez-vous s'il existe un moyen possible d'obtenir un rappel lorsque ces animations sont terminées? Je voudrais faire une animation en deux parties, une dans laquelle la boîte de dialogue glisse, puis fondre les vues. Comment attacher un auditeur à windowAnimations?
android_student
22

DialogFragment a une méthode publique getTheme () que vous pouvez surcharger pour cette raison exacte. Cette solution utilise moins de lignes de code:

public class MyCustomDialogFragment extends DialogFragment{
    ...
    @Override
    public int getTheme() {
        return R.style.MyThemeWithCustomAnimation;
    }
}
Siavash
la source
1
Plus de soucis ... Solution simple et directe.
Noundla Sandeep le
C'est la meilleure réponse pour moi!
superutilisateur
Bien que cela ne soit pas lié à la question d'origine, j'avais une galerie affichée dans MainAcitvity où le clic de l'utilisateur activera le diaporama dans DialogFragment. Il y a cependant une secousse dans MainActivity à chaque retour du diaporama. Cela est dû à l'utilisation de MainActivity AppTheme.NoActionBaralors que DialogFragment utilise la valeur par défaut AppTheme. La méthode ci-dessus a résolu mon problème en ayant un thème cohérent à la fois dans le fragment et dans l'activité.
tingyik90
Bro, you are genius
Jacky Chong
Cette solution a fonctionné pour moi. Cependant, une chose m'a confondu; définir windowEnterAnimation ou windowExitAnimation directement dans le thème ne ferait aucune différence dans mes animations DialogFragments. La seule chose qui a fonctionné pour moi a été de définir windowAnimationStyle sur un fichier XML séparé qui définissait le style et y définissait les animations d'entrée et de sortie
szaske
9

Pour obtenir une boîte de dialogue plein écran avec animation, écrivez ce qui suit ...

Modes:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="actionModeBackground">?attr/colorPrimary</item>
    <item name="windowActionModeOverlay">true</item>
</style>

<style name="AppTheme.NoActionBar">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

<style name="AppTheme.NoActionBar.FullScreenDialog">
    <item name="android:windowAnimationStyle">@style/Animation.WindowSlideUpDown</item>
</style>

<style name="Animation.WindowSlideUpDown" parent="@android:style/Animation.Activity">
    <item name="android:windowEnterAnimation">@anim/slide_up</item>
    <item name="android:windowExitAnimation">@anim/slide_down</item>
</style>

res / anim / slide_up.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="@android:interpolator/accelerate_quad">

    <translate
        android:duration="@android:integer/config_shortAnimTime"
        android:fromYDelta="100%"
        android:toYDelta="0%"/>
</set>

res / anim / slide_down.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="@android:interpolator/accelerate_quad">

    <translate
        android:duration="@android:integer/config_shortAnimTime"
        android:fromYDelta="0%"
        android:toYDelta="100%"/>
</set>

Code Java:

public class MyDialog extends DialogFragment {

    @Override
    public int getTheme() {
        return R.style.AppTheme_NoActionBar_FullScreenDialog;
    }
}

private void showDialog() {
    FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
    Fragment previous = getSupportFragmentManager().findFragmentByTag(MyDialog.class.getName());
    if (previous != null) {
        fragmentTransaction.remove(previous);
    }
    fragmentTransaction.addToBackStack(null);

    MyDialog dialog = new MyDialog();
    dialog.show(fragmentTransaction, MyDialog.class.getName());
}
maXp
la source
4

Utilisez la vue décor dans onStart dans votre fragment de dialogue

@Override
public void onStart() {
    super.onStart();


    final View decorView = getDialog()
            .getWindow()
            .getDecorView();

    decorView.animate().translationY(-100)
            .setStartDelay(300)
            .setDuration(300)
            .start();

}
Horatio
la source
1
La vue sous-jacente ne semble pas être cliquable par la suite
HaydenKai
3

Dans DialogFragment, l'animation personnalisée est appelée onCreateDialog. «DialogAnimation» est un style d'animation personnalisé dans la réponse précédente.

public Dialog onCreateDialog(Bundle savedInstanceState) 
{
    final Dialog dialog = super.onCreateDialog(savedInstanceState);
    dialog.getWindow().getAttributes().windowAnimations = R.style.DialogAnimation;
    return dialog;
}
Brownsoo Han
la source
1

Avez-vous regardé la formation des développeurs Android sur le zoom d'une vue ? Cela pourrait être un bon point de départ.

Vous souhaitez probablement créer une classe personnalisée étendant DialogFragment pour que cela fonctionne.

Jetez également un œil à la compatibilité de l'API Jake Whartons NineOldAndroids pour Honeycomb Animation jusqu'au niveau 1 de l'API.

grève
la source
1

Si vous voulez travailler sur des API, vous devez le faire dans votre DialogFragemnt-> onStart et non dans onCreateDialog

@Override
    public void onStart() 
    {
        if (getDialog() == null) 
        {
            return;
        }

        getDialog().getWindow().setWindowAnimations(
                  R.style.DlgAnimation);

        super.onStart();
    }
Latéral
la source
Meilleure solution à mon avis. Cela fonctionne également pour AlertDialogs, utilisez-le simplement dans onShowListener. Merci!
Gabor Novak
0

Ajouter ce code sur les valeurs anim

 <scale
    android:duration="@android:integer/config_longAnimTime"
    android:fromXScale="0.2"
    android:fromYScale="0.2"
    android:toXScale="1.0"
    android:toYScale="1.0"
    android:pivotX="50%"
    android:pivotY="50%"/>
<alpha
    android:fromAlpha="0.1"
    android:toAlpha="1.0"
    android:duration="@android:integer/config_longAnimTime"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"/>

appelez styles.xml

<style name="DialogScale">
    <item name="android:windowEnterAnimation">@anim/scale_in</item>
    <item name="android:windowExitAnimation">@anim/scale_out</item>
</style>

Sur le code java: définir Onclick

public void onClick(View v) {
        fab_onclick(R.style.DialogScale, "Scale" ,(Activity) context,getWindow().getDecorView().getRootView());
      //  Dialogs.fab_onclick(R.style.DialogScale, "Scale");

    }

configuration sur la méthode:

alertDialog.getWindow().getAttributes().windowAnimations = type;
Mue
la source