Criando aplicativos para Android Wear OS com Jetpack Compose

Um guia prático sobre como desenvolver aplicativos para Android Wear OS utilizando o moderno toolkit Jetpack Compose.

EN
PT

Jetpack Compose no Wear OS

Seleção de template no Android Studio

A Google anunciou na Google I/O o beta do Jetpack Compose para o Wear OS, versão do Android para Smartwatch. O Compose permite criar interfaces rapidamente usando sintaxe declarativa, similar ao SwiftUI do iOS. Este artigo demonstra criar um aplicativo simples em poucos minutos usando tecnologia moderna para desenvolvimento Android.

O Aplicativo

O aplicativo controla a quantidade de água consumida durante o dia. Requer a versão mais recente do Android Studio (neste caso, Android Studio Electric Eel 2022.1.1 Canary 5).

Iniciando um novo projeto

Template Wear OS Empty Compose Activity

Com Android Studio aberto, crie um novo projeto. Na lista de templates, selecione "Wear OS >> Empty Compose Activity" e continue.

Tela de configuração do projeto

Configure o app com nome, ID, localização do projeto e versão mínima do Android Wear OS (API Level 30 para este exemplo).

Customização de Cores

Estrutura do arquivo MainActivity.kt

A customização no Compose ocorre através de arquivos Kotlin em /theme, não via XML. Para trocar cores, modifique as referências de variáveis como primary:

val Blue700 = Color(0xFF1976d2)
val Blue900 = Color(0xFF0d47a1)
val DeepPurple200 = Color(0xFFb39ddb)
val DeepPurple400 = Color(0xFF512da8)

internal val wearColorPalette: Colors = Colors(
    primary = Blue700,
    primaryVariant = Blue900,
    secondary = DeepPurple200,
    secondaryVariant = DeepPurple400,
    error = Color.Red,
    onPrimary = Color.Black,
    onSecondary = Color.Black,
    onError = Color.Black
)

Nota: A função Color representa cores em ARGB (exemplo: vermelho é Color(0xFFFF0000)).

Criando a Tela Principal

Tela padrão do app Wear OS

A tela principal reside em MainActivity.kt. O Scaffold é um componente que fornece estrutura base, mostrando elementos do sistema como relógio no topo:

@OptIn(ExperimentalWearMaterialApi::class)
@Composable
fun WearApp() {
    WaterWearOSTheme {
        Scaffold(
            modifier = Modifier
                .fillMaxSize()
                .background(MaterialTheme.colors.background),
            timeText = {
                TimeText()
            },
        ) {}
    }
}

O layout usa:

  • WaterWearOSTheme { UI } - aplica tema aos elementos internos
  • Scaffold(Props) { UI } - estrutura base de layout
  • TimeText() - relógio padrão no topo

Os modificadores (Modifier) customizam elementos. .fillMaxSize() ocupa máximo espaço; .background() define cor de fundo.

Diagrama do layout Scaffold

Scaffold com TimeText implementado

Barra de Progresso Circular

Estado inicial do CircularProgressIndicator

Importe a nova função de Compose no corpo do Scaffold:

@OptIn(ExperimentalWearMaterialApi::class)
@Composable
fun WearApp() {
    WaterWearOSTheme {
        Scaffold(
            modifier = Modifier
                .fillMaxSize()
                .background(MaterialTheme.colors.background),
            timeText = {
                TimeText()
            },
        ) {
            ProgressIndicatorWater()
        }
    }
}

@Composable
fun ProgressIndicatorWater() {}

Use CircularProgressIndicator para criar a barra. As propriedades principais são:

  • startAngle - ângulo inicial
  • endAngle - ângulo final
  • progress - valor entre 0.0f (0%) e 1.0f (100%)

Diagrama de posicionamento de ângulos

Os ângulos seguem disposição circular padrão. Configure startAngle = 295f, endAngle = 245f para posicionar a barra acima do texto da hora.

@Composable
fun ProgressIndicatorWater() {
    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        CircularProgressIndicator(
            startAngle = 295f,
            endAngle = 245f,
            progress = 0.5f,
            strokeWidth = 5.dp,
            modifier = Modifier
                .fillMaxSize()
                .padding(all = 10.dp)
        )
        InfoWater()
    }
}

@Composable
fun InfoWater() {}

Barra de progresso com ângulos ajustados

Adicionando Informações e Ações

Dentro de InfoWater, use Column para alinhar elementos verticalmente:

@Composable
fun InfoWater() {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Text(
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 30.dp),
            textAlign = TextAlign.Center,
            color = MaterialTheme.colors.primary,
            text = "Você já bebeu 1 litro de água hoje"
        )

        Button(
            modifier = Modifier.padding(top = 5.dp),
            onClick = {},
        ) {
            Icon(
                painter = painterResource(id = R.drawable.cup_water),
                contentDescription = "Cup Water",
                modifier = Modifier
                    .size(ButtonDefaults.DefaultButtonSize)
                    .wrapContentSize(align = Alignment.Center),
            )
        }
    }
}

Os três elementos de layout principais são:

Diagrama dos tipos de container de layout

  • Box - sobrepõe elementos
  • Column - alinha verticalmente
  • Row - alinha horizontalmente

Hierarquia da estrutura de UI

Para importar ícones customizados, baixe SVGs do Material Design Icons Community e use "New > Vector Asset" na pasta res.

Implementação do Column layout

Lógica de Estado

Menu de importação de recurso Vector Asset

Dialog de importação SVG

Crie estado para rastrear litros consumidos:

private val count: MutableState<Float> = mutableStateOf(0f)

Modifique a barra de progresso para considerar a recomendação de 3 litros diários:

@Composable
fun ProgressIndicatorWater() {
    val recomedByDay = 3.0f
    val progressOfDay: Float = count.value / recomedByDay

    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        CircularProgressIndicator(
            startAngle = 295f,
            endAngle = 245f,
            progress = progressOfDay,
            strokeWidth = 5.dp,
            modifier = Modifier
                .fillMaxSize()
                .padding(all = 10.dp)
        )
        InfoWater()
    }
}

Atualize texto e botão para refletir o estado:

@Composable
fun InfoWater() {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Text(
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 30.dp),
            textAlign = TextAlign.Center,
            color = MaterialTheme.colors.primary,
            text = "Você já bebeu ${count.value} litro de água hoje"
        )
        Button(
            modifier = Modifier.padding(top = 5.dp),
            onClick = { count.value += 0.5f },
        ) {
            Icon(
                painter = painterResource(id = R.drawable.cup_water),
                contentDescription = "airplane",
                modifier = Modifier
                    .size(ButtonDefaults.DefaultButtonSize)
                    .wrapContentSize(align = Alignment.Center),
            )
        }
    }
}

Cada clique no botão adiciona 0.5 litros.

Próximos Passos

Demo final do app Wear OS

Resultado final do app

O desenvolvimento usou componentes disponíveis do Jetpack Compose para Wear OS. Consulte a documentação em "developer.android.com/training/wearables/compose" para explorar outros componentes UI.

Considere implementar persistência de dados para melhorias futuras.

Repositório: Github.com/TiagoDanin/WearOS-Count-Water-App

Let's Connect

Whether you have a project in mind, want to discuss tech, or just want to say hello, I'm always open to new conversations and opportunities.