[JAVA] Une histoire qui a rendu pratique avec Kotlin qu'il est difficile d'exécuter une animation en continu sur Android

J'ai eu l'opportunité de mettre en place une animation "ressembler à flottant" sur Android. Cela ressemble à l'image suivante.

Cette animation

  1. Montez un peu plus de 2 secondes
  2. Descendez un peu plus de 2 secondes

Est réalisé en exécutant "en continu" et "à plusieurs reprises". «En continu» signifie que lorsque l'animation de 1. est terminée, l'animation de 2. est lancée.

Java sur Android ...

Faire cela avec l'API d'animation View d'Android est généralement un code terrible. Ensuite, c'est ça.

//Animation qui monte en 2 secondes
final TranslateAnimation anim1 = new TranslateAnimation(
        Animation.RELATIVE_TO_SELF,  0.0f,
        Animation.RELATIVE_TO_SELF,  0.0f,
        Animation.RELATIVE_TO_SELF,  0.0f,
        Animation.RELATIVE_TO_SELF, -0.1f);
anim1.setDuration(2000);

//Animation qui descend en 2 secondes
final TranslateAnimation anim2 = new TranslateAnimation(
        Animation.RELATIVE_TO_SELF,  0.0f,
        Animation.RELATIVE_TO_SELF,  0.0f,
        Animation.RELATIVE_TO_SELF, -0.1f,
        Animation.RELATIVE_TO_SELF,  0.0f);
anim2.setDuration(2000);

anim1.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) { }

    @Override
    public void onAnimationEnd(Animation animation) {
        anim2.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) { }

            @Override
            public void onAnimationEnd(Animation animation) {
                // 3.Lorsque l'animation descendante est terminée, redémarrez l'animation ascendante
                view.startAnimation(anim1);
            }

            @Override
            public void onAnimationRepeat(Animation animation) { }
        });

        // 2.Lorsque l'animation vers le haut est terminée, démarrez l'animation vers le bas
        view.startAnimation(anim2);
    }

    @Override
    public void onAnimationRepeat(Animation animation) { }
});

// 1.Lancer l'animation pour monter
view.startAnimation(anim1);

La double douleur de l'imbrication des rappels est que le processus que vous souhaitez effectuer et l'ordre dans lequel vous écrivez le code sont inversés, ce qui ne peut pas être fait.

C'est le seul cas où vous souhaitez utiliser Kotlin (il est préférable d'utiliser la bibliothèque qui peut utiliser à la fois Java et Deferred).

(Je n'ai même pas eu besoin d'utiliser Kotlin ...)

Comme @glayash l'a commenté, si vous répétez l'animation comme celle-ci, vous pouvez écrire comme suit, pour que cela ne devienne pas un enfer de rappel et que vous n'ayez même pas besoin d'utiliser Kotlin.

//Animation qui monte en 2 secondes
final TranslateAnimation anim1 = new TranslateAnimation(
        Animation.RELATIVE_TO_SELF,  0.0f,
        Animation.RELATIVE_TO_SELF,  0.0f,
        Animation.RELATIVE_TO_SELF,  0.0f,
        Animation.RELATIVE_TO_SELF, -0.1f);
anim1.setDuration(2000);
//Inverser et répéter indéfiniment
anim1.setRepeatMode(Animation.REVERSE);
anim1.setRepeatCount(Animation.INFINITE);
view.startAnimation(anim1);

Si c'est Kotlin ...

C'est pourquoi je l'ai essayé avec Kotlin.

Tout d'abord, créez une "fonction qui exécute l'animation et passe à la suivante lorsque l'animation est terminée". Ici, je l'ai défini comme une fonction d'extension de View.

package net.amay077.animsample

import android.view.View
import android.view.animation.Animation
import kotlin.coroutines.experimental.suspendCoroutine

suspend fun View.startAnimationAsync(anim: Animation) {

    return suspendCoroutine { continuation ->
        anim.setAnimationListener(object : Animation.AnimationListener {
            override fun onAnimationStart(animation: Animation?) { }

            override fun onAnimationEnd(animation: Animation?) {
                continuation.resume(Unit)
            }

            override fun onAnimationRepeat(animation: Animation?) { }
        })

        this.startAnimation(anim)
    }
}

L'appelant ressemble à ceci: C'est le paradis par rapport à Java dans Callback Hell ... Il semble que vous deviez utiliser launch (UI) {} au lieu de ```async () {} `` car l'animation doit être appelée depuis le thread de l'interface utilisateur.

val button1 = findViewById(R.id.button1)

val anim1 = TranslateAnimation(
        Animation.RELATIVE_TO_SELF, 0.0f,
        Animation.RELATIVE_TO_SELF, 0.0f,
        Animation.RELATIVE_TO_SELF, 0.0f,
        Animation.RELATIVE_TO_SELF, -0.5f)
anim1.duration = 2000

val anim2 = TranslateAnimation(
        Animation.RELATIVE_TO_SELF, 0.0f,
        Animation.RELATIVE_TO_SELF, 0.0f,
        Animation.RELATIVE_TO_SELF, -0.5f,
        Animation.RELATIVE_TO_SELF, 0.0f)
anim2.duration = 2000

launch(UI) { //Je vais m'asynchroniser depuis le fil principal
    //Répéter tout le temps
    while (true) {
        button1.startAnimationAsync(anim1) // 1.Lancer une animation qui monte sur 2 secondes
        button1.startAnimationAsync(anim2) // 2.Exécutez une animation qui descend sur 2 secondes
    }
}

C'est la première fois que j'utilise Kotlin correctement, alors je peux peut-être encore l'améliorer. .. Veuillez indiquer tout bon code.

Pour l'implémentation à Kotlin, je me suis référé au site suivant

Au fait, vous pouvez également le faire avec C

C # (c'est-à-dire Xamarin.Android) peut également être obtenu avec une combinaison de «async / await (ie Task)» et «TaskCompletionSource».

C # a également une méthode d'extension qui peut être définie comme:

public static class ViewAnimationExtensions
{
    public static Task<bool> StartAnimationAsync(this View view, Animation anim)
    {
        var source = new TaskCompletionSource<bool>();
        EventHandler<Animation.AnimationEndEventArgs> handler = null;

        handler = (sender, e) =>
        {
            anim.AnimationEnd -= handler; //N'oubliez pas de vous désinscrire
            source.SetResult(true); //suite kotlin.resume(Unit)Toko
        };
        anim.AnimationEnd += handler; //Abonnez-vous à l'événement

        view.StartAnimation(anim);
        return source.Task;
    }
}

C'est le côté de départ. Ajoutez le mot-clé ʻawait`` au moment de l'appel, et ajoutez le mot-clé ʻasync à la méthode qui le contient (ici` ʻOnCreate).

protected async override void OnCreate(Bundle savedInstanceState)
{
    /*réduction*/

    while (true)
    {
        await button1.StartAnimationAsync(anim1);
        await button1.StartAnimationAsync(anim2);
    }
}

Ce serait bien si Kotlin pouvait être mélangé avec Java dans le même projet.

Recommended Posts

Une histoire qui a rendu pratique avec Kotlin qu'il est difficile d'exécuter une animation en continu sur Android
Trouvez une valeur pratique pour avoir une méthode et en faire un ValueObject
[Docker] Est-il suffisant de l'appeler une construction en plusieurs étapes? → L'histoire qui est devenue si bonne
J'ai créé un plug-in qui exécute jextract avec des tâches Gradle
Une histoire qui a rendu aussi facile que possible la vérification de l'opération lors de la création automatique d'une mise à jour de bibliothèque PR avec Github Dependabot
Une histoire que j'ai eu du mal à défier le pro de la concurrence avec Java
L'histoire de rendre possible la construction d'un projet qui a été construit par Maven avec Ant
Lors de l'appel de sshpass depuis Java avec shell etc., il semble qu'il soit nécessaire d'avoir un chemin.
Création d'une bibliothèque qui facilite la gestion des préférences partagées Android
J'ai créé un site qui résume les informations sur la restriction du sucre avec Vue.js
J'ai créé une application Janken avec kotlin
J'ai créé une application de calculatrice sur Android
J'ai créé une application Janken avec Android
Que faire quand est invalide car il ne commence pas par un "-"