Livro 2 - Projeto da Mini-Calculadora
| Site: | IFSC |
| Course: | 2025-2 - PROGRAMAÇÃO PARA DISPOSITIVOS MÓVEIS |
| Book: | Livro 2 - Projeto da Mini-Calculadora |
| Printed by: | Guest user |
| Date: | Friday, 19 December 2025, 3:13 PM |
1. Apresentação

Prezados alunos,
Neste livro, conheceremos novos recursos no desenvolvimento de aplicativos Android exemplificando com a construção de um aplicativo chamado MiniCalculadora.
O aplicativo aplica uma taxa de serviço a um valor digitado para mostrar o valor da taxa e o valor total. Quando se toca no teclado numérico para digitar o valor, o aplicativo já aplica a taxa de serviço (por padrão com o valor 15%) no valor total da conta, não necessitando de um botão para disparar a ação (cálculo). O valor da taxa de serviço é uma porcentagem que pode variar de 0% a 30% e será configurado através de uma barra de progresso (SeekBar). Mudanças no valor da conta ou na taxa de serviço irão automaticamente recalcular os demais valores na tela. Também é apresentado esses valores no formato de moeda (R$, por exemplo) dependendo da localidade do dispositivo Android.
Na construção do aplicativo serão apresentados novos componentes gráficos e como usar o ConstraintLayout para a construção de layouts de tela mais complexos.
Também discutiremos sobre persistência de dados, como salvar dados simples com com SharedPreferences.
Bons estudos!
2. Recursos Envolvidos
Nesse projeto usaremos os seguintes recursos na construção do aplicativo MiniCalculadora:
- Classe Activity
- Um aplicativo Android possui quatro tipos de componentes executáveis: activities, services, content providers e broadcast receivers. Neste projeto discutiremos activities que são subclasses da classe Activity (pacote android.app).
- Todo aplicativo possui diversas activities, cada uma vinculada a uma tela do aplicativo. Ao se clicar em uma View (componentes da interface) o usuário consegue interagir com a Activity.
- As Activities possuem um ciclo de vida onde passam por diferentes estados: em execução (active), pausado (paused) ou parado (stopped). A Activity muda de estados em resposta a diferentes tipos de eventos:
- Uma Activity ativa é visível e possui foco de toda tela.
- Uma Activity pausada é visível na tela, mas não possui foco, por exemplo, quando uma mensagem de alerta é mostrada.
- Uma Activity parada não é visível na tela, está no plano de fundo e pode ser encerrada pelo Android para poupar memória. Uma Activity é parada quando outra Activity se torna ativa. Por exemplo, durante o uso de um aplicativo se recebe uma chamada de telefone, o aplicativo das chamadas telefônicas se torna ativo e o anterior é parado.
- Quando uma Activity é iniciada (estado starting) ela sempre executa o método onCreate. É nesse método que definimos as configurações iniciais dos aplicativos.
- Outros tipos de método podem ser invocados de acordo com o clico de vida da Activity: onStart, onPause, onRestart, onResume, onStop e onDestroy.
- Para mais informações sobre o clico de vida de uma atividade, acesse o site da documentação oficial:
- ConstraintLayout
- O ConstraintLayout é uma forma de criar layouts através de Views relacionadas entre si ou com o parent, essas relações são chamadas de restrições (constraint)
- Cada View pode oferecer um posicionamento relativo em quatro direções: topo (top), fundo (bottom), esquerda (right) e direita (left).
- Essa configuração das Views pode parecer uma tarefa complexa mas pode ser facilmente realizada com a ajuda do editor de layout do Android Studio.
- Esse tipo de layout é bem flexível e permite criar designs complexos sem a necessidade de se usar hierarquias agrupadas.
- TextViews, EditText e SeekBar
- Neste projeto usaremos três componentes gráficos para o desenho do layout da tela: TextView (já utilizado no projeto 1), EditText e SeekBar.
- EditText - em outras tecnologias esse elemento é chamado de text box ou text field - trata-se de uma caixa de texto que aceita a entrada do usuário. Neste projeto vamos especificar a EditText para aceitar apenas números e restringir o máximo de números que podem ser digitados.
- SeekBar é uma barra deslizante para representar um valor inteiro entre 0 a 100 (por padrão). O usuário pode interagir com a barra e aumentar ou diminuir seu valor. Neste projeto, vamos usar a SeekBar para representar o valor da taxa de serviço e limitá-la para aceitar valores entre 0 e 30.
- Classe NumberFormat
- Vamos usar a classe NumberFormat (pacote java.text) para formatar os valores da aplicação para o padrão da moeda (R$, U$, etc.) de acordo com a localidade do aplicativo - uma parte importante da internacionalização de aplicativos. Além disso, também será aplicada formatação para mostrar a porcentagem da taxa de serviço.
- Interface TextWatcher
- Para fazer o tratamento de eventos de uma EditText é preciso implementar a interface TextWatcher (pacote android.text). Em particular, usaremos o método da interface onTextChanged para mostrar em moeda o novo valor inserido e calcular o valor da taxa de serviço e valor total da conta, toda vez que for digitado ou alterado um número da EditText.
- Interface OnSeekBarChangeListener
- Para fazer o tratamento de eventos de uma SeekBar temos que implementar a interface SeekBar.OnSeekBarChangeListener (pacote android.widget). Em particular, usaremos o método onProgressChanged para mostrar a taxa de serviço em porcentagem e calcular o valor da taxa de serviço e o total da conta toda vez que o usuário interagir com a barra de progresso.
- Material Design
- O Android segue as diretrizes de Material Design do Google para a construção de interfaces gráficas. Essas diretrizes especificam o comportamento dos componentes como elevação e sobra, cores, entre outros recursos.
- Atualmente, o Material Design encontra-se na versão 3 e também pode ser chamado de Material You
- Material Themes
- O tema de um aplicativo se refere a toda uma construção visual da aplicação. O Android oferece uma série de temas pré-definidos de acordo com o Material Design. Neste projeto vamos usar o tema chamado DayNight. Esse tipo de tema faz um contraste entre uma versão claro e outra escura.
- A partir do Android 10 (Api 29) é possível fazer a configuração do dispositivo entre dois temas: claro e escuro. Essa configuração reflete nos aplicativos, portanto ao usarmos o tema DayNight precisamos fazer duas versões, uma para o tema claro e outra para o tema escuro.
- Material Design: Elevation e Shadows
- Por especificações do Material Design, sombras (shadows) são aplicadas aos componentes de uma interface de acordo com a sua elevação com a ideia de imitar o mundo real. Logo, ao se especificar a propriedade elevation de uma View, o Android irá automaticamente aplicar um efeito de sombra ao componente.
- Segundo as diretrizes do Material Design, cada tipo de componente possui uma elevação recomendada, por exemplo, para um menu é 8dp e uma caixa de diálogo é 24dp.
- Material Design: Colors
- O Google também recomenda uma paleta de cores para ser usada em aplicativos. Cada cor possui versões de diferentes tonalidades, de tons mais claros a escuros, o que permite a construção de aplicativos com cores harmoniosas.
- Dentro do Android Studio existe uma interface para escolher facilmente entre as cores do Material design ou abrir uma janela para escolher uma cor personalizada.
- AndroidManifest.xml
- O arquivo AndroidManifest.xml é criado pelo Android Studio na inicialização do projeto. Esse arquivo contém diversas configuração do aplicativo como: nome do aplicativo, nome do pacote, nome do ícone, atividade principal, permissões e mais propriedades do aplicativo.
- Neste projeto iremos configurar o manifesto para que o aplicativo force o teclado numérico ficar aberto o tempo todo e aplicativo sempre ficar em modo retrato (portrait).
- Preferences
- Os aplicativos Android armazenam as preferências do usuário por meio de pares chave-valor (key-value), garantindo que essas informações sejam salvas de forma persistente no dispositivo. Assim, mesmo após o encerramento do aplicativo, as configurações são preservadas em um arquivo de preferências. Esse arquivo não é acessado diretamente; em vez disso, utiliza-se a classe SharedPreferences para ler, criar e alterar os dados.
- No projeto MiniCalculadora, o aplicativo acessa as preferências do usuário em dois momentos principais. Na inicialização do aplicativo para configurar a porcentagem a SeekBar. E no momento de pausa ou encerramento do aplicativo para salvar as informações para uma nova sessão futura.
3. Criando o projeto
Para começar o Projeto 2 vamos criar o projeto base de maneira similar ao Projeto 1. Siga os mesmos passos do Capitulo 3 do Livro 1 mas coloque as seguintes configurações:
- Escolha a template do projeto como Empty Views Activity
- Defina o nome do projeto para MiniCalculadora
- Especifique o campo Package name de acordo com as regras apresentadas no Livro 1
- Modifique o local de criação do projeto se achar necessário (Save Location)
- Na opção Language certifique-se de estar selecionado a linguagem Kotlin
- O mesmo com campo Mininum SDK, certifique-se que está selecionada a API 23 na caixa de seleção.
- Clique em Finish e aguarde a criação do novo projeto

4. Desenhando a tela do aplicativo
Para criar a tela do aplicativo usamos a ferramenta de desenho do Android Studio onde selecionamos os elementos gráficos na janela Palette e o arrastamos até a posição desejada na tela. Neste projeto serão utilizados os componentes EditText , TextView e SeekBar. Depois de arrastá-los, vamos fazer a configuração do posicionamento dos elementos segundo as diretrizes do ConstrainLayout.
4.1. O ConstrainLayout
O ConstraintLayout permite criar layout com views relacionadas entre si e com o parent de uma forma muito mais flexível sem precisar agrupar layouts uns dentro dos outros.
A razão para evitar o agrupamento de ViewGroups é devido ao tempo de renderização que pode crescer exponencialmente a cada nível de agrupamento. Se esse tempo aumenta, o aplicativo irá perder frames no que resulta em transições não tão suáveis.
Uma das maiores vantagens que o ConstraintLayout traz é a velocidade. Além do nos permitir criar UIs sem agrupar viewGroups, o editor de layouts do Android Studio facilita ainda mais o nosso trabalho. Veja abaixo um exemplo de layout criado usando o ConstraintLayout com a visualização de blueprint dentro do Android Studio:

Você pode conferir o posicionamento dos elementos, mas o mais importante são as suas restrições (constraint). Identificadas pelas setas, elas são as configurações que permitem um elemento configurar seu posicionamento em relação ao outro e podem possuir também um tamanho referente a margem (distância) entre um componente e outro.
Com o ConstraintLayout controlamos o posicionamento com os atributos:
- layout_constraintLeft_toLeftOf : Alinha o lado esquerdo da view desejada com o lado esquerdo da outra view
- layout_constraintLeft_toRightOf: Alinha o lado esquerdo da view desejada com o lado direito da outra view
- layout_constraintRight_toLeftOf: Alinha o lado direito da view desejada com o lado esquerdo da outra view
- layout_constraintRight_toRightOf: Alinha o lado direito da view desejada com o lado esquerdo da outra view
- layout_constraintTop_toTopOf: Alinha o lado de cima da view desejada com o lado de cima da outra view
- layout_constraintTop_toBottomOf: Alinha o lado de cima da view desejada com o lado de baixo da outra view
- layout_constraintBottom_toTopOf: Alinha o lado de baixo da view desejada com o lado de cima da outra view
- layout_constraintBottom_toBottomOf: Alinha o lado de baixo da view desejada com o lado de baixo da outra view
Nas próximas seções vamos ilustrar na prática como fazer a configuração dessas restrições com o editor de layout do Android Studio.
4.2. Adicionando os elementos na tela
A template Empty Views Activity cria um atividade em branco e aplica o ConstrantLayout como o layout padrão, logo o arquivo activity_main.xml já está configurado para o projeto. Usaremos o editor de layout do Android Studio para adicionar os componentes desejados na tela e configurar seu posicionamento, configurando todas as restrições (constraint) necessárias. No próximo capítulo será mostrada a configuração individual de cada componente, por hora vamos apenas preparar o design inicial da tela.
Componente amountEditText
A primeira seção do aplicativo consiste no componente para entrada de dados: EditText. Esse componente faz a leitura do teclado virtual e recebe informações do usuário. Essa caixa de texto está localizada na paleta Text e possui diferentes versões para lidar com determinados tipos de dados. Ela é um componente versátil e serve para ler dados numéricos, textos, e-mail ou telefone. No caso do aplicativo MiniCalculadora vamos escolher a variação Number.

Para a configuração inicial da EditText siga os passos abaixo:
- Abra o arquivo activity_main.xml
- Remova o componente TextView("Hello World"), ele é criado automaticamente pela template
- Arraste um componente EditText do tipo Number como na imagem acima para a tela de design
- Defina a propriedade id como amountEditText
Com esses passos você adicionou corretamente o novo componente e poderia arrastar e soltar para fazer o posicionamento do elemento em qualquer parte da tela. No entanto, não lidamos com posições absolutas. Vamos usar as restrições (constraints) para posicionar o elemento no topo da tela. Para fazer isso observe o seguinte quadro do editor de layout.

Com o componente selecionado, você conseguirá visualizar na aba de atributos as restrições aplicadas a ele. Inicialmente não existe nada, mas se você clicar no botão azul de "+" isso fará o componente criar uma relação com o componente mais próximo naquela direção (top, fundo, esquerda, direta). Essas relações também podem ser criadas usando as "bolotas" na borda do componente e arrastando até outro componente.

Se você ligar o componente ao topo da tela isso criará a restrição TopOf em relação ao parent. Isso criará um layout onde o componente sempre estará localizado logo abaixo do topo da tela. O mesmo poderia ser feito com o fundo para criar um componente que sempre apareça na posição mais baixa da tela, independente do tamanho da tela do dispositivo (Nota: isso não é possível se fosse usado posições absolutas).

Observe também que é possível associar a uma restrição uma margem (distância) para afastar os componentes, não os deixar grudados ou simplesmente para apresentar uma estética mais agradável. Você pode fazer isso facilmente na janela de atributos na seção Layout. O valor da margem é apresentando em múltiplos de 8dp pois segue as recomendações do Material Design, mas se desejar você pode manualmente colocar qualquer valor.
Uma observação importante quanto as restrições é usar ao menos duas para conseguir posicionar o elemento corretamente no eixo horizontal e vertical. Assim, você pode usar duas, três ou quatro restrições de acordo com o necessário.
Para fazer o posicionamento correto do componente crie as seguintes restrições: topo, esquerda e direita com o parent. Use margem 16dp e defini a propriedade layout_width para 0dp (match constraint). Como resultado você terá a seguinte pré-visualização:

Componente amountTextView
Para o design de tela serão adicionadas várias TextViews. Ao todo serão criados seis novos elementos: amountTextView, percentTextView, tipLabelTextView, tipTextView, totalLabelTextView e totalTextView.
Adicione primeiramente o amountTextView e ajuste seu posicionamento para ficar embaixo do componente amountEditText. Crie as restrições topo, esquerda e direita e use margem16dp. Defina a propriedade layout_width para 0dp (match constraint). As propriedades devem ficar como na imagem abaixo.

Componentes percentSeekBar e percentTextView
A SeekBar, ou barra deslizante, é um widget para selecionar valores de um determinado intervalo, por padrão do valor 0 ao 100. Vamos usar a SeekBar no aplicativo para representar um taxa em porcentagem (%).
Siga os passos abaixo para adicionar os novos componente na tela:
- Arraste um componente SeekBar (aba Widget da paleta) para a tela
- Defina a propriedade id como percentSeekBar
- Arraste um componente TextView
- Defina a propriedade id como percentTextView
- Na percentSeekBar: crie uma restrição no topo para posicionar abaixo de amountTextView, crie uma restrição na direita em relação ao parent e a esquerda em relação a percentTextView. Configure todas as margens para 16dp
- Na percentTextView: crie uma restrição a esquerda em relação ao parent (margem 16dp) e uma restrição do topo ao topo da percentSeekBar (margem 0dp).
- Adicionalmente, crie uma restrição do fundo ao fundo da percentSeekBar (margem 0dp). Essa última restrição fará com que a TextView se alinhe centralizada verticalmente a barra deslizante.
- Por fim, define as propriedades layout_width para 0dp (match constraint) e layout_height (32dp) para a percentSeekBar. Isso fará com que ela ocupe toda a largura disponível na tela e aumente seu tamanho. Se você configurou corretamente as restrições acima, a percentTextView se encontrará alinhada verticalmente
Observe o resultado final na blueprint abaixo:
Componentes tipLabelTextView e tipTextView
Os próximos componentes são duas TextView e seguem um posicionamento padrão de rótulos (labels), isto é, um texto fixo na esquerda e um valor apresentado na direita. Para o posicionamento dos elementos faremos algo parecido com antes. A TextView à direita usará as restrições para ela ficar abaixo da percentSeekBar e a direita do parent. E a outra TextView se alinha verticalmente a mesma.
Siga os passos abaixo para adicionar os novos componente na tela:
- Arraste um componente TextView
- Defina a propriedade id como tipLabelTextView
- Arraste um componente TextView
- Defina a propriedade id como tipTextView
- Na tipTextView: crie uma restrição no topo para posicionar abaixo de percentSeekBar, crie uma restrição na direita em relação ao parent e a esquerda em relação a tipLabelTextView. Configure todas as margens para 16dp
- Na tipLabelTextView: crie uma restrição a esquerda em relação ao parent (margem16dp) e uma restrição do topo a tipTextView(margem 0dp). Adicionalmente, crie uma restrição do fundo ao fundo da tipTextView (margem 0dp). Essa última restrição fará com que a TextView se alinhe verticalmente quando aumentarmos o tamanho da tipTextView no próximo capítulo. No momento, o design não é muito afetado mas após as modificações no próximo capítulo você verá a diferença.
- Por fim, define as propriedades layout_width para 0dp (match constraint) na tipTextView.
Observe o resultado final na blueprint abaixo:
Componentes totalLabelTextView e totalTextView
Repita os passos anteriores e crie duas novas TextViews: totalLabelTextView e totalTextView. Siga o mesmo esquema. Use como referência a blueprint abaixo:
Design e Blueprint
Certifique-se do layout estar correto pois no próximo capítulo faremos a configuração apenas das características de cada elemento. Se você seguiu os passos corretamente, o resultado final será o seguinte desenho tela:
4.3. Especificando os recursos strings
Para a configuração dos textos na tela iremos adicionar recursos strings no arquivo strings.xml como detalhado no projeto 1. No caso do EditText um nova propriedade hint deve ser configurada também, pois é ela que apresenta uma “dica” do que deve ser inserido na caixa de texto.
Assim, vá a propriedade text(ou hint no caso do EditText) de cada componente TextView e adicione um novo recurso string a cada elemento. A lista dos recursos e sua View relacionada se encontra na lista da tabela a seguir:

Observe que são omissas as strings para amountTextView, tipTextView e totalTextView. Isso ocorre porque os seus valores serão calculados e apresentados dinamicamente.
Adicionando novos recursos strings
Para adicionar os novos recursos strings ao elementos gráficos da tela procure a propriedade text e clique no botão branco ao lado. Observe que a posição exata da propriedade pode variar dependendo do tipo de elemento sendo configurado (TextView, EditText, SeekBar). Use como referência a figura:

Na janela Pick a resource clique no botão + localizado na parte superior esquerda da janela e selecione a opção String Value.

Na janela New String Value complete os campos com as informações adequadas, por exemplo:

Clique em OK para confirmar a criação do novo valor e em seguida selecione o valor correto e confirme novamente com o botão OK.
Repita o procedimento em todos os elementos restantes e adicione seus textos como informado na tabela.
4.4. Configurando os elementos
Siga os passos a seguir para fazer a configuração de cada elemento.
Configurando amountEditText
Nas configurações do EditText iremos fazer com que ele aceite apenas números e tenha um tamanho máximo. Para isso, localize as propriedades abaixo nesse componente e coloque os valores adequados:
- digits: 0123456789 – isso fará com que sejam aceitos apenas números, e não símbolos numéricos com -, “,” ou “.” presentes no teclado numérico.
- maxLength: 6 – limita o tamanho (número de caracteres) para o valor de entrada. Logo o valor mais alto aceito é 999999
Configurando amountTextView
Nas configurações localize as propriedades abaixo e coloque os valores adequados:
- background: #BBDEFB – essa propriedade especifica a cor de fundo com componente
- padding– essa propriedade especifica o espaçamento em volta do conteúdo da View. Nesse caso iremos selecionar a opção geral e alocar um novo tipo de recurso (similar as strings criadas no arquivo strings.xml) clicando no botão branco ao lado da caixa de entrada. Similar a quando se cria um novo recurso string, crie um novo recurso com o nome textview_padding e coloque seu valor como 16dp.

- elevation–propriedade para adicionar o efeito de sombra a View. Novamente crie um novo recurso e coloque o nome textview_elevation com o valor 4dp.
Note que, ambos recursos criados nessa etapa (padding e elevation) serão usados nos demais componentes da tela por isso é necessário criar eles dessa forma ao invés de digitar manualmente pois seus valores serão compartilhados. Isto é, se eu modificar futuramente o valor de elevação então todos os componentes serão automaticamente configurados também.
Configurando percentSeekBar
Por padrão o componente SeekBar possui a margem de valor de 0 a 100, iremos modificar essa barra para ir de 0 a 30. Também iremos definir o valor inicial como 15. Assim, selecione esse componente e localize as propriedades abaixo para colocar os valores adequados:
- max: 30
- progress: 15 – essa propriedade especifica a cor de fundo com componente
Configurando tipTextView e totalTextView
Selecione esses componentes e localize as propriedades abaixo para colocar os valores adequados:
- background: #FFE0B2
- gravity: center - essa propriedade faz com que o texto seja centralizado no meio do componente
- padding: selecione o recurso textview_padding
- elevation: selecione o recurso textview_elevation
Omitindo a EditText
Usar uma EditText para representar o símbolo da moeda corrente é um tarefa complicada, mas existem diversas soluções na comunidade para lidar com o problema. Por exemplo, é possível usar uma biblioteca para incorporar ao projeto uma EditText personalizada.
Nesse aplicativo iremos fazer um pequeno truque para lidar com o padrão de moeda (R$, U$, etc). Vamos omitir a EditText fazendo com que amountTextView fique em cima dela, para tanto vamos suar a propriedade elevação. O usuário continuará digitando valores mas não verá especificamente a caixa de texto, o que ele verá será a TextView apresentando o valor digitado corretamente em padrão de moeda, por exemplo: R$ 120,00.
Para aplicar essa mudança no aplicativo selecione amountTextView e modifique a restrição do topo para criar uma relação com o parent. Use margem 16dp.
Observação: esse pequeno truque foi baseado no projeto do livro e só da certo porque nesse aplicativo só existe uma única EditText. Isso faz com que o aplicativo ao abrir já inicie o teclado numérico focado a EditText oculta.
Resultado final
Se você executou com exatidão os passos acima terá como resultado final a seguinte tela do aplicativo:

5. Definindo um tema
Neste projeto iremos aprender como editar o tema do aplicativo e personalizar a nossa aplicação. O tema de um aplicativo são sua paleta de cores, tipografia e formas criadas para dar a identidade visual ao app. Pense nas cores temas do Facebook (azul) e iFood (vermelho) como exemplo. Essas cores aparecem em diferentes elementos de seus apps como botões, abas, texto em destaque, etc.
O Android e o Material Design oferecem uma gama de temas pré-definidos que podem ser personalizadas como se desejar. Na template Empty Views Activity é aplicado como tema padrão o tema do Material Design 3 DayNight na variação sem app bar.
Neste projeto da MiniCalculadora iremos modificar o tema para incluir a app bar e personalizar as cores primária, secundária e de realce do sistema. Observe abaixo como as propriedades das cores irão afetar o tema do aplicativo. Algumas propriedades acabam afetando diversos componentes gráficos na tela, como a cor primária e de realce, ao se modificar esses valores os componentes vão se adaptar à nova cor declarada, por exemplo, mudar a cor secundária mudará a cor da SeekBar.
Todas as informações sobre o tema do aplicativo estão localizadas no arquivo chamado styles.xml e está configurado no arquivo de manifesto qual tema é aplicada a cada atividade. No projeto MiniCalculadora as configurações já estão preparadas. Assim, não é necessário modificar o arquivo de manifesto, apenas alterar o estilo de tema definido em styles.xml para personalizar a aparência do aplicativo como desejarmos.
5.1. Temas do Material Design
Existem diversos temas para aplicar em seu aplicativo. Cada tema possui suas especificações e deve ser consultada a sua documentação. No caso dos temas do Material Design você pode conferir as informações em:
Nos temas do Material Design são aplicadas 12 cores para criar a paleta do aplicativo. Cada cor possui um termo para identificar o seu propósito, cor primária por exemplo, e também possui valores padrões para os tons claro e escuro.
Atributos de cores para o tema claro

Atributos de cores para o tema escuro

As cores do tema são aplicadas aos componentes de design. No caso de um botão, as seguintes características refletem no seu visual

Propriedades e cores
Cada propriedade de cor pode refletir em diferentes componentes visuais do aplicativo. Para aprender a criar seus temas, conheça qual a finalidade de cada propriedade:
- colorPrimary e colorSecondary representam as cores da sua marca
- colorPrimaryVariant e colorSecondaryVariantsão versões mais claras ou escuras da sua marca
- colorSurface é aplicada sobre "superfícies" como cartões e abas
- android:colorBackground é a cor de fundo da janela do seu aplicativo
- colorError é empregada para mensagens de erro ou avisos
- As várias cores on (colors (colorOnPrimary, colorOnSecondary, colorOnSurface, etc.) são são usados para tingir o conteúdo em primeiro plano (como texto e ícones) exibido sobre as outras cores. Elas precisam atender aos requisitos de acessibilidade e ter contraste suficiente em relação às cores em que são exibidos.
Recomendações
Ao criar seu tema considere os seguintes aspectos:
- Você deve sempre substituir colorPrimary, colorSecondary e suas variantes, a menos que sua marca seja parecidas com os valores padrões roxo/verde-azulado
- Você não precisa substituir todas as cores. Alguns, como colorSurface, usam cores neutras, portanto, confiar nos valores padrão é perfeitamente aceitável.
- Caso sua marca não defina nenhum tipo de cor secundária ou de destaque, não há problema em usar uma única cor tanto para colorPrimary quanto para colorSecondary. O mesmo pode ser dito das variantes (por exemplo, colorPrimary e colorPrimaryVariant podem ser iguais).
- Apesar de serem atributos separados, existe uma ligação inerente entre uma cor, sua variante (se existir) e sua cor “On” (por exemplo, colorPrimary, colorPrimaryVariant e colorOnPrimary). Substituir um significa que você precisa verificar os outros para ver se fazem sentido e atendem aos requisitos de acessibilidade.
5.2. Personalizando o tema do aplicativo
Para personalizar o tema do aplicativo MiniCalculadora, abra o arquivo styles.xml (ou themes.xml) como ilustrado na figura a seguir.

Dentro deste arquivo estão todas as configurações do tema do aplicativo. Na template atual do Android Studio o tema padrão aparece com informações em branco e se configura para não mostrar a app bar (isso pode ser visto na especificação NoActionBar). Basicamente, o arquivo apenas cria um tema baseado no DayNight do Material Design 3 e coloca um nome de acordo com o projeto.
Vamos realizar adicionar novos campos ao tema e realizar mudanças na aparência do seu aplicativo. Vamos definir as cores primárias, secundárias e configurar a aparência do aplicativo para mostrar a app bar. Mas primeiro vamos lidar com as cores do aplicativo.
Criando recursos de cor
O valor para cada cor é um código em hexadecimal (ex, #B2FAC3) e pode ser obtido através de diferentes fontes. Como referência você pode usar o conjunto de cores do Material Design para pintar seus aplicativos através do site https://materialuicolors.co/ ou escolher sua própria preferência.
Para este aplicativo vamos adicionar novos recursos de cor ao arquivo colors.xml. Para tanto, abra o arquivo e adicione as seguintes cores:
- Cor primária - colorPrimary - #2196F3
- Cor primária escura - colorPrimaryVariant- #1976D2
- Cor de realce - colorSecondary - #FF9100
O arquivo colors.xml deve ficar como:
<?xml version="1.0" encoding="utf-8"?>
<resources>
...
<color name="colorPrimary">#2196F3</color>
<color name="colorPrimaryVariant">#1976D2</color>
<color name="colorSecondary">#FF9100</color>
</resources>
Criação do tema personalizado
Agora precisamos configurar o tema do aplicativo para fazer o uso destas novas cores. Assim, abra o arquivo styles.xml e modifique as propriedades para usar os valores corretamente:
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.MiniCalculadora" parent="Theme.Material3.DayNight">
<!-- Customize your light theme here. -->
<!-- Primary brand color. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/colorSecondary</item>
<!-- App bar color. -->
<item name="colorSurface">@color/colorPrimary</item>
<item name="colorOnSurface">@color/white</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
</style>
<style name="Theme.MiniCalculadora" parent="Base.Theme.MiniCalculadora" />
</resources>
Inicialmente removemos a especificação NoActionBar na declaração do tema. A seguir definimos os valores para as cores primárias e secundárias de acordo com as cores declaradas no arquivo colors.xml. Configuramos as cores para a app bar alterando as propriedades das cores de superfícies. E por fim, declaramos especificamente qual será a cor da status bar.
O resultado final é apresentado na imagem a seguir. Para ver a status bar e app bar é necessário ativar a exibição da interface do sistema Android. Procure o ícone parecido com um olho e ative a opção "Show System UI" como na figura.

6. Lógica da Aplicação
No aplicativo MiniCalculadora, há um campo de entrada para inserir valores numéricos e uma barra deslizante que permite selecionar a taxa de serviço, variando entre 0% e 30%. O objetivo da MiniCalculadora é calcular automaticamente o valor da taxa de serviço e o valor total a ser pago, com base nos dados fornecidos pelo usuário. Além disso, todos os valores exibidos na interface do aplicativo devem seguir o formato adequado, incluindo a representação de porcentagens (%) e de moedas (R$, US$, entre outras). Neste capítulo, será explicada a lógica da aplicação e sua implementação no arquivo MainActivity.kt.
6.1. Pacotes utilizados
Na implementação do projeto, será necessário adicionar vários pacotes essenciais para a execução do código. Cada elemento gráfico da tela (widgets) e seu respectivo tratamento de eventos exigirão a inclusão dos pacotes apropriados. Além disso, para realizar a formatação adequada dos valores numéricos em moeda e porcentagem, utilizaremos uma classe proveniente do Java, a classe NumberFormat, que está localizada no pacote java.text.
6.2. Atributos da classe
Boas práticas de programação indicam que a criação de todos os atributos em uma classe deve ocorrer na parte superior do arquivo. Nessa seção, devem ser declaradas todas as variáveis e constantes que serão utilizadas pela aplicação.
Para este projeto, vamos criar duas constantes: currencyFormat e percentFormat. Ambas são instâncias da classe NumberFormat e serão responsáveis pela formatação da moeda e porcentagem, respectivamente. A especificação do tipo de formatação que será usada é obtida por meio da invocação dos métodos getCurrencyInstance() e getPercentInstance(), respectivamente. A classe NumberFormat será responsável por determinar o formato da moeda a ser usado, o qual depende das configurações locais do dispositivo Android. Isso significa que a formatação da moeda pode ser em reais (R$) ou dólares (U$), dependendo das configurações do dispositivo.
Além das constantes de formatação, declaramos duas variáveis para atender aos requisitos da aplicação:
- billAmount: representa o valor inserido pelo usuário e é inicializado com 0.
- percent: representa a taxa de serviço e é inicializado com 0.15.
Essas variáveis serão modificadas à medida que o usuário interage com as views da tela e serão usadas nos cálculos.
No início da classe, também declaramos variáveis para servir como referência aos componentes gráficos que serão manipulados na aplicação. Neste projeto, é necessário obter referências para as views amountTextView, percentTextView, tipTextView e totalTextView.
package ...
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.widget.EditText
import android.widget.SeekBar
import android.widget.TextView
import java.text.NumberFormat
class MainActivity : AppCompatActivity() {
//objetos para formatação nos padrões de MOEDA e PORCENTAGEM
private val currencyFormat: NumberFormat = NumberFormat.getCurrencyInstance()
private val percentFormat: NumberFormat = NumberFormat.getPercentInstance()
//variáveis para a lógica da aplicação
private var billAmount = 0.0 // R$ 0,00
private var percent = 0.15 // 15 %
//variáveis com referências aos elementos da UI
private var amountTextView: TextView? = null
private var amountEditText: EditText? = null
private var percentTextView: TextView? = null
private var percentSeekBar: SeekBar? = null
private var tipTextView: TextView? = null
private var totalTextView: TextView? = null
6.3. Inicialização da Atividade
No momento da criação de uma Activity, o método onCreate é invocado, e é neste método que devem ser configurados os aspectos iniciais do aplicativo. Aqui, faremos a conexão entre os elementos da interface gráfica e a programação, bem como a implementação do tratamento de eventos.
Para acessar um elemento da interface gráfica, utilizamos o método findViewById, que, por meio de seu parâmetro, retorna uma referência (objeto) ao elemento correspondente criado no arquivo de layout. Neste ponto, a classe R desempenha um papel crucial, pois, por meio do ID da View, ela consegue localizar corretamente o elemento na tela e retornar sua referência.
No que diz respeito ao tratamento de eventos, o aplicativo precisará implementar duas classes abstratas: TextWatcher (usada para reagir à inserção de texto) e SeekBarListener (que responde às interações com a barra de progresso). Para adicionar um tratamento de eventos ao EditText, inserimos o objeto criado a partir da classe abstrata utilizando o método addTextChangedListener(). No caso do tratamento de eventos para a SeekBar, adicionamos o objeto criado pela classe abstrata com o método setOnSeekBarChangeListener(). É importante notar que ao incluir esta parte do código, o programa indicará um erro, o que é normal, uma vez que ainda não criamos os objetos necessários. Faremos isso nos próximos capítulos.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//configura as referências aos componentes gráficos na tela
amountEditText = findViewById(R.id.amountEditText)
amountTextView = findViewById(R.id.amountTextView)
percentTextView = findViewById(R.id.percentTextView)
percentSeekBar = findViewById(R.id.percentSeekBar)
tipTextView = findViewById(R.id.tipTextView)
totalTextView = findViewById(R.id.totalTextView)
//apresenta os valores iniciais da taxa e total em padrão de moeda
tipTextView?.text = currencyFormat.format(0.0)
totalTextView?.text = currencyFormat.format(0.0)
//acessa o componente SeekBar e adiciona um tratamento de eventos
percentSeekBar?.setOnSeekBarChangeListener(seekBarListener)
//acessa o componente EditText e adiciona um tratamento de eventos
amountEditText?.addTextChangedListener(amountEditTextWatcher)
}
6.4. Método calculate()
O método calculate() pega os valores salvos e calcula o valor da taxa de serviço e total da conta. Esse método deve ser invocado após a alteração do valor de entrada e a porcentagem da taxa de serviço.
Sua lógica é bem simples, recebe os valores, aplica as fórmulas e atualiza os novos valores na tela com a propriedade text de cada View. Contudo, não vamos mostrar o valor numérico bruto na tela, mas assim usar a formatação dos números com a constante currencyFormat.
A formatação dos números usa um método format para receber um valor numérico e retornar a apresentação correta. Por exemplo, invocar o método format de CurrencyFormat com o valor 102.35 retornará R$102,35.
private fun calculate() {
// calcula o valor da taxa e total
val tip = billAmount * percent
val total = billAmount + tip
//mostrar os resultados na tela
tipTextView?.text = currencyFormat.format(tip);
totalTextView?.text = currencyFormat.format(total);
}
6.5. Interface SeekBarListener
De acordo com a iteração do usuário, o tratamento de eventos de uma SeekBar dispara três métodos: onProgressChanged, onStartTrackingTouch e onStopTrackingTouch. Para o nosso caso, é necessário implementar apenas o primeiro, o resto fica em branco. Assim, a se mover a barra de progresso o programa deve atualizar o novo valor da porcentagem. Contudo, lembre que o valor da barra de progresso na SeekBar varia de 0 a 30, assim o novo valor de porcentagem deve receber o valor inteiro da barra de progresso e converter em %, isto é, dividir por 100. Por fim, invoca-se o método calculate() para refazer os cálculos da aplicação.
private val seekBarListener = object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
//atualiza a variável com o novo valor
percent = progress/100.0
//atualiza UI
percentTextView?.text = percentFormat.format(percent)
//manda o programa executar os cálculos
calculate()
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
}
6.6. Interface TextWatcher
O tratamento de eventos de um EditText dispara três métodos: onTextChanged, beforeTextChanged e afterTextChanged. Para o nosso caso, é necessário implementar apenas o primeiro, o resto fica em branco. Ao se inserir ou apagar um digito na caixa de entrada, o método observa o valor digitado, o converte para representação em moeda e manda atualizar a amountTextView com o novo valor. Dessa forma, por mais que o usuário insira apenas números, a TextView faz com que apareça a visualização em formato de moeda.
Contudo, no caso de um valor em branco ou não numérico, a conversão do valor bruto em moeda irá dar errado e disparar uma exceção do tipo NumberFormatException. Assim, é necessário criar um tratamento de exceções (implementado através dos comandos try/catch) para caso aconteça esse erro e o programa não trave. Dessa forma, o método faz a tentativa de conversão normal, e em caso de erro, configura os valores como em branco. Independente do disparo ou não de uma exceção, a última etapa do tratamento de eventos é invocar o método calculate para refazer os cálculos da aplicação.
private val amountEditTextWatcher = object: TextWatcher {
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
try {
//faz a leitura do valor da caixa de texto e converte em número real (double)
billAmount = s.toString().toDouble() / 100.0
amountTextView?.text = currencyFormat.format(billAmount)
} catch (e: NumberFormatException) {
//se a entrada for um valor em branco (o usuário apagou o que digitou por exemplo)
//dispara uma exceção. Então o programa seta o valor da conta como ZERO
billAmount = 0.0
//reseta a entrada com a mensagem de dica inicial
amountTextView?.setText(R.string.enter_amount)
}
//manda o programa executar os cálculos
calculate()
}
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { }
}
7. Manifesto Android
O Manifesto Android (arquivo AndroidManifest.xml) é um arquivo essencial para qualquer aplicativo Android. Ele define informações fundamentais que o sistema operacional precisa para executar o aplicativo corretamente. No manifesto, você especifica a estrutura básica do app, permissões, componentes, entre outros detalhes que guiam o comportamento do aplicativo no dispositivo.
Algumas Informações Importantes no Manifesto:
Nome do Pacote (Package Name): O manifesto define o nome do pacote do aplicativo, que serve como um identificador único do app no Google Play e no sistema Android.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="br.edu.ifsc.ctds.minicalculadora">
Contudo, nas novas versões do Gradle essa propriedade foi movida e se encontra no arquivo build.gradle na seção Android -> namespace
Permissões: O manifesto é onde você declara as permissões que seu aplicativo precisa para acessar recursos protegidos do sistema, como internet, câmera, GPS, etc. Essas permissões são solicitadas ao usuário durante a instalação ou execução do app.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
Componentes do App: O manifesto define os principais componentes do aplicativo: Activities, Services, Broadcast Receivers e Content Providers. Isso informa ao sistema quais partes compõem o app e como ele interage com o usuário ou outros serviços.
- Activities: Representa telas do aplicativo.
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- Services: Define serviços que executam tarefas em segundo plano.
<service android:name=".MyService" />
- Broadcast Receivers: Define componentes que recebem notificações de eventos globais, como mudanças na conectividade.
<receiver android:name=".MyBroadcastReceiver" />
- Content Providers: Gerencia o acesso a um banco de dados compartilhado.
<provider android:name=".MyContentProvider" />
Configurações de Hardware e Software: O manifesto pode declarar quais recursos de hardware o app requer, como câmera, GPS ou sensor de movimento, além de configurações de software como bibliotecas externas.
<uses-feature android:name="android.hardware.camera" />
Configurações de Orientação e Tela: O manifesto permite especificar como o app lida com mudanças de orientação (paisagem/retrato) ou tamanhos de tela.
android:screenOrientation="portrait"
Importância do Manifesto
- Registro dos Componentes: Todos os componentes essenciais, como atividades e serviços, precisam ser registrados no manifesto para que o Android os reconheça.
- Segurança: As permissões e restrições declaradas no manifesto são essenciais para garantir a segurança do usuário, assegurando que o aplicativo apenas acesse os recursos necessários.
- Compatibilidade: Definir versões de SDK e hardware ajuda a garantir que o app funcione corretamente em dispositivos com diferentes especificações.
O manifesto é a “carta de apresentação” do aplicativo para o sistema Android, garantindo que ele funcione conforme as especificações e regras de segurança da plataforma.
7.1. Modo retrato e configuração do teclado virtual
Para finalizar o aplicativo, vamos configurar o arquivo do AndroidManifest.xml para forçar a inicialização do teclado virtual e orientação em forma de retrato (portrait). Para tanto, realize as etapas a seguir:
- Abra o arquivo AndroidManifest.xml
- Adicione nas propriedades da activity os campos:
- android:screenOrientation=”portrait”
- android:windowSoftInputMode=”stateAlwaysVisible”
O arquivo AndroidManifest.xml deve ficar parecido com o código a seguir, mudanças podem ocorrer dependendo da versão do Android Studio e Gradle instalado na sua máquina. O que importa para nós é apenas as duas linhas destacadas. Não precisa alterar nada do código original, apenas adicionar as duas linhas de código..
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateAlwaysVisible"
android:theme="@style/Theme.MiniCalculadora"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
</application>
</manifest>
8. Persistência de Dados
No desenvolvimento de aplicativos Android, é essencial lidar com a persistência de dados, ou seja, garantir que as informações geradas ou usadas pelo app sejam armazenadas e possam ser acessadas posteriormente, mesmo após o fechamento do aplicativo. O Android oferece diferentes formas de armazenamento para atender a diferentes necessidades:
- Armazenamento específico do app (Internal Storage): Utilizado para armazenar dados que pertencem exclusivamente ao aplicativo e que não precisam ser acessados por outros apps. Esses dados são removidos quando o aplicativo é desinstalado.
- Armazenamento compartilhado (External Storage): Permite o armazenamento de arquivos em uma área acessível por outros aplicativos e pelo próprio usuário, como músicas, fotos ou documentos.
- Preferências (SharedPreferences): Ideal para armazenar dados simples e em pequena quantidade, como configurações ou preferências do usuário.
- Bancos de dados (SQLite ou Room): Utilizado quando há necessidade de gerenciar dados estruturados de forma mais complexa, permitindo o armazenamento, consulta e manipulação de grandes volumes de informações.
Cada um desses métodos de persistência tem suas características e cenários de uso apropriados. A seguir, vamos explorar em mais detalhes o uso das SharedPreferences.
O que são SharedPreferences?
O SharedPreferences no Android é uma ferramenta para armazenar dados simples e em pequena escala, como configurações, preferências do usuário ou estados simples de atividades. Esse armazenamento é ideal para dados que não precisam de estrutura complexa, como texto simples, números ou valores booleanos.
Como Funciona
SharedPreferences armazena dados em um formato chave-valor. Isso significa que você define uma chave para cada valor armazenado, o que facilita a recuperação dos dados posteriormente. Os dados armazenados via SharedPreferences são persistidos até que o aplicativo seja desinstalado ou os valores sejam explicitamente removidos.
Principais Usos
- Configurações do aplicativo: Armazenar preferências de temas, idiomas, notificações, etc.
- Dados de login: Guardar informações sobre se o usuário já está logado ou precisa se autenticar novamente.
- Estados simples: Armazenar o progresso em um tutorial ou a última aba visualizada.
8.1. Como usar SharedPreferences
Neste seção vamos ilustrar como utilizar SharedPreferences no seu aplicativo.
Acesso as preferências compartilhadas
Um objeto SharedPreferences representa um arquivo que contém pares de chave-valor e oferece métodos simples para leitura e gravação de dados. Cada arquivo SharedPreferences é gerenciado pelo framework do Android e pode ser de acesso exclusivo ao aplicativo ou compartilhado.
Você pode acessar um arquivo de preferências de duas maneiras: criando um novo arquivo de preferências ou acessando um existente, utilizando um dos seguintes métodos:
- getSharedPreferences(): Use este método quando precisar de múltiplos arquivos de preferências identificados por nome. O nome do arquivo é especificado como o primeiro parâmetro e o método pode ser chamado em qualquer Context do aplicativo.
- getPreferences(): Este método é usado em uma Activity quando há necessidade de apenas um arquivo de preferências para aquela atividade. Ele recupera um arquivo padrão vinculado à Activity, portanto, não é necessário fornecer um nome.
Por exemplo, o código abaixo acessa o arquivo de preferências compartilhadas identificado pela chave de recurso R.string.preference_file_key e o abre no modo privado, garantindo que o arquivo seja acessível somente pelo aplicativo:
val sharedPref = getSharedPreferences(
getString(R.string.preference_file_key), Context.MODE_PRIVATE)
Essa primeira abordagem é útil caso seja necessário criar mais de um arquivo para seu aplicativo. Caso contrário, você pode usar a outra alternativa e criar apenas um arquivo de preferências compartilhadas com método getPreferences():
val sharedPref = getPreferences(Context.MODE_PRIVATE)
Salvar dados
Para salvar dados, usamos uma espécie de editor provido pelo arquivo de preferências. Através dele podemos salvar valores com métodos put____ de acordo com o tipo adequado. Os dados são salvos como um conjunto de chave/valor. Por fim, devemos usar o método apply para aplicar as mudanças desejadas no arquivo de preferências.
// Obtém o SharedPreferences
val sharedPreferences = getSharedPreferences("AppPrefs", MODE_PRIVATE)
// Abre o editor para salvar
val editor = sharedPreferences.edit()
// Armazena os dados com chave e valor
editor.putString("username", "JohnDoe")
editor.putInt("age", 25)
// Confirma a gravação dos dados
editor.apply()
Recuperar dados
A leitura dos dados é realizada com os métodos get____ dependendo do tipo adequado. Os dados são lidos de acordo com a chave informada. É necessário passar como parâmetro também um valor padrão, caso nenhum valor seja encontrado o método get retornará o valor padrão.
// Obtém o SharedPreferences
val sharedPreferences = getSharedPreferences("AppPrefs", MODE_PRIVATE)
// Recupera os dados usando a chave
val username = sharedPreferences.getString("username", "Guest")
val age = sharedPreferences.getInt("age", 0)
Remover dados
É possível também remover dados do arquivo preferências. Para tanto, usamos o nome da chave junto com o método remove. Por fim, devemos chamar o método apply para aplicar as mudanças ao arquivos.
// Obtém o SharedPreferences
val sharedPreferences = getSharedPreferences("AppPrefs", MODE_PRIVATE)
// Abre o editor para remover o valor
val editor = sharedPreferences.edit()
editor.remove("username")
// Confirma a remoção
editor.apply()
8.2. Tipos de Dados que Podem Ser Salvos em SharedPreferences
O SharedPreferences no Android é utilizado para armazenar dados simples e persistentes, de forma chave-valor. No entanto, ele é limitado a tipos de dados primitivos. Aqui estão os tipos de dados que podem ser armazenados diretamente no SharedPreferences:
- String: Utilizado para armazenar texto
- Int: Armazena números inteiros.
- Boolean: Usado para armazenar valores verdadeiros ou falsos (booleanos).
- Float: Armazena números com ponto flutuante (números decimais).
- Long: Utilizado para armazenar números inteiros maiores que os permitidos pelo tipo Int (32 bits)
- Set de Strings: Armazena conjuntos de strings (por exemplo, listas de palavras).
editor.putString("username", "JohnDoe")
editor.putInt("userAge", 25)
editor.putBoolean("isLoggedIn", true)
editor.putFloat("userHeight", 1.75f)
editor.putLong("lastLoginTime", System.currentTimeMillis())
editor.putStringSet("userInterests", setOf("Music", "Movies", "Coding"))
Como salvar dados do tipo Double?
Apesar de não existir uma forma específica de salvar dados do tipo Double é possível salvar os dados num formato de bits. E ao fazer a leitura converter esses bits para o valor.
// Obtém o SharedPreferences
val sharedPreferences = getSharedPreferences("AppPrefs", MODE_PRIVATE)
// Abre o editor para salvar
val editor = sharedPreferences.edit()
val price: Double = 2.50
editor.putLong("key", price.toBits())
// Confirma a gravação dos dados
editor.apply()
//Define um valor padrão
val defaultValue: Double = 1.00
//Le os dados em bits
val data = sharedPreferences.getLong("key", defaultValue.toBits())
//convert os bits em um número Double
val valuet = Double.fromBits(data)
8.3. Salvando valor de porcentagem
Neste capítulo, vamos aprender a utilizar o SharedPreferences para salvar a configuração da taxa no aplicativo MiniCalculadora. Quando o usuário fechar o aplicativo, o valor será salvo, e ao reabrir o app, a última configuração será carregada automaticamente. Isso é útil para garantir que as preferências do usuário sejam preservadas, proporcionando uma experiência mais fluida.
Para realizar esse comportamento, vamos operar em dois métodos do ciclo de vida do Android: onStart() e onPause().
Ciclo de Vida do Android: onStart() e onPause()
- onStart(): Esse método é chamado logo após a criação da atividade (ou após ela voltar ao foco), antes que o usuário comece a interagir com a interface. É o local ideal para recuperar dados salvos previamente. No nosso caso, utilizaremos o onStart() para carregar a taxa salva em SharedPreferences e configurar a interface da calculadora com esse valor.
- onPause(): Esse método é chamado quando a atividade está prestes a perder o foco (por exemplo, ao abrir outra atividade ou ao minimizar o aplicativo). É o local ideal para salvar dados, pois você garante que as informações atuais sejam preservadas antes de o app sair do foco. Vamos usá-lo para armazenar o valor da taxa configurado pelo usuário em SharedPreferences.
Método onStart
O melhor momento para ler os dados persistidos, como os salvos em SharedPreferences, é no método onStart(). Isso ocorre porque o onStart() é chamado toda vez que a atividade se torna visível para o usuário, seja após a criação inicial da atividade ou quando ela retorna ao foco depois de ser temporariamente interrompida.
Isso garante que os dados atualizados sejam carregados antes de o usuário interagir com a interface, tornando a experiência de uso mais fluida e consistente. Se você utilizar onCreate() para isso, os dados serão carregados apenas na criação inicial da atividade. No entanto, ao usar onStart(), você garante que, sempre que o usuário voltar ao aplicativo, ele verá os dados mais atualizados.
A implementação deste método acessa o arquivo de preferências compartilhadas padrão do aplicativo para ler um dado do tipo Long. O dado salvo é um valor Double que foi convertido em bits, e utilizamos o método fromBits para realizar a conversão de volta ao seu valor original. Além disso, atualizamos o componente percentTextView para exibir o valor no formato de porcentagem e ajustamos a propriedade progress do percentSeekBar para refletir as alterações realizadas.
override fun onStart() {
super.onStart()
val sharedPref = getPreferences(Context.MODE_PRIVATE)
val defaultValue = 0.15
val savedTip = sharedPref.getLong(getString(R.string.tip_percentage), defaultValue.toBits())
percent = Double.fromBits(savedTip)
//atualiza UI
percentTextView?.text = percentFormat.format(percent)
percentSeekBar?.progress = (percent*100).toInt()
}
Método onPause
O método onPause() é o mais indicado para persistência de dados em muitos casos, porque ele é chamado quando a atividade está prestes a sair do foco, seja temporariamente (como ao abrir outra atividade) ou de forma mais permanente (como ao fechar o app). Ao salvar os dados em onPause(), você garante que as preferências do usuário serão persistidas antes que o aplicativo seja interrompido.
A implementação do método irá acessar o arquivo de preferências compartilhadas padrão do aplicativo e salvar um dado do tipo Long. O dado salvo é o valor da variável double que foi convertido em bits.
override fun onPause() {
super.onPause()
val sharedPref = getPreferences(Context.MODE_PRIVATE)
with (sharedPref.edit()) {
putLong(getString(R.string.tip_percentage), percent.toBits())
apply()
}
}
9. Configurações finais
Neste livro, você criou o aplicativo interativo Mini Calculadora. Discutimos as funcionalidades do aplicativo e, em seguida, você o testou para calcular a gorjeta e o total, com base no valor da conta.
Você construiu a interface gráfica (GUI) do aplicativo usando o editor de layout do Android Studio, a janela Component Tree e a janela de Properties. Além disso, você editou o XML do layout e os arquivos de tema e cores para personalizar as cores primária, primária escura e de destaque do tema DayNight, que foram configuradas pela IDE ao criar o projeto. Apresentamos o código da classe MainActivity, uma subclasse de AppCompatActivity (e uma subclasse indireta de Activity) que define a lógica do aplicativo.
Na interface gráfica do aplicativo, você utilizou um ConstraintLayout para organizar os elementos na tela com suas constraint (restrições) e espaçamento. Exibiu texto em TextViews e recebeu entradas de um EditText e um SeekBar.
A classe MainActivity exigiu diversos recursos de programação orientada a objetos em Kotlin, como classes, objetos, métodos, interfaces e herança. Explicamos o conceito de inflar a GUI a partir do arquivo XML para sua representação visual na tela. Você aprendeu sobre a classe Activity do Android e parte do ciclo de vida de uma atividade. Especificamente, você sobrescreveu o método onCreate para inicializar o aplicativo quando ele é iniciado. No método onCreate, você usou o método findViewById da Activity para obter referências de cada uma das visualizações com as quais o aplicativo interage programaticamente. Definiu uma classe interna anônima que implementa a interface TextWatcher para que o app possa calcular novas gorjetas e totais à medida que o usuário insere o valor da conta no EditText. Também definiu uma classe interna anônima que implementa a interface OnSeekBarChangeListener, permitindo que o app calcule uma nova gorjeta e total conforme o usuário ajusta a porcentagem da gorjeta movendo o controle deslizante do SeekBar.
Você editou o arquivo AndroidManifest.xml para especificar que a MainActivity oferece suporte apenas à orientação retrato e que o teclado deve sempre ser exibido. Também discutimos outros elementos que o Android Studio incluiu no manifesto ao criar o projeto.
Por fim, você incluiu persistência de dados ao aplicativo, com o salvamento de dados simples usando SharedPreferences. Também, aprendemos mais sobre os ciclos de vida de uma atividade, onStart e onPause, e entendemos como a funcionalidade de persistência de dados se encaixa nesse perfil.
10. Referências
Paul Deitel, Harvey Deitel, Abbey Deitel, Michael Morgano. ANDROID para programadores: uma abordagem baseada em aplicativos. Revisão de Daniel Antonio Callegari; Tradução de João Eduardo Nobrega Tortello. Porto Alegre: Bookman, 2012.
LECHETA, Ricardo R. Android essencial com Kotlin. São Paulo: Novatec, 2019.
11. Ficha Técnica
| VERSÃO ATUAL - 2024.2 | |
|---|---|
| Autoria | Bruno Crestani Calegaro |
| Design Instrucional | Bruno Crestani Calegaro |
| Design Multimídia | Bruno Crestani Calegaro |
| Revisão textual e normativa |
Bruno Crestani Calegaro |

Este trabalho está licenciado com uma Licença Creative Commons - Atribuição-NãoComercial-CompartilhaIgual 4.0 Internacional.