O Android Jetpack compõe a primeira impressão

Depois de assistir a uma palestra sobre o Android Jetpack Compose no Google IO 2019, eu quis experimentar imediatamente. Além disso, a abordagem implementada lembrou muito a Flutter, da qual eu estava interessado anteriormente .



A própria biblioteca de composição está no estágio pré-alfa; portanto, não existem muitas documentações e artigos sobre ela. Em seguida, contarei com vários recursos que consegui encontrar, além da biblioteca de código aberto .


Esses recursos são:



O que é o Android Jetpack Compose?


Anteriormente, toda a interface do usuário do Android era baseada na classe View. Este tem sido o caso desde os primeiros dias do Android. E nesse sentido, muitas falhas herdadas e arquitetônicas se acumularam, o que poderia ser aprimorado. Mas fazer isso é bastante difícil sem quebrar todo o código escrito em sua base.


Nos últimos anos, muitos novos conceitos apareceram no mundo dos aplicativos clientes (incluindo as tendências do Frontend); portanto, a equipe do Google seguiu um caminho radical e reescreveu todo o nível da interface do usuário no Android. Assim, apareceu a biblioteca Android Jetpack Compose, que inclui truques conceituais de React, Litho, Vue, Flutter e muitos outros.


Vamos examinar alguns dos recursos da interface do usuário existente e compará-lo com o Redigir.


1. Independência das versões do Android


A interface do usuário existente está intimamente relacionada à plataforma. Quando os primeiros componentes do Design de materiais apareceram, eles funcionavam apenas com o Android 5 (API21) e superior. Para trabalhar em versões mais antigas do sistema, você deve usar a Biblioteca de Suporte.


O Compose faz parte do Jetpack, que o torna independente das versões do sistema e é possível usá-lo mesmo em versões mais antigas do Android (pelo menos com a API21).


2. Toda a API do Kotlin


Anteriormente, era necessário lidar com arquivos diferentes para criar uma interface do usuário. Descrevemos a marcação em xml e, em seguida, usamos o código Java / Kotlin para fazê-lo funcionar. Depois voltamos a outros arquivos xml para definir temas, animação, navegação, ... E até tentamos escrever código em xml (Data Binding).


O uso do Kotlin permite que você escreva UIs de estilo declarativo diretamente no código, em vez de xml.


3. Composable = Composite: usando composição em vez de herança


Criar elementos de interface do usuário personalizados pode ser bastante complicado. Precisamos herdar do View ou de seus descendentes e cuidar de muitas propriedades importantes antes de iniciar corretamente. Por exemplo, a classe TextView contém cerca de 30 mil linhas de código Java. Isso se deve ao fato de que ela contém muita lógica desnecessária que é herdada por elementos descendentes.


A composição surgiu por outro lado, substituindo herança por composição.


Padding é o mais adequado para ilustrar o que é:


Na interface do usuário existente, para renderizar o TextView recuado em 30dp :


imagem

precisamos escrever o seguinte código:


 <TextView android:id="@+id/simpleTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/cyan" android:padding="30dp" <------------------------ NOTE THIS android:text="Drag or tap on the seek bar" /> 

Isso significa que em algum lugar dentro do TextView.java ou em suas superclasses existe uma lógica que sabe como contar e desenhar recuos.


Vamos ver como você pode fazer o mesmo no Compose:


 // note: the cyan background color is omitted for now to keep it simple Padding(30.dp) { Text("Drag or tap on the seek bar") } 

Alterações
TextView tornou apenas Text() . A propriedade android:padding se transformou em um Padding que envolve o Text .


Os benefícios
Assim, o Text é responsável apenas por renderizar o próprio texto. Ele não sabe contar travessões. Padding , por outro lado, é responsável apenas pelo preenchimento e nada mais. Pode ser usado em torno de qualquer outro elemento.


4. Fluxo de dados unidirecional


O fluxo de dados unidirecional é um conceito importante se falarmos, por exemplo, sobre o controle do estado de um CheckBox em um sistema de interface do usuário existente. Quando o usuário toca na CheckBox , seu estado fica checked = true : a classe atualiza o estado View e chama um retorno de chamada do código que monitora a alteração do estado.


Em seguida, no próprio código, por exemplo, no ViewModel , você precisa atualizar a variável de state correspondente. Agora você tem duas cópias do estado pressionado, o que pode criar problemas. Por exemplo, alterar o valor da variável de state dentro do ViewModel fará com que o CheckBox atualizado, o que pode terminar em um loop sem fim. Para evitar isso, teremos que criar algum tipo de muleta.


Usar o Compose ajudará a resolver esses problemas, pois ele se baseia no princípio da unidirecionalidade. A mudança de estado será processada dentro da estrutura: simplesmente fornecemos o modelo de dados para dentro. Além disso, o componente no Compose agora não altera seu estado por conta própria. Em vez disso, ele apenas chama de retorno de chamada e agora é tarefa do aplicativo alterar a interface do usuário.


5. Melhorando a depuração


Como toda a interface do usuário agora está escrita no Kotlin, você pode depurar a interface do usuário. Eu não tentei isso sozinho, mas no podcast eles disseram que o depurador e os pontos de interrupção funcionam no Compose.


Palavras suficientes, mostre o código


Eu sei, quero ver rapidamente como é a interface do usuário no código (spoiler: muito semelhante ao Flutter se você tentar escrever nele).


Começaremos criando algumas View simples e comparando a aparência delas na interface do usuário existente e em Compor.


1. FrameLayout vs Wrap + Padding + Background


Reutilizamos nosso exemplo acima e tentamos fazer esse TextView recuado a 30dp com um fundo turquesa:


`TextView` recuado em` 30dp` e fundo turquesa

UI existente:


 <TextView android:id="@+id/simpleTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/cyan" <-------------- NOTE THIS android:padding="30dp" <------------------------ AND THIS android:text="Drag or tap on the seek bar" /> 

Agora observe o código que faz o mesmo em Redigir:


 @Composable fun MyText() { Wrap { Padding(30.dp) { DrawRectangle(color = Color.Cyan) Text("Drag or tap on the seek bar") } } } 

Aqui estão algumas coisas novas. Como o Text sabe apenas sobre a renderização, ele não se importa com preenchimento e plano de fundo. Portanto, para adicioná-los, precisamos usar três funções separadas:


  • DrawRectangle plano de fundo
  • Padding
  • Wrap automática é uma função que sobrepõe parâmetros como FrameLayout .

Fácil. Mas é um pouco diferente do sistema de interface do usuário existente com o qual estamos acostumados.


2. LinearLayout vertical vs Column


Agora vamos tentar fazer algo equivalente ao nosso bom e velho LinearLayout .
Para colocar dois elementos um abaixo do outro, como na figura abaixo, podemos usar a Column :


Dois elementos um abaixo do outro

O código ficará assim:


 @Composable fun FormDemo() { Column(crossAxisAlignment = CrossAxisAlignment.Start) { Text("Click the button below: ") Button(text = "Next") } } 

Aninhado no elemento Column será localizado verticalmente um abaixo do outro.


2a Indentação


Você provavelmente notou que o texto e o botão estão muito próximos da borda. Portanto, adicione Padding .


 @Composable fun FormDemo() { Padding(10.dp) { //   Column(crossAxisAlignment = CrossAxisAlignment.Start) { Text("Click the button below: ") Button(text = "Next") } } } 

Parece melhor:


Dois elementos recuam um abaixo do outro

2b. Intervalos


Também podemos adicionar alguma indentação entre Text e Button :


 @Composable fun FormDemo() { Padding(10.dp) { Column(crossAxisAlignment = CrossAxisAlignment.Start) { Text("Click the button below: ") HeightSpacer(10.dp) //    Button(text = "Next") } } } 

Como é nossa tela agora:


Dois elementos, um abaixo do outro, recuado e espaçado

2c. Horizontal LinearLayout vs Row


Coloque o segundo botão ao lado do primeiro:


Adicionado um segundo botão

Código para isso:


 @Composable fun FormDemo() { Padding(10.dp) { Column(crossAxisAlignment = CrossAxisAlignment.Start) { Text("Click the button below: ") HeightSpacer(10.dp) Row { //   Button(text = "Back") //   WidthSpacer(10.dp) //    Button(text = "Next") } } } } 

Dentro da Row dois botões estarão na horizontal. WidthSpacer adiciona distância entre eles.


2d. Gravity vs Alignment


Alinhe nossos elementos no centro, como a gravity faz na interface atual. Para mostrar diff, vou comentar as linhas antigas e substituí-las por novas:


 @Composable fun FormDemo() { Padding(10.dp) { // Column(crossAxisAlignment = CrossAxisAlignment.Start) { Column(crossAxisAlignment = CrossAxisAlignment.Center) { //  Text("Click the button below: ") HeightSpacer(10.dp) // Row { Row(mainAxisSize = FlexSize.Min) { //    Button(text = "Back") WidthSpacer(10.dp) Button(text = "Next") } } } } 

Teremos sucesso:


Alinhamento central

Com crossAxisAlignment = CrossAxisAlignment.Center os elementos aninhados serão centralizados horizontalmente. Também devemos definir o parâmetro Row como mainAxisSize = FlexSize.Min , semelhante em comportamento a layout_width = wrap_content , para que não se estenda pela tela devido ao padrão mainAxisSize = FlexSize.Max , que se comporta como layout_width = match_parent .


2d. Observação


Pelo que vimos nos exemplos acima, você pode ver que todos os elementos são construídos compostos de funções separadas: o padding é uma função separada, o spacer é uma função separada, em vez de ser propriedades dentro de Text , Button ou Column .


Elementos mais complexos, como RecyclerView ou ConstraintLayout estão em desenvolvimento: portanto, não consegui encontrar um exemplo com eles nas fontes de demonstração.


3. Estilos e Temas


Você provavelmente notou que os botões acima são roxos por padrão. Isso ocorre porque eles usam estilos padrão. Vamos ver como os estilos funcionam no Compose.


Nos exemplos acima, FormDemo marcado com anotação @Composable . Agora vou mostrar como esse elemento é usado na Activity :


 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { CraneWrapper{ MaterialTheme { FormDemo() } } } } 

Em vez da função setContentView() , usamos setContent() , uma função de extensão da biblioteca Compose.kt .


CraneWrapper contém a árvore de composição e fornece acesso a Context , Density , FocusManager e TextInputService .


MaterialTheme permite personalizar o tema para elementos.


Por exemplo, posso alterar a cor principal do tema para marrom da seguinte maneira:


 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { CraneWrapper{ // MaterialTheme { MaterialTheme(colors = MaterialColors(primary = Color.Maroon)) { FormDemo() } } } } 

Agora nossa tela ficará assim:


Marrom como cor primária

Outras cores e fontes que podem ser alteradas: MaterialTheme.kt # 57


O Rally Activity fornece um bom exemplo de como personalizar um tópico: código fonte para RallyTheme.kt


O que ver / ler


Se você quiser mais, poderá montar o projeto de amostra de acordo com as instruções aqui .


Como os usuários do Windows escrevem, agora não há uma maneira oficial de iniciar o Compose, mas há um guia não oficial do kotlinlang Slack .


Perguntas sobre o Compose podem ser feitas aos desenvolvedores no canal #compose kotlinlang Slack.


Deixe outros links nos comentários - os mais úteis serão adicionados aqui.


Conclusões


O desenvolvimento desta biblioteca está em pleno andamento, portanto, quaisquer interfaces mostradas aqui estão sujeitas a alterações. Ainda há muitas coisas que você pode aprender no código-fonte, como @Model e fluxo de dados unidirecional (fluxo de dados unidirecional). Talvez este seja um tópico para artigos futuros.

Source: https://habr.com/ru/post/pt461101/


All Articles