Keddit – Parte 3: Funções de extensão, extensões Android e mais …

Tempo de leitura: 5 minutes

Parte 3: NewsFragment.kt: Função de extensões, extensões Android e mais …

Nesta parte, veremos diferentes conceitos do Kotlin que nos ajudarão a criar nosso fragmento NewsFragment. No final desta história, você aprenderá:

  • Funções de extensão (Utility class?)
  • Valores padrão em parâmetros
  • Extensões Android (bind view)
  • Delegated Propriedades

Crie nosso NewsFragment.kt

Vamos criar nosso arquivo NewsFragment.kt, ele será responsável por mostrar as últimas novidades da API do Reddit e vamos usar um RecyclerView para mostrar as novidades.

Em Java, normalmente criaríamos um campo privado para armazenar o RecyclerView localmente e atribuí-lo quando estivermos aumentando a visualização. Tentando fazer o mesmo em Kotlin, será assim:

private var newsList: RecyclerView? = null

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    val view = inflater.inflate(R.layout.news_fragment, container, false)
    newsList = view.findViewById(R.id.news_list) as RecyclerView?
    newsList?.setHasFixedSize(true) // use esta configuração para melhorar o desempenho
    newsList?.layoutManager = LinearLayoutManager(context)

    return view
}

Este é um código válido e funciona, mas! não é um código “Kotlinizado”, então vamos torná-lo melhor. Com este pequeno trecho de código, vamos incorporar alguns novos conceitos sobre a linguagem:

  • Funções de extensão
  • Valores padrão em parâmetros
  • Extensões Android (bind view)
  • Delegated Propriedades

 

Funções de extensão (Utility class?)

As funções de extensão nos permitem estender a funcionalidade de uma classe adicionando novas funções. A classe não precisa pertencer a nós (poderia ser uma biblioteca de terceiros) e também sem exigir que herdemos a classe.

Este é realmente um recurso super poderoso! Como poderemos adicionar novas funções às classes existentes e é isso que veremos agora com a classe ViewGroup, um grande truque que encontrei neste link de Antonio Leiva que realmente encorajo você a segui-lo no Twitter. Também vamos entender como funciona e como consumi-lo do Java.

Como você sabe, ViewGroup é uma classe do Android SDK e, para aumentá-la, temos que fazer o seguinte:

inflater.inflate(R.layout.news_fragment, container, false)

Mas essa não é realmente uma maneira intuitiva de fazer isso. Isso deve ser algo que o ViewGroup deve ser capaz de fazer assim:

val view = container?.inflate(R.layout.news_fragment)

É como se o ViewGroup pudesse se inflar sozinho! Truque incrível! Mas como fazer isso? Vamos criar nossa primeira Função de Extensão:

Criei um arquivo chamado “Extensions.kt” no pacote “commons”. Verifique o código para vê-lo. E o código é assim:

fun ViewGroup.inflate(layoutId: Int): View {
    return LayoutInflater.from(context).inflate(layoutId, this, false)
}

O que estamos fazendo aqui é adicionar um novo método ao ViewGroup (veja como adicionamos o ViewGroup com um ponto antes do nome do método “inflate”), mas não estamos modificando a classe ViewGroup, mas adicionando uma nova função. Esta função será internamente um método estático, mas você a chamará de uma instância de uma classe com a notação de ponto, neste caso: container.inflate (…) e não ViewGroup.inflate (). Isso ocorre porque o compilador criará uma classe Util para nós. Se quiser usar esta função de extensão do Java, você a usará desta forma:

// Java
ExtensionsKt.inflate(container, R.layout.news_fragment);

// Kotlin
container?.inflate(R.layout.news_fragment)

No mundo Kotlin, é da maneira mais conveniente. Lembre-se de que estamos adicionando o “?” marcar apenas porque em nosso contêiner de exemplo poderia ser um objeto nulo e isso nos impede de obter uma NullPointerException.

O nome da classe Utility será igual ao nome do arquivo mais o sufixo “Kt” ou você pode substituí-lo por uma anotação específica:

@file:JvmName("ExtensionsUtils")

package br.capsistema.kedditporpassos.commons

import ...

fun ViewGroup.inflate(layoutId: Int): View {
    ...
}

// Use-o desta forma em Java:
ExtensionsUtils.inflate(container, R.layout.news_fragment);

Mais detalhes sobre interoperabilidade podem ser encontrados aqui.

Voltando à nossa função:

fun ViewGroup.inflate(layoutId: Int): View {
    return LayoutInflater.from(context).inflate(layoutId, this, false)
}

Dentro do bloco de código é como se você estivesse escrevendo código, pois é um método novo real da classe (como um método regular) é por isso que você pode acessar a instância da classe com a palavra-chave “this” e acessar a variável local “context“.

Excelente! Esta é a nossa primeira função de extensão e sabemos muito mais sobre este conceito. Nosso código foi atualizado desta forma:

// old code:
val view = inflater.inflate(R.layout.news_fragment, container, false)

// replaced with:
val view = container?.inflate(R.layout.news_fragment)

Mas aqui estamos perdendo nosso parâmetro attachToRoot que temos no método inflate inflater. Vamos adicionar isso ao nosso código.

 

Valores padrão em parâmetros

No Kotlin, você pode definir valores padrão nos parâmetros de uma função (também em um construtor de classe, mas não veremos isso agora). Portanto, vamos adicionar um valor padrão para o parâmetro attachToRoot:

fun ViewGroup.inflate(layoutId: Int, attachToRoot: Boolean = false): View {
    return LayoutInflater.from(context).inflate(layoutId, this, attachToRoot)
}

Caso você não especifique o parâmetro attachToRoot, ele assumirá o valor padrão, então você pode chamá-lo de diferentes maneiras:

container?.inflate(R.layout.news_fragment) // default: false
container?.inflate(R.layout.news_fragment, true)

 

Extensões Android

Esta é a alternativa Kotlin para o famoso método “findViewById()” (ou outras bibliotecas de terceiros para vincular propriedades a um elemento de uma visualização). As extensões do Android adicionam algumas propriedades de extensão convenientes que nos permitem acessar os elementos de uma visualização, pois é uma propriedade dentro de nossa Atividade ou Fragmento com o tipo de visualização adequado já definido.

Antes de começar a alterar nosso código, vamos configurar nosso projeto para habilitar extensões Android. Modifique o arquivo build.gradle de nosso módulo de aplicativo, aplique o plug-in kotlin-android-extensions e sincronize novamente com o gradle:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

Isso é tudo! Vamos substituir o findViewById por este.

Layout NewsFragment:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/news_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

Classe NewsFragment:

Poderemos acessar o news_list RecyclerView de nosso fragmento NewsFragment. Vamos incluir uma importação para as propriedades sintéticas geradas pelo plug-in:

import kotlinx.android.synthetic.main.news_fragment.*

Desta forma, você tem acesso a todos os elementos do layout que você escolhe nesta importação e os usa diretamente do seu código. Para ter certeza de que o layout já foi inflado, vamos mover nossa atribuição newsList do método onViewCreated() para onActivityCreated(), onde temos certeza de que a visualização (news_fragment) já foi inflada.

Vamos poder acessar news_list diretamente agora:

// old code:
newsList = view?.findViewById(R.id.news_list) as RecyclerView?
newsList?.setHasFixedSize(true)
newsList?.layoutManager = LinearLayoutManager(context)

// new code:
news_list.setHasFixedSize(true)
news_list.layoutManager = LinearLayoutManager(context)
Notice that we use news_lists and it’s a non-nullable object so we can

Observe que usamos news_lists e é um objeto não anulável para que possamos usá-lo em nosso código sem o “?” ponto de interrogação. Isso pode ser um problema se você executar este código em outra parte do ciclo de vida da atividade e a visualização não tiver sido inflada anteriormente, isso lançará uma exceção no tempo de execução.

Bem, nosso código está ficando melhor! Mas espere! E se quisermos ter news_list localmente para realizar algumas outras ações, como configurar o adaptador?

Com certeza, você ainda pode usar a propriedade estendida news_list, mas para o propósito deste tutorial, veremos Delegated Properties que nos ajudarão a fazer isso.

 

Propriedades delegadas são uma excelente maneira de reutilizar o comportamento comum que pode ser necessário para uma propriedade. No Kotlin, você já tem algumas propriedades delegadas comuns definidas na linguagem (também pode criar as suas próprias). Aqui, vamos usar esta propriedade delegada existente:

  • Lazy properties: O valor é calculado apenas no primeiro acesso.

Este tópico é realmente um mundo, então aqui vamos ver apenas a propriedade preguiçosa e, em outras partes, continuaremos introduzindo outros conceitos sobre Delegated Properties.

Esta é uma ótima propriedade delegada que usaremos para evitar a inicialização de nossa newsList como um objeto anulável. Com o Lazy, vamos criá-lo como uma propriedade não anulável e será executado apenas quando você o usar e apenas na primeira vez.

Lazy inicializará newsList com o valor que você executa no bloco de código:

private val newsList: RecyclerView by lazy {
    view?.findViewById(R.id.news_list) as RecyclerView
}

Aqui, podemos usar a propriedade sintética news_list para evitar o uso de findViewById e, como o tipo pode ser inferido pelo contexto, também podemos remover o tipo de propriedade:

private val newsList by lazy {
    news_list
}

O bloco lento será executado quando o usarmos, neste caso no método onActivityCreated():

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)

    newsList.setHasFixedSize(true) // <-- Lazy executed!
    newsList.layoutManager = LinearLayoutManager(context)
}

Além disso, poderíamos mover esse código de inicialização dentro de onActivityCreated para o bloco preguiçoso desta maneira:

private val newsList by lazy {
    news_list.setHasFixedSize(true)
    news_list.layoutManager = LinearLayoutManager(context)
    news_list // this will work as the return type
}

Mas, como não estamos chamando a newsList em nenhum lugar, o RecyclerView levantará uma exceção, pois não tem um gerenciador de layout definido. Então, vamos deixá-lo como tínhamos anteriormente, mas vamos alterá-lo desta forma mais tarde.

 

Conclusão

Bem, estamos prontos para começar a desenvolver o Adaptador na próxima história. Agora temos um novo fragmento criado com Kotlin 🙂

Twitter: https://twitter.com/caneto

Artigo seguinte
Parte 4: RecyclerView – Delegar adaptadores e classes de dados com Kotlin