Tutorial do Jetpack Compose: replicando o aplicativo de áudio Dribbble, parte 2
Implementando uma IU totalmente personalizada com animações complexas: Action Panel
Na Exyte , tentamos contribuir com o código aberto o máximo possível, desde o lançamento de bibliotecas e componentes até a escrita de artigos e tutoriais. Um tipo de tutorial que fazemos é a replicação — pegando um componente de interface do usuário complexo, implementando-o usando novas estruturas e escrevendo um tutorial ao lado. Começamos com o SwiftUI há alguns anos, mas desta vez finalmente incursionamos no Android, usando a estrutura declarativa de interface do usuário do Google: Jetpack Compose.
Este é o segundo artigo da série Dribbble Replicating. A postagem anterior demonstrou a implementação da barra de áudio animada de largura dinâmica . Este se concentrará na replicação do Painel de Ação.
Quebrando tudo
Considere como o painel foi projetado. Vejamos este painel com animações em câmera lenta:
Em cada item da lista de comentários, temos dois elementos:
CommentListItemque exibe as informações do comentário.ActionPanel, um pequeno painel com botões de ação de comentário dentro. Ele rolaCommentListItemquando o usuário clica no comentário.
BoxWithConstraints(
modifier = modifier
.fillMaxWidth()
.background(color = MaterialTheme.colors.surface),
contentAlignment = Alignment.Center,
) {
val density = LocalDensity.current
val maxWidth = with(density) { constraints.maxWidth.toDp() }
val maxHeight = commentItemHeight - 16.dp
CommentListItem(
avatarId = comment.avatarId,
name = comment.name,
text = comment.text,
date = comment.date,
onClick = { onClick(comment) },
)
ActionPanel(
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(horizontal = 16.dp),
maxWidth = maxWidth,
maxHeight = maxHeight,
isVisible = isActionsVisible,
onActionClick = onActionClick
)
}
Quando targetStatemuda, a transição executará todas as suas animações filhas em direção aos valores de destino especificados para o novo targetState. Caso você não tenha trabalhado com animações antes, observe que labelé necessário que as ferramentas do Android Studio diferenciem as animações.
Vamos considerar a animação em expansão. Usamos transitionSpecpara definir a animação e especificar animações para cada par de initialStatee targetState. O keyframesparâmetro especifica as fases da animação com base nos valores dos instantâneos definidos em diferentes pontos no tempo durante a animação. A qualquer momento, o valor da animação será interpolado entre dois valores de quadro-chave. Neste caso, a animação de altura começa com uma certa velocidade para CORNER_DURATION, então continua em uma velocidade diferente para EXPAND_DURATION. Para informações detalhadas, você pode consultar a documentação oficial
val stateTransition = updateTransition(targetState = isVisible, label = "Visibility")
val targetWidth = stateTransition.animateDp(
transitionSpec = {
keyframes {
durationMillis = CORNER_DURATION + EXPAND_DURATION
if (targetState) {
maxHeight at CORNER_DURATION
maxWidth at CORNER_DURATION + EXPAND_DURATION
} else {
maxHeight at EXPAND_DURATION
MIN_RADIUS.dp at EXPAND_DURATION + CORNER_DURATION
}
}
},
label = "Width"
) { value -> if (value) maxWidth else MIN_RADIUS.dp }
val targetHeight by stateTransition.animateDp(
transitionSpec = {
keyframes {
durationMillis = CORNER_DURATION + EXPAND_DURATION
if (targetState) {
maxHeight at CORNER_DURATION
} else {
maxHeight at EXPAND_DURATION
MIN_RADIUS.dp at EXPAND_DURATION + CORNER_DURATION
}
}
},
label = "Height"
) { value -> if (value) maxHeight else MIN_RADIUS.dp }
val contentAlphaProvider = remember {
{ (targetWidth.value - MIN_RADIUS.dp) / (maxWidth - MIN_RADIUS.dp) }
}





































![O que é uma lista vinculada, afinal? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)