Lluvia de estrellas en Android usando Android Property Animation

En este artículo, vamos a crear una lluvia de estrellas usando la animación de propiedades de Android. Crearemos una animación un poco más complicada, animando múltiples propiedades en múltiples objetos. Para este efecto, al hacer clic en un botón, se creará una estrella con un tamaño aleatorio, que se agregará al contenedor de fondo, justo fuera de la vista desde la parte superior de ese contenedor. La estrella procederá a caer hasta la parte inferior de la pantalla acelerando a medida que avanza. la estrella también girará cuando caiga. A continuación se proporciona un GIF de muestra para tener una idea de lo que vamos a hacer en este artículo. Tenga en cuenta que vamos a implementar este proyecto utilizando el lenguaje Kotlin

Star Shower in Android using Android Property Animation

Implementación paso a paso

Paso 1: Crear un nuevo proyecto

Para crear un nuevo proyecto en Android Studio, consulte Cómo crear/iniciar un nuevo proyecto en Android Studio . Tenga en cuenta que seleccione Kotlin como lenguaje de programación. 

Paso 2: necesitaremos algunas variables locales para mantener el estado.

  • una referencia al Starfield ViewGroup que es solo el padre de la vista de estrella actual.
  • el ancho y la altura de ese contenedor que se utilizará para calcular los valores finales de traslación de las estrellas fugaces.
  • el ancho y la altura predeterminados de la estrella que se cambiarán más tarde con un factor de escala para obtener estrellas de diferentes tamaños.

val container = star.parent como ViewGroup

val contenedorW = contenedor.ancho

val containerH = container.height

var estrellaW: Flotante = estrella.ancho.toFloat()

Cree una nueva Vista para contener la estrella, ya que es un activo VectorDrawable, use un AppCompatImageView , que tiene la capacidad de alojar ese tipo de recurso. Crea la estrella y agrégala al contenedor de fondo.

val newStar = AppCompatImageView(esto)

newStar.setImageResource(R.drawable.ic_star)

newStar.layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,

                    FrameLayout.LayoutParams.WRAP_CONTENT)

Paso 3: Dimensionar y posicionar la estrella

No hemos definido la posición de la imagen en el contenedor, por lo que está posicionada en (0, 0) por defecto. Lo arreglaremos en este paso.

(i) Establezca el tamaño de la estrella. Modifique la estrella para que tenga un tamaño aleatorio, de .1x a 1.6x de su tamaño predeterminado. Utilice este factor de escala para cambiar los valores de ancho/alto almacenados en caché.

newStar.scaleX = Math.random().toFloat() * 1.5f + .1f

nuevaEstrella.escalaY = nuevaEstrella.escalaX

estrellaW *= nuevaEstrella.escalaX

estrellaH *= nuevaEstrella.escalaY

Ahora ha almacenado en caché el píxel H/W de la estrella almacenado en starW y starH:

(ii) Ahora coloque la nueva estrella. Debería aparecer aleatoriamente en algún lugar entre el borde izquierdo y el borde derecho horizontalmente. El siguiente código usa el ancho de la estrella para ubicarla desde la mitad de la pantalla a la izquierda (-starW / 2) hasta la mitad de la pantalla a la derecha (con la estrella posicionada en (containerW – starW / 2). 

newStar.translationX = Math.random().toFloat() * containerW – starW / 2

Paso 4: Creación de animadores para rotación y caída de estrellas

Es hora de trabajar en la animación. La estrella debe girar a medida que cae hacia abajo. Podemos animar dos propiedades juntas. La rotación usará un movimiento lineal suave (moviéndose a una velocidad constante durante toda la animación de rotación), mientras que la animación de caída usará un movimiento acelerado (simulando la gravedad tirando de la estrella hacia abajo a una velocidad constantemente más rápida). Así que creará dos animadores y agregará un interpolador a cada uno.

(i) Primero, cree dos animadores, junto con sus interpoladores:

val mover = ObjectAnimator.ofFloat(newStar, View.TRANSLATION_Y, -starH, containerH + starH)

mover.interpolator = AccelerateInterpolator(1f) // provoca un suave movimiento de aceleración

val rotator = ObjectAnimator.ofFloat(newStar, View.ROTATION,

           (Math.random() * 1080).toFloat()) // la estrella rota una cantidad aleatoria entre 0 y 1080 grados 

rotator.interpolator = LinearInterpolator() //la rotación procederá a una velocidad constante a medida que cae la estrella

La animación del motor es responsable de hacer que la estrella «caiga». Anima la propiedad TRANSLATION_Y pero provoca un movimiento vertical en lugar de horizontal. El código se anima de -starH a (containerH + starH), lo que lo coloca justo fuera del contenedor en la parte superior y lo mueve hasta que está justo fuera del contenedor en la parte inferior, como se muestra aquí:

Paso 5: Ejecutar las animaciones en paralelo con AnimatorSet

Ahora es el momento de juntar estos dos animadores en un solo AnimatorSet . Es básicamente un grupo de animaciones, junto con instrucciones sobre cuándo ejecutar esas animaciones. Puede reproducir animaciones en paralelo.

(i) Cree el AnimatorSet y agréguele los animadores secundarios. El tiempo de animación predeterminado de 300 milisegundos es demasiado rápido para las estrellas fugaces, así que establezca la duración en un número aleatorio entre 500 y 2000 milisegundos, para que las estrellas puedan caer a diferentes velocidades.

conjunto de valores = AnimatorSet()

set.playTogether(motor, rotador)

set.duration = (Math.random() * 1500 + 500).toLong()

(ii) Una vez que newStar se haya caído por la parte inferior de la pantalla, debe retirarse del contenedor. Configure un oyente simple para que espere el final de la animación y elimínelo. Luego inicia la animación.

set.addListener(objeto: AnimatorListenerAdapter() {

 anula la diversión onAnimationEnd (animación: ¿Animador?) {

container.removeView(nuevaEstrella)

 }

})

seleccione arranque()

aplicación> res> diseño> actividad_principal.xml completar actividad_principal.xml

XML

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
  
    <Button
        android:id="@+id/showerButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="star shower"
        app:layout_constraintBottom_toTopOf="@+id/frameLayout"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
      
    <FrameLayout
        android:id="@+id/frameLayout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="@color/black"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/showerButton"
        app:layout_constraintVertical_bias="0.0">
  
        <ImageView
            android:id="@+id/star"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:contentDescription="star image"
            android:visibility="invisible"
            app:srcCompat="@drawable/ic_star"
            tools:ignore="VectorDrawableCompat" />
  
    </FrameLayout>
  
</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt completar MainActivity.kt

Kotlin

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator
import android.view.animation.LinearInterpolator
import android.widget.Button
import android.widget.FrameLayout
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatImageView
  
class MainActivity : AppCompatActivity() {
      
    lateinit var showerButton: Button
    lateinit var star: ImageView
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        showerButton = findViewById<Button>(R.id.showerButton)
        star = findViewById(R.id.star)
  
        showerButton.setOnClickListener {
            shower()
        }
    }
  
    private fun shower() {
        // Create a new star view in a random X position above the container.
        // Make it rotateButton about its center as it falls to the bottom.
  
        // Local variables
        val container = star.parent as ViewGroup
        val containerW = container.width
        val containerH = container.height
        var starW: Float = star.width.toFloat()
        var starH: Float = star.height.toFloat()
  
        // Create the new star (an ImageView in layout holding drawable star image) 
        // and add it to the container
        val newStar = AppCompatImageView(this)
        newStar.setImageResource(R.drawable.ic_star)
        newStar.layoutParams = FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.WRAP_CONTENT,
            FrameLayout.LayoutParams.WRAP_CONTENT
        )
        container.addView(newStar)
  
        // Scale the view randomly between 10-160% of its default size
        newStar.scaleX = Math.random().toFloat() * 1.5f + .1f
        newStar.scaleY = newStar.scaleX
        starW *= newStar.scaleX
        starH *= newStar.scaleY
  
        // Position the view at a random place between 
        // the left and right edges of the container
        newStar.translationX = Math.random().toFloat() * containerW - starW / 2
  
        // Create an animator that moves the view from a starting position right about the container
        // to an ending position right below the container. Set an accelerate interpolator to give
        // it a gravity/falling feel
        val mover = ObjectAnimator.ofFloat(newStar, View.TRANSLATION_Y, -starH, containerH + starH)
        mover.interpolator = AccelerateInterpolator(1f)
  
        // Create an animator to rotateButton the 
        // view around its center up to three times
        val rotator = ObjectAnimator.ofFloat(
            newStar, View.ROTATION,
            (Math.random() * 1080).toFloat()
        )
        rotator.interpolator = LinearInterpolator()
  
        // Use an AnimatorSet to play the falling and 
        // rotating animators in parallel for a duration
        // of a half-second to two seconds
        val set = AnimatorSet()
        set.playTogether(mover, rotator)
        set.duration = (Math.random() * 1500 + 500).toLong()
  
        // When the animation is done, remove the 
        // created view from the container
        set.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator?) {
                container.removeView(newStar)
            }
        })
  
        // Start the animation
        set.start()
    }
  
    private fun ObjectAnimator.disableViewDuringAnimation(view: View) {
  
        // This extension method listens for start/end 
        // events on an animation and disables
        // the given view for the entirety of that animation.
        addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationStart(animation: Animator?) {
                view.isEnabled = false
            }
  
            override fun onAnimationEnd(animation: Animator?) {
                view.isEnabled = true
            }
        })
    }
}

Ahora, ejecute su aplicación. Puede hacer clic en el botón » LLUVIA DE ESTRELLAS » varias veces, creando una nueva estrella y una nueva animación cada vez.

Producción:

Código fuente: Haga clic aquí

Publicación traducida automáticamente

Artículo escrito por anju1406 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *