Introdução

Neste tutorial, você aprimora o aplicativo DiceRoller do último tutorial e aprende como adicionar e usar recursos de imagem em seu aplicativo. Você também aprenderá sobre a compatibilidade do aplicativo com diferentes versões do Android e como o Android Jetpack pode ajudar.

O que já deveria saber

O que aprenderá

O que fará

Neste tutorial, você cria o aplicativo DiceRoller iniciado no tutorial anterior e adiciona imagens de dados que mudam quando o dado é lançado. O aplicativo DiceRoller final se parece com este:

Se você não trabalhou no último tutorial, pode baixar o aplicativo inicial aqui: DiceRoller.

No final do último tutorial, você possuia um aplicativo que atualiza uma vista de texto com um número entre 1 e 6 cada vez que o usuário toca em um botão. No entanto, o aplicativo é chamado DiceRoller, não 1-6 Number Generator, então seria bom se os dados realmente parecessem dados. Nesta tarefa, você adiciona algumas imagens de dados ao seu aplicativo. Então, em vez de atualizar o texto quando o botão é pressionado, você troca uma imagem diferente para cada resultado da rolagem.

Etapa 1: Add the images

  1. Abra o projeto do aplicativo DiceRoller no Android Studio se ainda não estiver aberto. Se você não trabalhou no último tutorial, pode baixar o aplicativo aqui: DiceRoller.
  2. Na vista Projeto> Android, expanda a pasta res e, em seguida, expanda drawable.



    Seu aplicativo usa muitos recursos diferentes, incluindo imagens e ícones, cores, strings e layouts XML. Todos esses recursos são armazenados na pasta res. A pasta drawable é onde você deve colocar todos os recursos de imagem para seu aplicativo. Já na pasta drawable você pode encontrar os recursos para os ícones do iniciador do aplicativo.
  3. Clique duas vezes em ic_launcher_background.xml. Observe que esses são arquivos XML que descrevem o ícone como uma imagem vetorial. Os vetores permitem que suas imagens sejam desenhadas em muitos tamanhos e resoluções diferentes. As imagens bitmap, como PNG ou GIF, podem precisar ser dimensionadas para dispositivos diferentes, o que pode resultar em alguma perda de qualidade.
  4. Clique em Preview na coluna direita do editor XML para visualizar o drawable vetorial em formato visual.


  5. Baixe as imagens de dados para seu aplicativo em DiceImages.zip. Descompacte o arquivo. Você deve ter uma pasta de arquivos XML parecida com esta:

  1. No Android Studio, clique no menu suspenso na parte superior da vista do projeto que atualmente diz Android e escolha Project. A captura de tela abaixo mostra a estrutura do seu aplicativo no sistema de arquivos.


  2. Expanda DiceRoller > app > src > main > res > drawable.
  3. Arraste todos os arquivos XML individuais da pasta DiceImages para o Android Studio e para a pasta drawable. Clique em OK.
  1. Mude o projeto de volta para a vista Android e observe que seus arquivos XML de imagem de dados estão na pasta drawable.
  2. Clique duas vezes em dice_1.xml e observe o código XML para esta imagem. Clique no botão Preview para obter uma prévia da aparência real desse drawable vetorial.

Etapa 2: Update the layout to use images

Agora que você tem os arquivos de imagem de dados em sua pasta res/drawables, pode acessar esses arquivos a partir do layout e código do seu aplicativo. Nesta etapa, você substitui o TextView que exibe os números por um ImageView para exibir as imagens.

  1. Abra o arquivo de layout activity_main.xml se ainda não estiver aberto. Clique na guia Text para visualizar o código XML do layout.
  2. Exclua o elemento <TextView>.
  3. Adicione um elemento <ImageView> com estes atributos:
<ImageView
   android:id="@+id/dice_image"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="center_horizontal"
   android:src="@drawable/dice_1" />

Use um ImageView para exibir uma imagem em seu layout. O único atributo novo para este elemento é android:src, para indicar o recurso de origem da imagem. Nesse caso, uma fonte de imagem de @drawable/dice_1 significa que o Android deve procurar nos recursos de drawable (res/drawable) para a imagem chamada dice_1.

  1. Clique no botão Preview para pré-visualizar o layout. Deve ser assim:

Etapa 3: Update the code

  1. Abra MainActivity. Esta é a aparência da função rollDice() até agora:
private fun rollDice() {
   val randomInt = (1..6).random()

   val resultText: TextView = findViewById(R.id.result_text)
   resultText.text = randomInt.toString()
}

Observe que a referência a R.id.result_text pode estar destacada em vermelho - isso porque você excluiu TextView do layout e esse ID não existe mais.

  1. Exclua as duas linhas no final da função que definem a variável resultText e defina sua propriedade de texto. Você não está mais usando um TextView no layout, então não precisa de nenhuma das linhas.
  2. Use findViewByID() para obter uma referência à nova ImageView no layout por ID (R.id.dice_image) e atribua essa vista para uma nova variável diceImage:
val diceImage: ImageView = findViewById(R.id.dice_image)
  1. Adicione um bloco when para escolher uma imagem de dado específico com base no valor de randomInteger:
val drawableResource = when (randomInt) {
   1 -> R.drawable.dice_1
   2 -> R.drawable.dice_2
   3 -> R.drawable.dice_3
   4 -> R.drawable.dice_4
   5 -> R.drawable.dice_5
   else -> R.drawable.dice_6
}

Tal como acontece com os IDs, você pode fazer referência às imagens de dados na pasta drawable com os valores na classe R. Aqui, R.drawable se refere à pasta drawable do aplicativo e dice_1 é um recurso de imagem de dado específico dentro dessa pasta.

  1. Atualize a fonte da ImageView com o método setImageResource() e a referência à imagem da matriz que você acabou de encontrar.
diceImage.setImageResource(drawableResource)
  1. Compile e execute o aplicativo. Agora, ao clicar no botão Roll, a imagem deve ser atualizada com a imagem apropriada.

Tudo em seu aplicativo funciona, mas desenvolver aplicativos é mais do que apenas ter um código que funcione. Você também deve entender como escrever aplicativos de bom desempenho e bom comportamento. Isso significa que seus aplicativos devem funcionar bem, mesmo se o usuário não tiver o dispositivo Android mais caro ou a melhor conectividade de rede. Seus aplicativos também devem continuar a funcionar sem problemas à medida que você adiciona mais recursos, e seu código deve ser legível e bem organizado.

Nesta tarefa, você aprenderá uma maneira de tornar seu aplicativo mais eficiente.

  1. Abra MainActivity, se ainda não estiver aberto. No método rollDice(), observe a declaração da variável diceImage:
val diceImage : ImageView = findViewById(R.id.dice_image)

Como rollDice() é o tratador de cliques para o botão Roll, toda vez que o usuário toca nesse botão, seu aplicativo chama findViewById() e obtém outra referência a este ImageView. Idealmente, você deve minimizar o número de chamadas para findViewById(), pois o sistema Android está pesquisando toda a hierarquia de vistas a cada vez, e essa operação é cara.

Em um pequeno aplicativo como este, não é um grande problema. Se você estiver executando um aplicativo mais complicado em um telefone mais lento, chamar continuamente findViewById() pode causar atrasos no aplicativo. Em vez disso, é uma prática recomendada apenas chamar findViewById() uma vez e armazenar o objeto View em um campo. Mantendo a referência à ImageView em um campo permite que o sistema acesse a View diretamente a qualquer momento, o que melhora o desempenho.

  1. No topo da classe, antes de onCreate(), crie um campo para conter o ImageView.
var diceImage : ImageView? = null

Idealmente, você inicializaria essa variável aqui quando ela for declarada ou em um construtor - mas as atividades do Android não usam construtores. Na verdade, as vistas no layout não são objetos acessíveis na memória até que tenham sido infladas no método onCreate(), pela chamada de setContentView(). Você não pode inicializar a variável diceImage até que isso aconteça.

Uma opção é definir a variável diceImage como anulável, como neste exemplo. Defina-o como null quando for declarado e, em seguida, atribua-o ao ImageView real em onCreate() com findViewById(). Isso complicará seu código, no entanto, pois agora você deve verificar o valor null toda vez que quiser usar diceImage. Existe uma maneira melhor.

  1. Altere a declaração diceImage para usar a palavra-chave lateinit e remova a atribuição null:
lateinit var diceImage : ImageView

A palavra-chave lateinit promete ao compilador Kotlin que a variável será inicializada antes que o código chame qualquer operação nela. Portanto, não precisamos inicializar a variável para null aqui, e podemos tratá-la como uma variável não anulável quando a usamos. É uma prática recomendada usar lateinit com campos que mantêm vistas exatamente dessa maneira.

  1. Em onCreate(), após o método setContentView(), use findViewById() para obter o ImageView.
diceImage = findViewById(R.id.dice_image)
  1. Exclua a linha antiga em rollDice() que declara e obtém a ImageView. Você substituiu essa linha pela declaração de campo anterior.
val diceImage : ImageView = findViewById(R.id.dice_image)
  1. Execute o aplicativo novamente para ver se ele ainda funciona conforme o esperado.

Agora você está usando dice_1 como a imagem inicial do dado. Em vez disso, digamos que você não queira exibir nenhuma imagem até que o dado seja lançado pela primeira vez. Existem algumas maneiras de fazer isso.

  1. Abra activity_layout.xml na guia Text.
  2. No elemento <ImageView>, defina o atributo android:src como "@drawable/empty_dice":
android:src="@drawable/empty_dice" 

A imagem empty_dice foi uma das imagens que você baixou e adicionou à pasta drawable. Tem o mesmo tamanho que as outras imagens de dados, mas está vazio. Esta imagem é a que será mostrada quando o aplicativo for iniciado pela primeira vez.

  1. Clique na guia Design. A imagem da matriz está vazia agora, mas também não é visível na prévia.



    É bastante comum que o conteúdo de um design seja definido dinamicamente em tempo de execução - por exemplo, qualquer aplicativo que pegue dados da internet provavelmente deve começar com uma tela em branco ou vazia. Mas é útil quando você está projetando um aplicativo para ter algum tipo de espaço reservado no layout para que você saiba o que está planejando.
  2. Em activity_layout.xml, copie a linha android:src e cole uma segunda cópia. Altere a palavra "android" para "tools", para que seus dois atributos fiquem assim:
android:src="@drawable/empty_dice" 
tools:src="@drawable/empty_dice" />

Aqui, você alterou o namespace XML deste atributo do namespace android padrão para o namespace tools. O namespace tools é usado quando você deseja definir o espaço reservado para o conteúdo do que é usado apenas na prévia ou no editor de design no Android Studio. Os atributos que usam o namespace tools são removidos quando você compila o aplicativo.

Os namespaces são usados ​​para ajudar a resolver ambiguidades ao se referir a atributos que possuem o mesmo nome. Por exemplo, esses dois atributos na etiqueta <ImageView> possuem o mesmo nome (src), mas o namespace é diferente.

  1. Examine o elemento <LinearLayout> na raiz do arquivo de layout e observe os dois namespaces definidos aqui.
<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   ...
  1. Altere o atributo tools:src na etiqueta ImageView para dice_1 em vez de empty_dice:
android:src="@drawable/empty_dice" 
tools:src="@drawable/dice_1" />

Observe que a imagem dice_1 está no lugar agora o espaço reservado para a imagem na prévia.

  1. Compile e execute o aplicativo. Observe que a imagem do dado está vazia no aplicativo real até que você clique ou toque em Roll.

Um dos melhores aspectos sobre o desenvolvimento para Android é o grande número de dispositivos em que seu código pode ser executado - do Nexus One ao Pixel, a fatores de forma como tablets, Pixelbooks, relógios, TVs e carros.

Quando você escreve para Android, você não escreve aplicativos separados para cada um desses diferentes dispositivos - mesmo os aplicativos que são executados em formatos radicalmente diferentes, como relógios e TVs, podem compartilhar código. Mas ainda existem restrições e estratégias de compatibilidade das quais você precisa estar ciente para oferecer suporte a tudo isso.

Nesta tarefa, você aprenderá como direcionar seu aplicativo para níveis específicos de API do Android (versões) e como usar as bibliotecas do Android Jetpack para oferecer suporte a dispositivos mais antigos.

Etapa 1: Explore API levels

No tutorial anterior, quando você criou seu projeto, você indicou o nível de API Android específico que seu aplicativo deve suportar. O sistema operacional Android tem diferentes números de versão com nomes de guloseimas saborosas que estão em ordem alfabética. Cada versão do sistema operacional vem com novos recursos e funcionalidades. Por exemplo, o Android Oreo foi fornecido com suporte para aplicativos Picture-in-picture, enquanto o Android Pie introduziu Slices. Os níveis de API correspondem às versões do Android. Por exemplo, API 19 corresponde ao Android 4.4 (KitKat).

Devido a uma série de fatores, incluindo o que o hardware pode suportar, se os usuários optam por atualizar seus dispositivos e se os fabricantes oferecem suporte a diferentes níveis de sistema operacional, os usuários inevitavelmente acabam com dispositivos que executam diferentes versões de sistema operacional.

Ao criar seu projeto de aplicativo, você especifica o nível mínimo de API que seu aplicativo suporta. Ou seja, você especifica a versão mais antiga do Android compatível com seu aplicativo. Seu aplicativo também tem um nível para o qual é compilado e um nível ao qual se destina. Cada um desses níveis é um parâmetro de configuração nos arquivos de compilação do Gradle.

  1. Expanda a pasta Gradle Scripts e abra o arquivo build.gradle (Module: App).

    Este arquivo define os parâmetros de construção e dependências específicas para o módulo de aplicativo. O arquivo build.gradle (Project: DiceRoller) define os parâmetros de construção para o projeto como um todo. Em muitos casos, seu módulo de app é o único módulo em seu projeto, então essa divista pode parecer arbitrária. Mas se seu aplicativo se tornar mais complexo e você o dividir em várias partes, ou se seu aplicativo suportar plataformas como o Android watch, você poderá encontrar módulos diferentes no mesmo projeto.
  2. Examine a seção android na parte superior do arquivo build.gradle. (O exemplo abaixo não é a seção inteira, mas contém o que você está mais interessado neste tutorial).
android {
   compileSdkVersion 28
   defaultConfig {
       applicationId "com.example.android.diceroller"
       minSdkVersion 19
       targetSdkVersion 28
       versionCode 1
       versionName "1.0"
   }
  1. Examine o parâmetro compileSdkVersion.
compileSdkVersion 28

Este parâmetro especifica o nível de API do Android que o Gradle deve usar para compilar seu aplicativo. Esta é a versão mais recente do Android que seu aplicativo pode suportar. Ou seja, seu aplicativo pode usar os recursos de API incluídos neste nível de API e inferior. Nesse caso, seu aplicativo oferece suporte à API 28, que corresponde ao Android 9 (Pie).

  1. Examine o parâmetro targetSdkVersion, que está dentro da seção defaultConfig:
targetSdkVersion 28

Este valor é a API mais recente com a qual você testou seu aplicativo. Em muitos casos, é o mesmo valor de compileSdkVersion.

  1. Examine o parâmetro minSdkVersion.
minSdkVersion 19

Este parâmetro é o mais importante dos três, pois determina a versão mais antiga do Android na qual seu aplicativo será executado. Dispositivos que executam o sistema operacional Android mais antigo do que este nível de API não podem executar seu aplicativo.

Escolhendo o nível mínimo de API para seu aplicativo pode ser um desafio. Defina o nível da API muito baixo e você perderá os recursos mais recentes do sistema operacional Android. Defina um valor muito alto e seu aplicativo somente poderá ser executado em dispositivos mais novos.

Ao configurar seu projeto e chegar ao ponto de definir o nível mínimo de API para seu aplicativo, clique em Help me choose para ver a caixa de diálogo API Version Distribution A caixa de diálogo fornece informações sobre quantos dispositivos usam diferentes níveis de sistema operacional e recursos que foram adicionados ou alterados nos níveis de sistema operacional. Você também pode verificar as notas de versão da documentação do Android e o painel, que contém mais informações sobre as implicações do suporte a diferentes níveis de API.

Etapa 2: Explore compatibility

Escrevendo para diferentes níveis de API do Android é um desafio comum que os desenvolvedores de aplicativos enfrentam, portanto, a equipe de estrutura do Android fez muito trabalho para ajudá-lo.

Em 2011, a equipe lançou a primeira biblioteca de suporte, uma biblioteca desenvolvida pelo Google que oferece classes compatíveis com versões anteriores e funções úteis. Em 2018, o Google anunciou o Android Jetpack, uma coleção de bibliotecas que inclui muitas das classes e funções anteriores da biblioteca de suporte, além de expandir a biblioteca de suporte.

  1. Abra MainActivity.
  2. Observe que sua classe MainActivity se estende não da própria Activity, mas de AppCompatActivity.
class MainActivity : AppCompatActivity() { 
...

AppCompatActivity é uma classe de compatibilidade que garante que sua atividade tenha a mesma aparência em diferentes níveis de sistema operacional de plataformas.

  1. Clique no símbolo + próximo à linha que começa com import para expandir as importações para sua classe. Observe que a classe AppCompatActivity é importada do pacote androidx.appcompat.app. O namespace para as bibliotecas do Android Jetpack é androidx.
  2. Abra build.gradle (Module: App) e role para baixo até a seção de dependências.
dependencies {
   implementation fileTree(dir: 'libs', include: ['*.jar'])
   implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
   implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
   implementation 'androidx.core:core-ktx:1.0.1'
   implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
   testImplementation 'junit:junit:4.12'
   androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
   androidTestImplementation 
        'androidx.test.espresso:espresso-core:3.1.0-alpha4'
}

Observe a dependência da biblioteca appcompat, que faz parte do androidx e contém a classe AppCompatActivity.

Etapa 3: Add compatibility for vector drawables

Você usará seu novo conhecimento sobre namespaces, Gradle e compatibilidade para fazer um ajuste final em seu aplicativo, o que otimizará o tamanho de seu aplicativo em plataformas mais antigas.

  1. Expanda a pasta res e, em seguida, expanda drawable. Clique duas vezes em uma das imagens de dados.

    Como você aprendeu antes, todas as imagens de dados são, na verdade, arquivos XML que definem as cores e formas dos dados. Esses tipos de arquivos são chamados de drawables vetoriais. O bom dos drawables vetoriais em comparação aos formatos de imagem de bitmap como PNG é que os drawables vetoriais podem ser redimensionados sem perder qualidade. Além disso, um drawable vetorial é geralmente um arquivo muito menor do que a mesma imagem em um formato de bitmap.

    Algo importante a se notar sobre drawables vetoriais é que eles são suportados na API 21 em diante. Mas o SDK mínimo do seu aplicativo é definido como API 19. Se você experimentou seu aplicativo em um dispositivo ou emulador API 19, verá que o aplicativo parece ser compilado e executado perfeitamente. Então, como isso funciona?

    Quando você constrói seu aplicativo, o processo de construção do Gradle gera um arquivo PNG de cada um dos arquivos vetoriais, e esses arquivos PNG são usados ​​em qualquer dispositivo Android abaixo de 21. Esses arquivos PNG extras aumentam o tamanho do seu aplicativo . Aplicativos desnecessariamente grandes não são ótimos - eles tornam os downloads mais lentos para os usuários e ocupam mais do espaço limitado de seus dispositivos. Aplicativos grandes também possuem uma chance maior de serem desinstalados e de usuários não conseguirem fazer o download ou cancelar os downloads desses aplicativos.

    A boa notícia é que existe uma biblioteca de compatibilidade do Android X para drawables vetoriais desde API de nível 7.
  2. Abra build.gradle (Module: App). Adicione esta linha à seção defaultConfig:
vectorDrawables.useSupportLibrary = true
  1. Clique no botão Sync Now. Cada vez que um arquivo build.gradle é modificado, você precisa sincronizar os arquivos de construção com o projeto.
  2. Abra o arquivo de layout activity_main.xml. Adicione este namespace à etiqueta raiz <LinearLayout>, abaixo do namespace tools:
xmlns:app="http://schemas.android.com/apk/res-auto"

O namespace app é para atributos que vêm de seu código customizado ou de bibliotecas e não da estrutura Android principal.

  1. Altere o atributo android:src no elemento <ImageView> para ser app:srcCompat.
app:srcCompat="@drawable/empty_dice"


O atributo app:srcCompat usa a biblioteca Android X para oferecer suporte a drawables vetoriais em versões anteriores do Android, de volta ao nível 7 da API.

  1. Construa e execute seu aplicativo. Você não verá nada diferente na tela, mas agora seu aplicativo não precisa usar arquivos PNG gerados para as imagens dos dados, não importa onde eles sejam executados, o que significa um arquivo de aplicativo menor.

Projeto Android Studio: DiceRollerFinal

Desafio: Modifique o aplicativo DiceRoller para ter dois dados. Quando o usuário toca no botão Roll, cada dado deve ter um valor independente do outro.

Dica: Crie uma função privada para obter uma imagem drawable aleatória e retornar um inteiro para o recurso drawable. Use essa função para cada uma das imagens da matriz.

private fun getRandomDiceImage() : Int { ... }

Código de solução de desafio de programação

Projeto Android Studio: DiceRollerFinal-challenge

Recursos do aplicativo:

Usando drawables vetoriais em vistas de imagens:

A palavra-chave lateinit:

O namespace tools para atributos de tempo de design:

Níveis de API:

Android Jetpack:

Compatibilidade com versões anteriores para drawables vetoriais:

O namespace do app:

Documentação para desenvolvimento em Android:

De outros:

Esta seção lista as possíveis tarefas de casa para os alunos que estão trabalhando neste tutorial como parte de um curso ministrado por um instrutor.

Altere um aplicativo

Adicione um botão Clear ao aplicativo DiceRoller que define a imagem do dado de volta para a imagem vazia.

Responda a essas perguntas

Pergunta 1

Qual atributo <ImageView> indica uma imagem de origem que deve ser usada apenas no Android Studio?

Pergunta 2

Qual método muda o recurso de imagem para um ImageView no código Kotlin? xmx

Pergunta 3

O que a palavra-chave lateinit em uma declaração de variável indica no código Kotlin?

Pergunta 4

Qual configuração do Gradle indica o nível de API mais recente com o qual seu aplicativo foi testado?

Pergunta 5

Você vê uma linha de importação em seu código que começa com androidx. O que isto significa?

Envie seu aplicativo para avaliação

Verifique se o aplicativo tem o seguinte:

Comece a próxima lição: 01.4: Aprenda a se ajudar

Para obter enlaces para outros tutoriais neste curso, consulte a página de destino dos tutoriais Fundamentos de Android em Kotlin.