Primeiras aulas do curso Kotlin parte 2: Mais recursos da linguagem e boas práticas

Kotlin parte 2: Mais recursos da linguagem e boas práticas

Configurando a view do resumo - Introdução

Olá pessoal! Meu nome é Alex Felipe. Sou instrutor da Alura, e estarei apresentando o curso de Android com Kotlin Parte 2. Caso você ainda não tenha feito a primeira parte desse curso, recomendamos fortemente que você a faça antes de começar a parte 2.

Na primeira parte do treinamento, fizemos uma lista de transações. Mas naquela app básica, também era possível calcular quanto as transações rendiam de receita e o quanto as mesmas rendiam de despesa. E então, tínhamos o cálculo total para ter o líquido do nosso financeiro.

Além dessa funcionalidade, temos uma lista que deixamos como fixa no primeiro curso. Entretanto, o ideal é fazer com que ela seja criada dinamicamente, ou seja, fazer com que o usuário tenha as opções tanto de receita quanto de despesa.

Na parte 2 do curso de Kotlin, daremos essa possibilidade ao usuário, de acrescentar receitas de um valor qualquer, settar a data dessa receita, e também definir uma categoria para ela. O mesmo acontecerá com as despesas, onde o usuário também terá a possibilidade de acrescentá-las na lista, de qualquer valor, data e categoria.

Mas pode surgir aquela dúvida: O que vamos aprender de parte técnica do Kotlin em relação a essa lista dinâmica? Basicamente, teremos a capacidade de usar recursos um pouco mais específicos da linguagem, como por exemplo as expressões Lambdas. As Expressões Lambdas estão presentes também no Java 8. Já no Android, a princípio não temos essa possibilidade de usá-las, pois utilizamos o Java 6 ou Java 7.

Com Kotlin, nós temos essa possibilidade. Por mais que estejamos usando o Java 6 ou Java 7.

Além disso, veremos como podemos usar um único objeto, e chamar diversas funções e properties em uma única estrutura.

Ao longo do treinamento da segunda parte do Kotlin, faremos diversos refatoramentos no código. Todas as vezes que implementarmos uma tela ou uma nova funcionalidade, por exemplo, passaremos por diversos processos de refatoração visando um código mais objetivo, e essa é uma das propostas desse curso.

Tentaremos melhorar cada vez mais o código utilizando as features do Kotlin.

Vamos começar?

Configurando a view do resumo - Adicionando as receitas no resumo

Atualmente, a app contém listas de transações: duas de despesa, e outras duas de receita. Mas, se olharmos bem na parte de cima da app, percebemos algumas informações como Receita, Despesa e Total, mas ao lado não há nada dizendo o que elas significam para nós.

A imagem a seguir nos mostra como é a nossa app base. Veja só:

Como podemos ver, temos a lista de transações na parte debaixo, e temos, ainda, uma Despesa, uma Receita e um Total no painel acima. No entanto, há uma diferença: a "Receita" está com o valor de R$ 140,00, a "Despesa" com R$ 20,50, e o "Total" com o resultado de R$ 119,50.

Em outras palavras, a parte do painel superior é justamente um resumo do que está acontecendo na lista abaixo. Mas por que um resumo?

Os R$ 140,00 mostrados significam a soma da Receita de R$ 100,00 mais a Receita Indefinida de R$ 40,00. Como podemos ver também, em Despesa é indicado somente a única transação de despesas de R$ 20,50, e no Total é feita a subtração.

Inclusive, para checarmos esse resultado, adicionaremos mais uma despesa, a fim de realizar outra subtração.

A despesa será no valor de R$ 50,00, e sua categoria será de Contas. Clicamos em "Adicionar". O valor das Despesas no painel superior mudou para o valor de R$ 70,50, ou seja, foi somado o valor da despesa Comida de R$ 20,50 mais o valor da despesa Contas de R$ 50,00.

A app base é calcula e mostra todas as despesas e receitas. E é justamente isso o que vamos implementar nessa parte do curso.

Como podemos indicar essa informação de Resumo?

Já que o Resumo está dentro dessa parte do layout, trabalharemos com a nossa activity.

No Android Studio, dentro do projeto, buscaremos pela ListaTransacoesaActivity com o comando "Ctrl + N".

Dentro dessa activity, como podemos fazer com que essas transações seja somadas? A princípio, precisamos passar por todas as transações e pegar as transações de Receita, e então, fazemos uma soma. Vamos começar com esse processo?

A pergunta agora é a seguinte: Como passaremos por todas as transações no Kotlin?

Uma das técnicas que podemos utilizar é um recurso muito parecido como o do Java, que é a estrutura do For Each. Dentro dele, indicamos que queremos uma transação de transações, no caso.

class ListaTransacoesActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_lista_transacoes)

        val transacoes: List<Transacao> = transacoesDeExemplo()

        // For each no Java
        for(transacao : transacoes){

        }

        configuraLista(transacoes)
    }
}

Porém, há uma estrutura no Kotlin muito parecida com o For Each do Java, porém ela é conhecida como For Loop.

Qual é a diferença entre o For Each e o For Loop?

A diferença é muito simples: trocamos : pela keyword in!

    // For each no Kotlin
    for(transacao in transacoes){

    }

Isso significa que estamos pegando uma transação em transações. Uma outra parte bacana é que no For Each do Java, era necessário colocar o tipo. Mas no Kotlin isso não é necessário, pois já é implícito que a variável transacao é do tipo Transacao.

Bom, vamos primeiro resolver a parte do Resumo. Para conseguirmos filtrar a informação que será mostrada ao lado da Receita, precisamos verificar se de fato, a transação em questão é do tipo Receita.

Essa verificação será feita com um If().

class ListaTransacoesActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_lista_transacoes)

        val transacoes: List<Transacao> = transacoesDeExemplo()

        for(transacao in transacoes){
            if(transacao.tipo == Tipo.RECEITA) {

            }
        }

        configuraLista(transacoes)
    }
}

Agora que já sabemos que uma transacao é uma RECEITA, podemos fazer o seguinte. Em nossa App Base, repare que está sendo somado as transações de "Receita". Então, podemos fazer o mesmo comportamento, o processo de soma!

Para conseguirmos somar todas as transações, precisaremos de uma variável inicial, ou seja, uma variável que comece com zero.

class ListaTransacoesActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_lista_transacoes)

        val transacoes: List<Transacao> = transacoesDeExemplo()

        var totalReceita = BigDecimal.ZERO
        for(transacao in transacoes){
            if(transacao.tipo == Tipo.RECEITA) {

            }
        }

        configuraLista(transacoes)
    }
}

Certo, depois disso podemos pegar o valor de transacao e somar com o valor totalReceita:

    val totalReceita = BigDecimal.ZERO
    for(transacao in transacoes){
        if(transacao.tipo == Tipo.RECEITA) {
            totalReceita.plus(transacao.valor)
        }
    }

Só que temos um detalhe a ser discutido. O BigDecimal é uma classe mutável, em outras palavras, o valor da receita não está sendo alterado pelo plus().

O que acontece é que o plus() está pegando o total da receita e está somando com o valor da transação. E então, ele devolve um novo BigDecimal que tem a soma desses dois valores.

Precisamos então reatribuir esse valor para o totalReceita.

    val totalReceita = BigDecimal.ZERO
    for(transacao in transacoes){
        if(transacao.tipo == Tipo.RECEITA) {
            totalReceita = totalReceita.plus(transacao.valor)
        }
    }

Mas aí, entramos no caso em que estamos alterando a variável totalReceita, sendo ela val. Agora então, podemos mudar para var.

O que é preciso fazer depois da soma?

Basicamente, só temos que mostrar o resultado da soma na view. Atualmente nenhum dado está sendo mostrado nela.

Como podemos colocar essa soma na view? Da mesma maneira, podemos utilizar o componente resumo_card_receita e chamar a property text, e depois atribuir essa informação ao totalReceita.

    var totalReceita = BigDecimal.ZERO
    for(transacao in transacoes){
        if(transacao.tipo == Tipo.RECEITA) {
            totalReceita = totalReceita.plus(transacao.valor)
        }
    }
    resumo_card_receita.text = totalReceita.toString()

Lembre-se que quando colocamos o toString(), estamos pegando o valor sem nenhum tipo de formatação. Então, podemos utilizar a função formataParaBrasileiro().

    var totalReceita = BigDecimal.ZERO
    for(transacao in transacoes){
        if(transacao.tipo == Tipo.RECEITA) {
            totalReceita = totalReceita.plus(transacao.valor)
        }
    }
    resumo_card_receita.text = totalReceita.formataParaBrasileiro()

Vamos ver se funciona? Com o "Alt + Shift + F10" executamos a aplicação.

E olhe só, o valor de 600 reais apareceu na view na frente de Receita. Vamos dar uma olhada em como ficou o código.

Se olharmos o onCreate, é difícil de entender que todo esse código esta adicionando uma receita no Resumo. Em outras palavras, faz sentido usar a técnica que vimos anteriormente, fazer a refatoração extraindo uma função do código.

Selecionando o código parcial acima, e com o "Ctrl + Alt + M", abrimos a janela Extract Function do Android Studio, onde informaremos que essa parte do código que foi selecionada, adiciona uma receita no Resumo.

Podemos chamá-la de adicionaReceitaNoResumo(transacoes). E agora, ao olharmos novamente para o código, fica muito mais fácil de saber que o método adicionaReceitaNoResumo(transacoes) irá, de fato, adicionar uma receita ao Resumo.

Mas perceba que, nós só fizemos a parte da adição da Receita. E agora, começaremos o processo de adicionar uma despesa no próximo vídeo. Até mais!

Configurando a view do resumo - Criando a classe para o componente resumo

Até agora, conseguimos configurar o valor da Receita no Resumo das transações. Mas perceba que aqui, temos uma responsabilidade a mais na activity, onde antes era somente configurar a lista de transações. Agora, ela também é responsável por configurar o Resumo.

Em outras palavras, faz todo o sentido realizar uma refatoração na qual irá delegar toda essa responsabilidade para uma classe específica, a fim de computar as informações do Resumo.

Como podemos mandar uma responsabilidade para outras classe?]

Dentro do projeto, acessaremos "app > java > br.com.alura.financask > ui", e criar uma classe que será responsável por cuidar da View do Resumo. Portanto, usaremos o "Alt + Insert" para criar um arquivo novo, selecionaremos "Kotlin File/Class", e a chamaremos de ResumoView.

Relembrando, essa classe ficará responsável por colocar todas as informações que teremos da View, em relação ao Resumo.

package br.com.alura.financask.ui

class ResumoView {

}

A ideia é copiar o comportamento do adicionaReceitaNoResumo(), e colar dentro da classe ResumoView. Realizamos os imports necessários, e então conseguimos fazer com que o adicionaReceitaNoResumo() fique em uma classe responsável. Portanto, não precisamos mais dele no momento. Podemos apagá-lo de nossa activity.

Em seguida, dentro de onCreate(), faremos uma instância de ResumoView. A partir dessa instância, chamaremos o adicionaReceitaNoResumo().

class ListaTransacoesActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_lista_transacoes)

        val transacoes: List<Transacao> = transacoesDeExemplo()

        ResumoView().adicionaReceitaNoResumo(transacoes)

        configuraLista(transacoes)
    }
}

Não podemos nos esquecer que o método adicionaReceitaNoResumo() está como private. Removemos o modificador desse método na classe ResumoView, e assim a função torna-se pública.

Ainda dentro da classe ResumoView, vamos resolver um problema de compilação. Veja que neste processo, dentro da função adicionaReceitaNoResumo() precisamos acessar o componente resumo_card_receita, ou seja, precisamos fazer uso do Synthetic. Mas, isso não é possível, pois ResumoView não é uma activity que tem o poder de pegar todas as propriedades de um layout e transformar em uma propriedade dela.

Em outras palavras, precisamos de uma referência de uma View, assim como fizemos no adapter.

Então, podemos falar que o ResumoView() irá receber, via construtor, uma property privada que será uma view do tipo View.

class ResumoView(private val view: View) {

}

E agora que temos essa view, podemos chamar o componente que precisamos, apenas importando:

class ResumoView(private val view: View) {

    fun adicionaReceitaNoResumo(transacoes: List<Transacao>) {
        var totalReceita = BigDecimal.ZERO
        for (transacao in transacoes){
            if (transacao.tipo == Tipo.RECEITA) {
                totalReceita = totalReceita.plus(transacao.valor)
            }
        }
        view.resumo_card_receita.text = totalReceita.formataParaBrasileiro()
    }
}

Com o atalho "Ctrl + Alt + O", conseguimos remover os imports que não estão mais sendo usados no projeto.

Recapitulando o que fizemos: para poder pegar o componente resumo_card_receita, declaramos um objeto do tipo View para ter acesso a ele.

Agora, é a vez de ver o que está acontecendo de errado com a activity ListaTransacoesActivity.

Perceba que temos um problema de compilação, pois o ResumoView() exige um objeto do tipo View.

Como podemos pegar um objeto do tipo View aqui na activity?

A activity possui uma property que nos permite pegar uma janela na qual ela representa a aplicação para nós. Bom, então logicamente usaríamos a property chamada window, certo?... Errado!

Esse window que aparece para nós na IDE, é um objeto do tipo Window, e o que precisamos é de uma View! Para pegar uma View do window, podemos pegar a partir de uma "view decorada":

class ListaTransacoesActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_lista_transacoes)

        val transacoes: List<Transacao> = transacoesDeExemplo()
        window.decorView
        ResumoView().adicionaReceitaNoResumo(transacoes)

        configuraLista(transacoes)
    }
}

A funcionalidade representa de fato, a tela da activity, sendo assim uma das formas de se pegar uma view da activity. Inclusive, podemos extraí-la para uma variável, acrescentando .val:

    window.decorView.val

E depois, podemos nomeá-la de view:

class ListaTransacoesActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_lista_transacoes)

        val transacoes: List<Transacao> = transacoesDeExemplo()

        val view = window.decorView
        ResumoView(view).adicionaReceitaNoResumo(transacoes)

        configuraLista(transacoes)
    }
}

Então o decorView nos devolve uma View.

Após essa refatoração, vamos ver se ainda está tudo funcionando? Utilizaremos o comando "Alt + Shift + F10".

Legal, tudo está funcionando perfeitamente. Mas agora estamos delegando toda a responsabilidade para o ResumoView.

Podemos fazer um outro processo de refatoração aqui.

Nas linhas abaixo, estamos configurando o Resumo:

    val view: View = window.decorView
    ResumoView(view).adicionaReceitaNoResumo(transacoes)

Selecionamos essas linhas, e utilizamos o atalho "Ctrl + Alt + N" para extrair uma função, onde que, é dentro dessa função que colocaremos todo o comportamento para configurar a View de Resumo. Colocaremos o nome de configuraResumo.

class ListaTransacoesActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_lista_transacoes)

        val transacoes: List<Transacao> = transacoesDeExemplo()

        configuraResumo(transacoes)

        configuraLista(transacoes)
    }
}

Agora que estamos conseguindo adicionar a receita, podemos começar com o processo de adicionar a despesa. Dentro da ResumoView, criaremos a função adicionaDespesaNoResumo() que ficará responsável por adicionar uma nova Despesa.

class ResumoView(private val view: View) {

    fun adicionaReceitaNoResumo(transacoes: List<Transacao>) {
        var totalReceita = BigDecimal.ZERO
        for (transacao in transacoes){
            if (transacao.tipo == Tipo.RECEITA) {
                totalReceita = totalReceita.plus(transacao.valor)
            }
        }
        view.resumo_card_receita.text = totalReceita.formataParaBrasileiro()
    }

    fun adicionaDespesaNoResumo(transacoes: List<Transacao>) {
        var totalDespesa = BigDecimal.ZERO
        for (transacao in transacoes){
            if (transacao.tipo == Tipo.DESPESA) {
                totalDespesa = totalDespesa.plus(transacao.valor)
            }
        }
        view.resumo_card_despesa.text = totalDespesa.formataParaBrasileiro()
    }
}

Legal, a função para a despesa está criada. Mas, você não concorda que o nome da função não precisa ser adicionaDespesaNoResumo()? Aliás, não é necessário nos métodos referentes a Receita e a Despesa, pois ambas as funções estão adicionando coisas dentro do Resumo, então podemos remover a parte NoResumo do nome desses métodos.

Não teremos problemas em fazer essa mudança no método adicionaDespesa() pois ele acabou de ser criado. Entretanto, não podemos simplesmente alterar o nome do primeiro método para adicionaReceita(), pois a activity utiliza essa função, e então ela irá parar de compilar.

Podemos utilizar um recurso para modificar uma função que já está sendo usada. Com o cursor em cima da função, utilize o comando "Shift + F6", e então podemos remover a parte ...NoResumo. Dessa forma, mantemos a compilação e batemos em todos os pontos que estavam utilizando essa função.

Vamos começar com a lógica!

Como fizemos, ao invés de colocar no componente resumo_card_receita, colocamos no componente resumo_card_despesa.

Repare que ainda não estamos utilizando a função adicionaDespesa() para realmente adicionar uma despesa. Se executarmos o projeto agora, não aparecerá nenhuma despesa no Resumo. Em outras palavras, é necessário chamar essa função na activity e chamá-la.

Pegaremos o objeto do ResumoView(view) para reutilizá-lo.

private fun configuraResumo(transacoes: List<Transacao>) {
    val view: View = window.decorView
    val resumoView = ResumoView(view)
    resumoView.adicionaReceita(transacoes)
    resumoView.adicionaDespesa(transacoes)
}

Vamos ver se está tudo funcionando corretamente, executando o comando "Alt + Shift + F10".

Olhe só:

Além da receita, foi adicionada a despesa no painel superior de Resumo, onde está sendo somado uma despesa indefinida de R$ 200,00 mais uma despesa de almoço de R$ 20,50

Como podemos ver, agora temos um valor referente as Despesas, onde foi somado uma despesa indefinida no valor de R$ 200,00 mais uma outra despesa de almoço no valor de R$ 20,50.

Vamos voltar ao código. Todas as vezes que chamamos a função adicionaReceita() ou adicionaDespesa(), estamos mandando a lista de transações. De fato, os dois métodos estão utilizando a mesma lista. Então, faz todo o sentido enviarmos essa lista no construtor primário. Dessa forma, ele será transformado em uma property que será acessível para todos os métodos, eliminando ter que enviar essa lista como um parâmetro todas as vezes que queremos adicionar uma Receita ou Despesa.

class ResumoView(private val view: View,
                 private val transacoes: List<Transacao>) {

    fun adicionaReceita() {
        // lógica do for each
    }

    fun adicionaDespesa() {
        // lógica do for each
    }
}

Assim feito, podemos apagar o parâmetro transacoes: List<Transacao> dos métodos da Receita e da Despesa. A partir de agora, as transações serão baseadas na nova property.

Ao voltarmos para a activity, vemos alguns problemas de compilação. Isso ocorre, pois é preciso passar transacoes como parâmetro de ResumoView.

class ListaTransacoesActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_lista_transacoes)

        val transacoes: List<Transacao> = transacoesDeExemplo()

        configuraResumo(transacoes)

        configuraLista(transacoes)
    }

    private fun configuraResumo(transacoes: List<Transacao>) {
        val view: View = window.decorView
        val resumoView = ResumoView(view, transacoes)
        resumoView.adicionaReceita()
        resumoView.adicionaDespesa()
    }
}

Testaremos o código agora com "Alt + Shift + F10".

As informações foram mantidas, e isso nos diz que o código está funcionando corretamente. Com isso, conseguimos adicionar tanto a Receita quanto a Despesa de uma maneira bem mais elegante.

Sobre o curso Kotlin parte 2: Mais recursos da linguagem e boas práticas

O curso Kotlin parte 2: Mais recursos da linguagem e boas práticas possui 219 minutos de vídeos, em um total de 58 atividades. Gostou? Conheça nossos outros cursos de Android em Mobile, ou leia nossos artigos de Mobile.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Android acessando integralmente esse e outros cursos, comece hoje!

  • 1056 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

Premium

  • 1056 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$75
à vista R$900
Matricule-se

Premium Plus

  • 1056 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$100
à vista R$1.200
Matricule-se

Max

  • 1056 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$120
à vista R$1.440
Matricule-se
Procurando planos para empresas?
Acesso por 1 ano
Estude 24h/dia onde e quando quiser
Novos cursos toda semana