Construindo o Formulário de Atualização de Perfil - Android M-Commerce
(3478)
CategoriasAndroid, Design, Protótipo
AutorVinÃcius Thiengo
VÃdeo aulas186
Tempo15 horas
ExercÃciosSim
CertificadoSim
CategoriaEngenharia de Software
Autor(es)Mauricio Aniche
EditoraCasa do Código
Edição1
Ano2012
Páginas194
Tudo bem?
Neste artigo vamos a construção da tela de atualização de dados de perfil de usuário, tela do projeto Android mobile-commerce, app BlueShoes.
O desenvolvimento será simples, principalmente devido a quantidade de códigos já encapsulados em outras aulas, mas também teremos de enfrentar um problema em relação a necessidade de mantermos alguns códigos repetidos devido a atualizações em layout que devem ocorrer em código dinâmico.
Antes de continuar, não deixe de se inscrever 📫na lista de emails do Blog para ter acesso exclusivo às novas aulas do projeto e também a outros conteúdos restritos somente ao Blog.
A seguir os tópicos abordados:
- Conhecendo agora no projeto Android mobile-commerce;
- Estratégia para a tela de atualização de perfil:
- Trabalhando a atividade de dados de perfil:
- Atualizando as entidades de configuração de conta:
- Testes e resultados;
- Vídeos;
- Conclusão;
- Fontes.
Conhecendo agora no projeto Android mobile-commerce
Você chegou ao projeto Android mobile-commerce somente agora? Então não deixe de primeiro conferir todas as aulas anteriores a esta nova aula.
Antes de lhe apresentar os links das aulas, saiba que nesta primeira parte do projeto o nosso foco é na construção de toda a interface gráfica do aplicativo.
Seguem as aulas que devem ser consumidas antes desta 13ª aula:
- 1ª aula - Android Mobile-Commerce, Apresentação e Protótipo do Projeto;
- 2ª aula - Início de Projeto e Menu Gaveta Customizado - Android M-Commerce;
- 3ª aula - Fragmento da Tela Sobre e Links Sociais - Android M-Commerce;
- 4ª aula - Localização com Rota GPS, E-mail e Telefones - Android M-Commerce;
- 5ª aula - Políticas de Privacidade e Porque não a GDPR - Android M-Commerce;
- 6ª aula - Login com ConstraintLayout e TextWatcher Para Validação - Android M-Commerce;
- 7ª aula - Refatoração do Login, Pavimentando o Caminho Para Outros Formulários - Android M-Commerce;
- 8ª aula - Como Criar a Tela de Recuperação de Acesso - Android M-Commerce;
- 9ª aula - Criando a Tela de Cadastro de Usuário - Android M-Commerce;
- 10ª aula - Aplicando o Padrão Template Method Para Limpar o Login e o Cadastro - Android M-Commerce;
- 11ª aula - Como Criar a UI de Configurações de Conta de Usuário - Android M-Commerce;
- 12ª aula - Entendendo o Bug do Menu, Link de Cadastro e Parcelize - Android M-Commerce.
Lembrando que as aulas são liberadas semanalmente para a lista de e-mails 📩 do Blog, logo, não esqueça de se inscrever nela, é gratuito.
Estratégia para a tela de atualização de perfil
Como já definido em outras aulas, incluindo as aulas de refatoração de código, vamos iniciar o desenvolvimento da tela de atualização de dados de perfil pelos códigos estáticos, códigos XML. Segue roteiro:
- Primeiro vamos ao desenvolvimento de todos os códigos estáticos, da tela de atualização perfil, que são independentes de códigos dinâmicos;
- Depois vamos ao desenvolvimento dos códigos dinâmicos da atividade de atualização de dados de perfil;
- Por fim vamos às atualizações de códigos de outras entidades que permitem o acesso a atividade de perfil.
Antes de prosseguir, o projeto Android BlueShoes está por completo disponível no repositório dele em: https://github.com/viniciusthiengo/blueshoes-kotlin-android.
Protótipo estático
A seguir o protótipo estático da tela de atualização de dados de perfil de usuário:
Formulário de perfil | Load - atualização em back-end Web |
Erro na atualização | Atualização bem sucedida |
Trabalhando a atividade de dados de perfil
Como informado anteriormente: vamos iniciar pelos códigos estáticos e assim seguir para os códigos dinâmicos, códigos Kotlin.
Arquivo de Strings
Nossa primeira atualização é no arquivo de Strings, com dados que são passíveis de serem obtidos diretamente do protótipo estático do app BlueShoes.
Em /res/values/strings.xml adicione os códigos em destaque:
<resources>
...
<!-- ConfigProfileActivity -->
<string name="title_activity_config_profile">
Perfil
</string>
<string name="info_profile_photo">
Toque na foto para abrir a galeria ou fotografia.
</string>
<string name="name_label">Nome:</string>
<string name="config_profile">Atualizar perfil</string>
<string name="config_profile_going">Atualizando…</string>
<string name="invalid_name">
Nome inválido.
</string>
<string name="invalid_config_profile">
Um nome válido deve ser fornecido
</string>
</resources>
Drawable para o campo de imagem de perfil
O campo para mudança de imagem de perfil na verdade é apenas um ImageView com um ícone centralizado e um background personalizado:
Para o background personalizado, que por sinal é bem simples, no folder /res/drawable crie o arquivo XML bg_image_view_profile.xml com o código a seguir:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<!--
Definindo a cor de backgrund da View alvo.
-->
<solid android:color="@color/colorTextBox" />
<!--
Definindo bordas de 1dp de espessura e na
cor cinza escuro.
-->
<stroke
android:color="@color/colorViewLine"
android:width="1dp" />
</shape>
Note que as referências de cores são pouco genéricas até o momento, pois elas foram criadas em outros contextos. Por exemplo: o cinza escuro de borda está com o rótulo colorViewLine. A cor de fundo está com o rótulo colorTextBox.
Posteriormente, em novas refatorações, vamos corrigir também este ponto em projeto, pois querendo ou não isso atrapalha a leitura do código por parte de outros desenvolvedores, leitura dos futuros developers do app.
Ícone para o campo de imagem de perfil
O ícone, diferente de muitos outros já utilizados em projeto, foi baixado diretamente do protótipo estático no MarvelApp:
Sendo assim, a melhor maneira de te-lo, exatamente como em protótipo, é baixa-lo direto do repositório e então coloca-lo em sua versão de projeto, dentro de sua instalação do Android Studio.
A seguir os links para download do ícone de campo de imagem de perfil:
- /res/drawable-mdpi/ic_photo_black_24dp.png;
- /res/drawable-hdpi/ic_photo_black_24dp.png;
- /res/drawable-xhdpi/ic_photo_black_24dp.png;
- /res/drawable-xxhdpi/ic_photo_black_24dp.png.
Não esqueça de coloca-los em seus respectivos folders drawable.
Ok, Thiengo. Mas enquanto você não utiliza o VectorDrawable como você fez para obter essa imagem nas versões drawable necessárias dela?
- Primeiro temos de realizar o download da imagem pelo MarvelApp (como apresentado na imagem anterior);
- Depois é carrega-la no Android Asset Studio, mais precisamente em Generic icon generator, e assim colocar as configurações:
- Trim whitespace: Trim;
- Padding: 0%;
- Asset size: 24dp;
- Asset padding: 0dp;
- Color: Black;
- Name: ic_photo_black_24dp.
- Por fim realizar o download do pacote.
Se você quiser prosseguir com outro ícone, até mesmo algum do Material Design Icons, sem problemas quanto a isso. Aqui estou buscando ao máximo ter um projeto final igual ao projeto definido em protótipo estático.
Layout do formulário de perfil
Como estamos trabalhando com uma atividade de formulário, é evidente que estaremos reaproveitando inúmeros códigos já criados em outras aulas de telas com formulários.
Sendo assim é certo que a atividade FormActivity estará sim na hierarquia da atividade de atualização de dados de perfil de usuário.
Com isso, temos de desenvolver somente um layout principal para a nossa nova tela que contém o formulário de configurações de perfil.
Em /res/layout crie o XML content_config_profile.xml com o código a seguir:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="36dp">
<ImageView
android:id="@+id/iv_profile"
android:layout_width="108dp"
android:layout_height="108dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:src="@drawable/ic_photo_black_24dp"
android:tint="@color/colorLightGrey"
android:scaleType="centerInside"
android:background="@drawable/bg_image_view_profile"
android:onClick="callGallery" />
<TextView
android:id="@+id/tv_photo_info"
style="@style/TextViewOrangeInfo"
android:layout_width="90dp"
app:layout_constraintTop_toTopOf="@+id/iv_profile"
app:layout_constraintBottom_toBottomOf="@+id/iv_profile"
app:layout_constraintLeft_toRightOf="@+id/iv_profile"
android:maxLines="10"
android:text="@string/info_profile_photo" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/et_name"
app:layout_constraintLeft_toLeftOf="@+id/et_name"
android:text="@string/name_label" />
<EditText
android:id="@+id/et_name"
style="@style/EditTextFormField"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:background="@drawable/bg_form_field"
android:inputType="textPersonName"
android:imeOptions="actionDone" />
<Button
android:id="@+id/bt_send_profile"
style="@style/ButtonForm"
app:layout_constraintTop_toBottomOf="@+id/et_name"
app:layout_constraintRight_toRightOf="@+id/et_name"
android:text="@string/config_profile"
android:onClick="mainAction" />
</android.support.constraint.ConstraintLayout>
Diferente de outros formulário, desta vez não teremos hint (placeholder) no EditText em tela, isso, pois o campo de ID et_name sempre terá um valor e assim o rótulo acima do campo facilita ao usuário o entendimento de para que esse campo é utilizado.
No primeiro TextView do XML anterior tivemos de definir android:maxLines="10", pois o estilo TextViewOrangeInfo tem a definição de maxLines sendo de uma linha. Qualquer valor igual ou maior do que 10 é seguro para nós, pois o texto presente neste TextView é longo e terá quebras de linhas - nunca igual ou maior do que 10 linhas.
Note também que já adiantamos aqui alguns pontos do código dinâmico da atividade de atualização de dados de perfil, pois já foram definidos todos os atributos android:onClick. Não há problemas quanto a isso.
A seguir o diagrama do layout anterior:
Criando a ConfigProfileActivity
A atividade de atualização de dados de perfil será bem simples. Ao invés de seguir todo o conhecido protocolo de criação de atividade, isso pelo menu de contexto do Android Studio, vamos apenas copiar e colar, no mesmo pacote, a atividade ForgotPasswordActivity.
Porém coloque como nome da nova atividade: ConfigProfileActivity.
O código inicial da ConfigProfileActivity será como a seguir:
class ConfigProfileActivity :
FormActivity() {
override fun onCreate( savedInstanceState: Bundle? ) {
super.onCreate( savedInstanceState )
et_name.validate(
{
it.length > 1
},
getString( R.string.invalid_name )
)
et_name.setOnEditorActionListener( this )
/*
* Name é um dos dados de banco de dados, e campo de
* formulário, que nunca poderá estar vazio.
* */
val user = intent.getParcelableExtra<User>( User.KEY )
et_name.setText( user.name )
}
override fun getLayoutResourceID()
= R.layout.content_config_profile
override fun backEndFakeDelay(){
backEndFakeDelay(
false,
getString( R.string.invalid_config_profile )
)
}
override fun blockFields( status: Boolean ){
iv_profile.isEnabled = !status
et_name.isEnabled = !status
bt_send_profile.isEnabled = !status
}
override fun isMainButtonSending( status: Boolean ){
bt_send_profile.text =
if( status )
getString( R.string.config_profile_going )
else
getString( R.string.config_profile )
}
fun callGallery( view: View ){
Toast
.makeText( this, "TODO", Toast.LENGTH_SHORT )
.show()
}
}
Primeiro, note no onCreate() que será necessário o envio do objeto user também para a ConfigProfileActivity. Tendo em mente que ele já está presente em AccountSettingsActivity.
Todo o restante do código é bem simples, com métodos obrigatórios sendo implementados, métodos encapsulados e isolados em aulas anteriores de nosso projeto.
O método listener de clique no campo de imagem, callGallery(), já está pré-implementado (terá uma aula somente sobre a galeria de imagens, incluindo a possibilidade de tirar fotografias).
Mas ainda há um problema nesta tela.
O problema de campo sobrepondo outro campo
É isso mesmo, novamente o problema de sobreposição de campos de formulário, problema que "deveria" ter sido solucionado de uma vez por todas.
Se já estivéssemos com todas as configurações desta aula finalizadas, exceto o algoritmo de correção do problema de sobreposição de campos, teríamos na tela de atualização de dados de usuário:
Ai você pergunta: mas Thiengo, nós já temos um algoritmo em FormEmailAndPasswordActivity que resolve este problema certo?
Quase certo!
O algoritmo em FormEmailAndPasswordActivity é específico para um TextView de políticas de privacidade.
Nosso foco agora é no ImageView que representa o campo de imagem de perfil, pois ele é o que exigirá menos alteração em código dinâmico para mantermos a tela, independente do status dela, com a configuração correta de acordo com o que foi definido em protótipo estático.
Sendo assim a nossa melhor alternativa agora é reproduzir todo o código de atualização de constraints já presente em FormEmailAndPasswordActivity, em LoginActivity e em SignUpActivity, reproduzir esse código com alterações pontuais para então, em futuras refatorações, ser possível melhorarmos todo o script com facilidade.
Correção da sobreposição de campos
Em ConfigProfileActivity vamos primeiro adicionar todo o código listener de mudança de status de teclado virtual:
class ConfigProfileActivity :
FormActivity(),
KeyboardUtils.OnSoftInputChangedListener {
override fun onCreate( ... ) {
super.onCreate( ... )
KeyboardUtils.registerSoftInputChangedListener(
this,
this
)
...
}
...
override fun onDestroy() {
KeyboardUtils.unregisterSoftInputChangedListener( this )
super.onDestroy()
}
override fun onSoftInputChanged( height: Int ) {
/* TODO */
}
}
Para os códigos de atualização direta nas configurações do ImageView de campo de imagem de perfil, vamos tentar manter o máximo possível igual aos códigos já isolados em FormEmailAndPasswordActivity, pois esse comportamento agora provavelmente nos ajudará em novas refatorações.
Ainda em ConfigProfileActivity acrescente o código em destaque:
class ConfigProfileActivity :
FormActivity(),
KeyboardUtils.OnSoftInputChangedListener {
...
override fun onSoftInputChanged( ... ) {
if( isAbleToCallChangeTargetViewConstraints() ){
changeTargetViewConstraints(
KeyboardUtils.isSoftInputVisible( this )
)
}
}
private fun isAbleToCallChangeTargetViewConstraints()
= true
private fun changeTargetViewConstraints(
isKeyBoardOpened: Boolean
){
val photoProfileId = iv_profile.id
val parent = iv_profile.parent as ConstraintLayout
val constraintSet = ConstraintSet()
val size = (108 * ScreenUtils.getScreenDensity()).toInt()
/*
* Definindo a largura e a altura da View em
* mudança de constraints, caso contrário ela
* fica com largura e altura em 0dp.
* */
constraintSet.constrainWidth(
photoProfileId,
size
)
constraintSet.constrainHeight(
photoProfileId,
size
)
/*
* Centralizando a View horizontalmente no
* ConstraintLayout.
* */
constraintSet.centerHorizontally(
photoProfileId,
ConstraintLayout.LayoutParams.PARENT_ID
)
if( isConstraintToSiblingView( isKeyBoardOpened ) ){
setConstraintsRelativeToSiblingView( constraintSet, photoProfileId )
}
else{
constraintSet.connect(
photoProfileId,
ConstraintLayout.LayoutParams.TOP,
ConstraintLayout.LayoutParams.PARENT_ID,
ConstraintLayout.LayoutParams.TOP
)
}
constraintSet.applyTo( parent )
}
private fun isConstraintToSiblingView(
isKeyBoardOpened: Boolean
): Boolean {
return isKeyBoardOpened || ScreenUtils.isLandscape()
}
private fun setConstraintsRelativeToSiblingView(
constraintSet: ConstraintSet,
targetViewId: Int
) {
constraintSet.connect(
targetViewId,
ConstraintLayout.LayoutParams.BOTTOM,
tv_name.id,
ConstraintLayout.LayoutParams.TOP,
(30 * ScreenUtils.getScreenDensity()).toInt()
)
}
}
Problema de sobreposição de campos: resolvido, com muito código repetido, mas resolvido.
Atualização no AndroidManifest
Ainda é preciso adicionarmos a nova atividade ao AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest
...>
<application
...>
...
<activity
android:name=".view.ConfigProfileActivity"
android:label="@string/title_activity_config_profile"
android:theme="@style/AppTheme.NoActionBar"/>
</application>
</manifest>
Atualizando as entidades de configuração de conta
Vamos agora a atualizações necessárias em projeto para que seja possível acessar a nossa nova tela de atualização de dados de perfil de usuário.
Ops! Configuração atual do Gradle Nível de Projeto
Antes de iniciarmos as atualizações em entidades diretamente ligadas a tela de configuração perfil, vamos primeiro ver como está a configuração atual do Gradle Nível de Projeto, ou build.gradle (Project: BlueShoes), do aplicativo:
buildscript {
ext.kotlin_version = '1.3.31'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
mavenCentral() /* Para acesso a API RoundedImageView */
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Se necessário em seu caso, atualize ext.kotlin_version = '1.3.31' e classpath 'com.android.tools.build:gradle:3.4.1' e então sincronize o projeto. Todas as APIs em uso continuarão funcionando sem problemas.
Ampliando a AccountSettingItem
Nossa classe de domínio responsável por representar os itens da tela de configurações de conta tem de passar por uma atualização, simples, mas necessária, pois será em cada um dos objetos desta classe que também haverá a referência a atividade que deverá ser aberta quando houver o acionamento do item.
Em AccountSettingItem adicione o trecho em destaque:
class AccountSettingItem(
val label: String,
val description: String,
val activityClass: Class<out FormActivity>
)
Agora em AccountSettingItem podemos passar como terceiro argumento valores no seguinte formato: ConfigProfileActivity::class.java. Exatamente como esperamos no segundo argumento de Intent para invocarmos o startActivity() - ainda nesta aula chegaremos a esta parte.
O <out FormActivity> nos permite passar como argumento qualquer tipo Class de uma subclasse de FormActivity.
Corrigindo a AccountSettingsItemsDataBase
Agora é preciso corrigir as inicializações de objetos AccountSettingItem em AccountSettingsItemsDataBase:
class AccountSettingsItemsDataBase {
companion object{
fun getItems( context: Context )
= listOf(
AccountSettingItem(
context.getString( R.string.setting_item_profile ),
context.getString( R.string.setting_item_profile_desc ),
ConfigProfileActivity::class.java
),
AccountSettingItem(
context.getString( R.string.setting_item_login ),
context.getString( R.string.setting_item_login_desc ),
ConfigProfileActivity::class.java
),
AccountSettingItem(
context.getString( R.string.setting_item_address ),
context.getString( R.string.setting_item_address_desc ),
ConfigProfileActivity::class.java
),
AccountSettingItem(
context.getString( R.string.setting_item_credit_cards ),
context.getString( R.string.setting_item_credit_cards_desc ),
ConfigProfileActivity::class.java
)
)
}
}
Vamos prosseguir nesta aula sempre utilizando o objeto ConfigProfileActivity::class.java como terceiro argumento de AccountSettingItem. Em aulas futuras, onde criaremos as outras telas de configurações de conta, nós atualizaremos os pontos necessários em AccountSettingsItemsDataBase.
Atualizando a AccountSettingsActivity
O objeto user que teremos de acessar também em ConfigProfileActivity está presente na AccountSettingsActivity.
Esse objeto user será necessário também em outras telas de atualização de configurações de conta, não somente na tela de atualização de dados de perfil.
Sendo assim é correto afirmar que no código acionador de atividades de configuração, que estará presente na classe adaptadora de itens do RecyclerView de AccountSettingsActivity, sempre haverá o algoritmo de acesso ao objeto user em AccountSettingsActivity.
Com isso, em AccountSettingsActivity, podemos criar um método simples que acessa esse objeto direto da fonte dele, o objeto intent.
Em AccountSettingsActivity adicione o método getUser(), e atualizações necessárias, como em destaque a seguir:
...
override fun onCreate( ... ) {
...
val user = getUser()
tv_user_connected.text = String.format(
"%s %s",
getString( R.string.connected ),
user.name
)
...
}
/*
* Método que permitirá o acesso ao User em Intent
* também por parte de outros objetos dependentes de
* AccountSettingsActivity.
* */
fun getUser()
= intent.getParcelableExtra<User>( User.KEY )
...
Assim podemos partir para a criação do listener de clique em classe adaptadora.
Listener de clique no adapter de AccountSettings
Com um código ainda simples, em AccountSettingsItemsAdapter adicione o trecho em destaque:
...
inner class ViewHolder( itemView: View ) :
RecyclerView.ViewHolder( itemView ),
View.OnClickListener {
...
init{
itemView.setOnClickListener( this )
...
}
...
override fun onClick( view: View ) {
val activity = view.context as AccountSettingsActivity
val user = activity.getUser()
val intent = Intent(
activity,
items[ adapterPosition ].activityClass
)
intent.putExtra( User.KEY, user )
activity.startActivity( intent )
}
}
...
Veja como a nova propriedade activityClass de AccountSettingItem alivia em muito a necessidade de termos em código um bloco condicional para sabermos qual atividade invocar.
Com essa última atualização em projeto, os toques em itens de configurações de conta passam a ser funcionais e o objeto user é sempre enviado a atividade acionada.
Testes e resultados
Em sua instalação do Android Studio, vá ao menu de topo e acesse "Build", então clique em "Rebuid project". Ao final do rebuild execute o aplicativo em seu emulador Android.
Não esqueça de colocar o objeto user como um "usuário conectado". Este objeto está na MainActivity:
...
val user = User(
"Thiengo Vinícius",
R.drawable.user,
true /* Usuário conectado. */
)
...
Acessando a tela de atualização de dados de perfil, temos:
Com isso finalizamos mais um importante trecho de interface gráfica de nosso projeto Android mobile-commerce. Ainda temos de trabalhar, para esta área do app, a parte de seleção de imagem... conteúdo de uma próxima aula.
Antes de prosseguir, não esqueça de se inscrever na 📫 lista de emails do Blog para receber todas as aulas do projeto Android BlueShoes.
Se inscreva também no canal do Blog em: YouTube Thiengo.
Vídeos
Abaixo os vídeos com o passo a passo da construção da nova tela de atualização de dados de perfil e também com o passo a passo das atualizações necessárias em projeto para que a nova tela possa ser acionada:
O código do projeto pode ser acessado pelo GitHub dele em: https://github.com/viniciusthiengo/blueshoes-kotlin-android.
Conclusão
Ainda com foco em telas que contêm formulários, demos continuidade a área de configurações de conta construindo desta vez a tela que permite a atualização de dados de perfil de usuário, mais precisamente a atualização do nome e imagem de perfil.
Muitos códigos foram reaproveitados, principalmente os códigos de FormActivity, mas foi inevitável, ao menos neste ponto do projeto, a repetição dos códigos de atualização de constraints em script dinâmico, mas certamente esses códigos passarão por melhorias para resolver isso: duplicação.
A parte de acesso a galeria e a câmera do aparelho será discutida em outra aula, pois ainda é preciso definirmos a API que será utilizada além de como ficarão os códigos de solicitação de permissão em tempo de execução.
Caso você tenha dúvidas ou dicas para este projeto, não hesite em deixar logo abaixo nos comentários.
Curtiu o conteúdo? Não deixe de compartilha-lo. E, por fim, se inscreva na 📩 lista de emails, respondo às suas dúvidas também por lá.
Abraço.
Fontes
MyClass.class syntax in Kotlin
Comentários Facebook