Tutorial de Jetpack Compose: Replicar la aplicación Dribbble Audio Parte 1 | de Nik Afonasov | enero 2023

En Exyte tratamos de contribuir tanto como sea posible al código abierto, desde la publicación de bibliotecas y componentes hasta la redacción de artículos y tutoriales. Un tipo de tutoriales que hacemos es la replicación: tomamos un componente de interfaz de usuario complejo, lo implementamos con nuevos marcos y escribimos un tutorial junto con él. Comenzamos con SwiftUI hace algunos años, pero hoy finalmente nos estamos mudando a Android usando el marco de trabajo de interfaz de usuario declarativo de Google: Jetpack Compose.Concepto de diseño del talentoso Taras Migulko @dribbbleNuestra aplicación consta de 3 pantallas con transiciones animadas entre ellas:Para facilitar la lectura, la replicación de estas pantallas se analiza en varios artículos que cubren los siguientes elementos y animaciones de la interfaz de usuario:

  • Forma de onda de la primera pantalla
  • Panel de acciones de la segunda pantalla
  • Encabezado colapsado desde la tercera pantalla
  • Transición de gesto de arrastre
  • Transición de elemento común

Comenzaremos la serie animando la forma de onda. Esto es lo que queremos implementar:Todo el elemento consta de barras de volumen verticales animadas, espaciadas a intervalos regulares. Veamos las dimensiones básicas que vamos a utilizar:Como saben, el mundo de los dispositivos Android es rico en pantallas de diferentes tamaños, y esto plantea la siguiente pregunta: ¿cómo mostrar la forma de onda en las pantallas de diferentes dispositivos para que ocupe todo el espacio disponible? Para que este widget ocupe todo el ancho de la pantalla, puede aumentar proporcionalmente el ancho de las líneas o aumentar el número de líneas. Elegimos la segunda forma de preservar la nitidez de la imagen. En este caso, el primer paso es calcular el número requerido de filas. Para hacer esto, necesitamos dividir el ancho de lienzo disponible por el tamaño de una sola barra de volumen (ancho de barra + ancho de espacio). Además, necesitamos usar toInt() en lugar de roundToInt() porque solo queremos usar elementos que coincidan completamente con el tamaño, independientemente del redondeo.val count = (canvasWidth / (barWidthFloat + gapWidthFloat)).toInt().coerceAtMost( MaxLinesCount ) A continuación, calcule startOffset:val animationvolumewidth = número * (barWidthFloat + gapWidthFloat)var startOffset = (canvasWidth – animationvolumewidth) / 2Para mostrar la falta de salida de audio, reducimos la amplitud de las fluctuaciones de altura. Definamos los valores máximos y mínimos para las alturas de las barras: val barMinHeight = 0fval barMaxHeight = canvasHeight / 2f / heightDividerEl parámetro heightDivider difiere según el estado del audio: inactivo o reproduciendo. Para una transición suave entre estos estados, podemos usar: val heightDivider by animateFloatAsState(targetValue = if (isAnimating) 1f else 6f,animationSpec = tween(1000, easing = LinearEasing))Para animaciones infinitas, usamos RememberInfiniteTransition(), donde animations es la lista de elementos que se animarán y random es una propiedad que nos ayuda a cambiar el tiempo de animación aleatoriamente >()val random = Remember { Random(System.currentTimeMillis( )) }Ahora veamos la animación. Animar todas las barras ejercería una gran presión sobre la duración de la batería de un teléfono. Por lo tanto, para ahorrar recursos, solo usamos una pequeña cantidad de valores de animación flotantes: repeat(15) {val durationMillis = random.nextInt(500, 2000)animations + = infiniteAnimation.animateFloat(initialValue = 0f,targetValue = 1f,animationSpec = infiniteRepeatable(animation = tween(durationMillis),repeatMode = RepeatMode.Reverse,))} Para evitar que la animación se repita cada 15 líneas, puede configurar initialMultipliers.val al azar initialMultipliers = Remember {mutableListOf().apply {repeat(MaxLinesCount) { this += random.nextFloat() }}}Ahora realizamos las siguientes operaciones para cada línea: 1) Obtener valores de altura aleatorios, en nuestro caso se repiten cada 15 veces. 2) Agregue initialMultipliers a currentSize para reducir la posibilidad de repetir valores: 3) Use la interpolación lineal para cambiar el tamaño de la altura sin problemas: // 1val currentSize = animaciones[index % animations.size].value// 2var barHeightPercent = multiplicadores iniciales[index] + currentSizeif (barHeightPercent > 1.0f) {val diff = barHeightPercent – 1.0fbarHeightPercent = 1.0f – diff}// 3val barHeight = lerpF(barMinHeight, barMaxHeight, barHeightPercent) Una vez que tenga las dimensiones, puede dibujar una barra de volumen (1) y calcule el desplazamiento para la siguiente barra de volumen (2). // 1 dibuje bardrawLine(color = barColor,start = Offset(startOffset, canvasCenterY – barHeight / 2),end = Offset(startOffset, canvasCenterY + barHeight / 2), StrokeWidth = barWidthFloat,cap = StrokeCap.Round,)// 2 Calcule el desplazamiento para el siguiente barstartOffset += barWidthFloat + gapWidthFloatPara una mejor comprensión, aquí está todo el código de dibujo combinado ejecutándose en el ciclo: repeat(count) { index ->val currentSize = animaciones[index % animations.size].valuevar barHeightPercent = multiplicadores iniciales[index] + currentSizeif (barHeightPercent > 1.0f) {val diff = barHeightPercent – 1.0fbarHeightPercent = 1.0f – diff}val barHeight = lerpF(barMinHeight, barMaxHeight, barHeightPercent)drawLine(color = barColor,start = Offset(startOffset, canvasCenterY – barHeight / 2 ),end = Offset(startOffset, canvasCenterY + barHeight / 2),strokeWidth = barWidthFloat,cap = StrokeCap.Round,)startOffset += barWidthFloat + gapWidthFloat} Esto completa la implementación de la forma de onda animada de ancho variable. La siguiente parte de la serie demostrará la implementación del panel de acción. ¡Hasta luego!

Deja una respuesta

Tu dirección de correo electrónico no será publicada.