Finalmente, chegou o momento em que você não precisa criar o Android Studio para experimentar a nova estrutura declarativa da interface do usuário para o Android. O Jetpack Compose agora está disponível como a primeira visualização do desenvolvedor no repositório Maven do Google. Com esta notícia, minha segunda-feira de manhã começou. E imediatamente houve um desejo de ver que conjunto de ferramentas eles estavam esperando.

Decidi começar meu conhecido imediatamente com uma tentativa de introduzi-lo no projeto de estimação publicado no Google Play. Além disso, durante muito tempo eu quis fazer uma página “Sobre o aplicativo”. Neste artigo, falarei sobre os principais componentes e etapas da conexão do Compose:
- Conexão de dependência
- Temas e estilos. Integração com os existentes no projeto.
- Testes de acessibilidade e interface do usuário.
- Os principais componentes e análogos dos herdeiros da View.
- Trabalhe com o Estado.
Conexão de dependência
Para começar, atualizei o estúdio de 3.5 para 3.5.1 (em vão), adicionei dependências básicas. Uma lista completa pode ser vista em um artigo de Cyril .
// build.gradle ext.compose_version= '0.1.0-dev01' //build.gradle dependencies{ ... implementation "androidx.compose:compose-runtime:$compose_version" kapt "androidx.compose:compose-compiler:$compose_version" implementation "androidx.ui:ui-layout:$compose_version" implementation "androidx.ui:ui-android-text:$compose_version" implementation "androidx.ui:ui-text:$compose_version" implementation "androidx.ui:ui-material:$compose_version" }
E então eu tentei coletar tudo isso por causa das versões dispersas do Firebase. Após o que eu me deparei com os obstáculos de composição:
app/src/main/AndroidManifest.xml Error: uses-sdk:minSdkVersion 16 cannot be smaller than version 21 declared in library [androidx.ui:ui-layout:0.1.0-dev01] .../ui-layout-0.1.0-dev01/AndroidManifest.xml as the library might be using APIs not available in 16 Suggestion: use a compatible library with a minSdk of at most 16, or increase this project's minSdk version to at least 21, or use tools:overrideLibrary="androidx.ui.layout" to force usage (may lead to runtime failures)
Sim, o Compose estava disponível apenas com o minSdk 21 (Lolipop). Talvez essa seja uma medida temporária, mas esperava-se que ela suportasse versões anteriores do sistema operacional.
Mas isso não é tudo. O Redigir funciona no Reflection, em vez do Plugin do compilador Kotlin, como indicado anteriormente, por exemplo, aqui . Portanto, para que tudo comece, você precisa adicionar o Kotlin Reflect também dependendo de:
implementation "org.jetbrains.kotlin:kotlin-reflect"
Bem, para a sobremesa. Compor O dp implementa funções de extensão para Int, Long, Float, marcadas com a palavra-chave inline. Isso pode causar um novo erro de compilação:
Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6. Please specify proper '-jvm-target' option * https://stackoverflow.com/questions/48988778/cannot-inline-bytecode-built-with-jvm-target-1-8-into-bytecode-that-is-being-bui
Para resolver, é necessário registrar explicitamente a versão da JVM para o Kotlin:
android { … kotlinOptions { jvmTarget = "1.8" } }
Isso parece ser tudo. Muito mais fácil do que construir seu próprio estúdio)
Vamos tentar executar o Hello World (também do artigo de Cyril, mas, ao contrário dele, adicione Compose dentro do Fragment). O layout do fragmento é um FrameLayout vazio.
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val fragmentView = inflater.inflate(R.layout.fragment_about, container, false) (fragmentView as ViewGroup).setContent { Hello("Jetpack Compose") } return fragmentView } @Composable fun Hello(name: String) = MaterialTheme { FlexColumn { inflexible {
Começamos, a seguinte tela é exibida:
Devido ao fato de o Composable usar o tema Material padrão, temos uma AppBar roxa. Bem, e, como esperado, não é de todo consistente com o tema sombrio do aplicativo:
Vamos tentar resolver isso.
Temas e estilos. Integração com os existentes no projeto.
Para usar estilos existentes no Composable, passamos-os para o construtor MaterialTheme:
@Composable fun Hello(name: String) = MaterialTheme(colors = MaterialColors( primary = resolveColor(context, R.attr.colorPrimary, MaterialColors().primary), secondary = resolveColor(context, R.attr.colorSecondary, MaterialColors().secondary), onBackground = resolveColor(context, R.attr.textColor, MaterialColors().onBackground) )){...}
O MaterialTheme em si consiste em duas partes: MaterialColors e MaterialTypography.
Para resolver as cores, usei um invólucro sobre os estilos:
private fun resolveColor(context: Context?, @AttrRes attrRes: Int, colorDefault: Color) = context?.let { Color(resolveThemeAttr(it, attrRes).data.toLong()) } ?: colorDefault private fun resolveThemeAttr(context: Context, @AttrRes attrRes: Int): TypedValue { val theme = context.theme val typedValue = TypedValue() theme.resolveAttribute(attrRes, typedValue, true) return typedValue }
Nesta fase, o AppBar ficará verde. Mas para repintar o texto, você precisa executar mais uma ação:
Text("Hello $name!", style = TextStyle(color = +themeColor { onBackground }))
O tema é aplicado ao widget usando a operação unary plus. Ainda o veremos ao trabalhar com o Estado.
Agora, a nova tela parece uniforme com o restante do aplicativo nas duas variantes do tema:
O Compose também encontrou o arquivo DarkTheme.kt nas fontes, cujas funções podem ser usadas para determinar vários gatilhos para ativar um tema sombrio no Android P e 10.
Testes de acessibilidade e interface do usuário.
Até que a tela comece a crescer com novos elementos, vamos ver como fica no Inspetor de Layout e com a exibição das bordas dos elementos no Modo de Desenvolvimento ativada:

Aqui veremos o FrameLayout, dentro do qual apenas o AndroidComposeView. As ferramentas existentes para testes de acessibilidade e interface do usuário não são mais aplicáveis? Talvez, em vez deles, agora exista uma nova biblioteca: androidx.ui:ui-test
.
Os principais componentes e análogos dos herdeiros da View.
Agora vamos tentar tornar a tela um pouco mais informativa. Primeiro, altere o texto, adicione um botão que leva à página do aplicativo no Google Play e uma imagem com um logotipo. Vou mostrar o código imediatamente e o que aconteceu:
@Composable fun AboutScreen() = MaterialTheme(...) { FlexColumn { inflexible { TopAppBar<MenuItem>(title = { Text(getString(R.string.about)) }) } expanded(1F) { VerticalScroller { Column { Image() Title() MyButton() } } } } } private fun Image() { Center { Padding(16.dp) { Container( constraints = DpConstraints( minWidth = 96.dp, minHeight = 96.dp ) ) { imageFromResource(resources, R.drawable.ic_launcher) } } } } private fun Title() { Center { Padding(16.dp) { Text(getString(R.string.app_name) + " " + BuildConfig.VERSION_NAME, style = TextStyle(color = +themeColor { onBackground })) } } } private fun MyButton() { Center { Padding(16.dp) { Button(getString(R.string.about_button), onClick = { openAppInPlayStore() }) } } }
Os princípios básicos da composição do widget não foram alterados desde a primeira aparição das fontes Compose .
Do interessante:
Agora, vamos examinar o principal grupo de visualizações existente e tentar encontrar análogos no Compose.
Em vez de FrameLayout, você pode usar Stack. Tudo é simples aqui: os widgets filhos se sobrepõem e são posicionados dependendo da função usada para o anexo: alinhados, posicionados ou expandidos.
LinearLayout é substituído imediatamente por dois widgets: Coluna e Linha em vez de usar o parâmetro android: guidance. Eles, por sua vez, contêm FlexColumn e FlexRow com uma camada de função inflexível sobre uma subárvore aninhada. Bem, o FlexColumn e o FlexRow são criados no Flex com a orientation = LayoutOrientation.Vertical
do parâmetro orientation = LayoutOrientation.Vertical
ou Horizontal
.
Uma hierarquia semelhante para os widgets FlowColumn, FlowRow e Flow. A principal diferença: se o conteúdo não couber em uma coluna ou linha, a próxima será desenhada a seguir e os widgets incorporados "fluirão" para lá. É difícil para mim imaginar o real objetivo desses widgets.
O efeito ScrollView é conseguido colocando uma coluna ou linha dentro de um VerticalScroller ou HorizontalScroller. Os dois compõem dentro do Scroller, passando o parâmetro isVertical = true
ou false
.
Em busca de um análogo para ConstraintLayout, ou pelo menos RelativeLayout, deparei com um novo widget de Tabela. Tentei executar o código de exemplo no meu aplicativo: DataTableSamples.kt . Mas, como não tentei simplificar o exemplo, não funcionou para fazê-lo funcionar.
Trabalhar com o Estado
Uma das inovações mais esperadas da estrutura é sua prontidão para uso em arquiteturas unidirecionais construídas com base em um único estado. E isso deveria introduzir a anotação @Model para marcar classes que fornecem Estado para renderizar a interface do usuário.
Considere um exemplo:
data class DialogVisibleModel(val visible: Boolean, val dismissPushed: Boolean = false) ... @Composable fun SideBySideAlertDialogSample() { val openDialog = +state { DialogVisibleModel(true) } Button(text = "Ok", onClick = { openDialog.value = DialogVisibleModel(true) }) if (openDialog.value.visible) { AlertDialog( onCloseRequest = {
Isso cria uma classe de dados para o modelo de estado e não precisa ser marcada com a anotação @Model.
O próprio estado inicial é criado dentro da função @Composable usando o estado +.
A visibilidade da caixa de diálogo é determinada pela propriedade visible do modelo obtida chamando a propriedade value.
Essa propriedade também pode ser configurada para um novo objeto imutável, como acontece no onClick dos dois botões. O primeiro se esconde, o segundo - fecha o diálogo. A caixa de diálogo pode ser reaberta clicando no botão Ok, definido dentro da mesma função @Composable.
Ao tentar estabelecer um estado fora desta função, ocorre um erro:
java.lang.IllegalStateException: Composition requires an active composition context.
O contexto pode ser obtido atribuindo o valor da função setContent {} no onCreateView, mas ainda não é claro como usá-lo, por exemplo, no Presenter ou em outra classe que não seja Fragmento ou Atividade, para alterar o estado.

Isso conclui nossa análise da nova biblioteca Jetpack Compose. A estrutura justifica seu nome arquitetonicamente, substituindo toda a herança, que era tão inconveniente na hierarquia Exibir, pela composição. Ainda há muitas perguntas sobre como os análogos de ViewGroups mais complexos serão implementados, como ConstraintLayout e RecyclerView; documentação e visualizações insuficientes.
É absolutamente claro que o Compose não está pronto para uso, mesmo em pequenas aplicações de combate.
Mas esta é apenas a primeira versão do Dev Preview. Será interessante observar o desenvolvimento do conceito de trabalhar com o Estado e as bibliotecas da comunidade com base no Compose.
Se você encontrar exemplos mais bem-sucedidos de código ou documentação para casos que não consegui, escreva nos comentários.