Criando e Configurando o Menu Principal - YouTuber Android App - Parte 3

Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba grátis conteúdos Android sem precedentes! Você receberá um email de confirmação. Somente depois de confirma-lo é que eu poderei lhe enviar os conteúdos semanais exclusivos. Os artigos em PDF são entregues somente para os inscritos na lista.

Email inválido.
Blog /Android /Criando e Configurando o Menu Principal - YouTuber Android App - Parte 3

Criando e Configurando o Menu Principal - YouTuber Android App - Parte 3

Vinícius Thiengo
(2161)
Go-ahead
"O método consciente de tentativa e erro é mais bem-sucedido que o planejamento de um gênio isolado."
Peter Skillman
Prototipagem Android
Capa do curso Prototipagem Profissional de Aplicativos
TítuloAndroid: Prototipagem Profissional de Aplicativos
CategoriasAndroid, Design, Protótipo
AutorVinícius Thiengo
Vídeo aulas186
Tempo15 horas
ExercíciosSim
CertificadoSim
Acessar Curso
Quer aprender a programar para Android? Acesse abaixo o curso gratuito no Blog.
Lendo
TítuloManual de DevOps: como obter agilidade, confiabilidade e segurança em organizações tecnológicas
CategoriaEngenharia de Software
Autor(es)Gene Kim, Jez Humble, John Willis, Patrick Debois
EditoraAlta Books
Edição
Ano2018
Páginas464
Conteúdo Exclusivo
Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba gratuitamente conteúdos Android sem precedentes!
Email inválido

Tudo bem?

Neste artigo vamos dar continuidade ao nosso projeto de aplicativo Android para YouTubers.

Nesta terceira parte do projeto nós vamos desenvolver toda a configuração de interface do menu principal, o bottom menu:

Bottom menu principal do aplicativo Android para YouTubers

Antes de prosseguir, saiba que:

A versão e-book (PDF 📙) do projeto completo está disponível somente para os inscritos da lista de e-mails 📧 do Blog.

Se você se inscrever ainda hoje é possível que o link do e-book esteja disponível na página de confirmação de sua inscrição na lista de e-mails do Blog.

É isso.

A seguir os tópicos que estaremos abordando neste terceiro conteúdo do projeto de aplicativo Android para YouTubers:

O que já temos até aqui

Se você chegou no projeto somente agora, saiba que este não é o primeiro e nem mesmo o último artigo já publicado.

Todo o roteiro de construção do projeto está na listagem a seguir:

Para tirar o máximo proveito do projeto de aplicativo Android que estaremos desenvolvendo é inteligente seguir cada um dos conteúdos na ordem apresentada na lista anterior.

Repositório

Para ter acesso a todos os fontes do projeto já finalizado, entre no repositório GitHub dele em:

➙ GitHub do projeto de aplicativo Android para YouTuber.

Estratégia e criação do menu

O menu principal do projeto, diferente do que você possa imaginar, é todo configurado em um framework de lista. Mais precisamente em um RecyclerView.

E o motivo disso é, sem rodeios: liberdade.

Infelizmente o BottomNavigationView foi criado para no máximo cinco itens de menu.

E parte da proposta de layout do nosso projeto de aplicativo é que o menu principal seja um bottom menu, porém com possibilidade para conter inúmeros itens, opções.

Sendo assim temos como uma boa estratégia utilizar o RecyclerView junto ao LayoutManager GridLayoutManager com somente uma coluna e listagem de itens na horizontal:

Bottom menu do app Android para YouTubers

Vamos aos códigos.

Estáticos de interface

Primeiro a parte simples, a parte estática.

Strings

No arquivo de Strings /res/values/strings.xml adicione as novas definições a seguir:

...
<!-- Menu -->
<string name="item_menu_last_video">
Vídeo
</string>
<string name="item_menu_social_networks">
Redes
</string>
<string name="item_menu_play_lists">
Play Lists
</string>
<string name="item_menu_exclusive_groups">
Grupos
</string>
<string name="item_menu_about_channel">
Sobre
</string>
<string name="item_menu_books">
Livros T.I.
</string>
<string name="item_menu_courses">
Cursos T.I.
</string>
<string name="item_menu_business">
Business
</string>
...

 

Cuidado com os rótulos de itens.

Pois dependendo do tamanho o layout de menu não ficará como esperado. Mesmo se você definir android:maxLines e android:ellipsize no TextView que vai receber o rótulo.

Sendo assim, escolha de maneira inteligente cada rótulo quando você estiver utilizando este aplicativo para algum outro canal do YouTube.

Cores

Agora no arquivo de cores, /res/values/colors.xml.

Adicione as seguintes novas definições de cores:

...
<!-- Bottom Menu -->
<color name="colorMenuSeparatorLine">#F1F1F1</color>
<color name="colorMenuBackground">@android:color/transparent</color>
<color name="colorMenuItemBackground">@android:color/transparent</color>
<color name="colorMenuItemSelected">#444444</color>
<color name="colorMenuItemNotSelected">#AFAFAF</color>
...

 

Sim, tem componente com a cor sendo @android:color/transparent.

E isso vai continuar assim, pois o arquivo estático de definição de cores é um dos principais arquivos daqueles que deverão passar por atualizações quando um outro canal for utilizado no app.

Ou seja, até mesmo as definições com @android:color/transparent poderão receber outros valores.

Ícones vetoriais

Como definido em protótipo, cada uma das opções de menu principal estará acompanhada de um ícone de sistema.

Aqui vamos utilizar os ícones em formato vetorial, pois para nossa necessidade em projeto teremos mais pontos a favor do que pontos contra em relação ao uso de imagens rasterizadas.

Os ícones de menu são os seguintes:

Novo "último vídeo" presente em canal, ic_circular_play.xml:

Ícone da opção de menu Último vídeo do canal

Spoiler: o ícone acima, mesmo sendo vetorial, também será o nosso ícone de nova notificação.

Redes sociais (e sites) do canal, ic_social_networks.xml:

Ícone da opção de menu Redes do canal

PlayLists do canal, ic_play_lists.xml:

Ícone da opção de menu PlayLists do canal

Grupos exclusivos do canal, ic_groups.xml:

Ícone da opção de menu Grupos do canal

Sobre (a proposta) do canal, ic_about.xml:

Ícone da opção de menu Sobre o canal

Livros do autor do canal, ic_books.xml:

Ícone da opção de menu Livros do canal

Cursos do autor do canal, ic_courses.xml:

Ícone da opção de menu Cursos do canal

Contatos comerciais do canal, ic_business.xml:

Ícone da opção de menu Contatos comerciais do canal

Se você estiver seguindo o projeto passo a passo (como eu espero). Então:

  • Clique nos links dos ícones apresentados anteriormente;
  • Faça o download deles;
  • e Os coloque no folder /res/drawable do projeto.

Os ícones foram todos baixados no Flaticon. Tem muita variedade no site e é gratuito.

Layout de item

Agora o layout de item de menu, layout que será carregado no adapter do framework de lista.

Primeiro vamos à simples estrutura dele:

Diagrama do layout menu_item.xml

Agora, em /res/layout, crie o arquivo XML menu_item.xml com o código a seguir:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="68dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@color/colorMenuItemBackground"
android:gravity="center"
android:orientation="vertical">

<ImageView
android:id="@+id/iv_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginBottom="6dp"
app:tint="@color/colorMenuItemNotSelected" />

<TextView
android:id="@+id/tv_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="12sp"
android:textStyle="normal" />
</LinearLayout>

 

Se você está se questionando o porquê de não estarmos utilizando o atributo android:drawableTop no TextView para assim diminuir ainda mais o código de layout de item.

Então saiba que com ele o layout não ficou exatamente como esperado e definido em protótipo estático. Sendo assim vamos prosseguir com a estrutura atual.

Mas se você quiser tentar a sua própria versão utilizando o android:drawableTop, siga em frente. Pois ao menos o código de layout ficará bem menor.

Configurações de IDs

Agora vamos às configurações de identificadores único de cada uma das opções de menu principal.

No folder /res/values crie o arquivo XML ids.xml como a seguir:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="last_video" />
<item type="id" name="social_networks" />
<item type="id" name="play_lists" />
<item type="id" name="exclusive_groups" />
<item type="id" name="about_channel" />
<item type="id" name="books" />
<item type="id" name="courses" />
<item type="id" name="business" />
</resources>

 

Identificadores únicos serão necessários para cada um dos itens de menu. Há inúmeras maneiras de resolvermos essa necessidade, incluindo o uso de constantes ou enums.

Mas como uma das propostas do primeiro release é deixar o código com uma leitura fácil...

... o arquivo de IDs tem somente essa responsabilidade: portar identificadores únicos em projeto.

Assim facilitando ao menos o entendimento sobre a necessidade de uso de identificadores únicos para a seleção correta de telas de acordo com o item de menu selecionado.

Isso tudo ficará mais claro quando chegar o momento no projeto em que tivermos de vincular os fragmentos de tela às opções de itens de menu.

Sendo assim, se algo ficou confuso neste instante... somente siga com o projeto, pois as dúvidas vão sendo esclarecidas com o decorrer do desenvolvimento.

Classes de domínio

Finalmente chegamos às classes de modelo, domínio de problema.

Na raiz do projeto, crie o pacote /model.

Em seguida crie dentro deste novo pacote as classes que vão representar os itens de menu e o estado atual de cada um deles.

Vamos começar com a classe estado, pois é um simples enum.

Segue o código de MenuItemStatus:

package thiengo.com.br.canalvinciusthiengo.model

enum class MenuItemStatus {
SELECTED,
NOT_SELECTED
}

 

Note que vamos utilizar essa estratégia de código (enum class) para facilitar a leitura de todo o projeto.

Essa é uma estratégia que eu utilizo com frequência nos aplicativos que desenvolvo e ajuda principalmente a evitar os temíveis "valores mágicos" em código.

Nós utilizaremos a estratégia de enum class em ainda mais pontos do projeto. Então, ao menos para este aplicativo Android, se familiarize com ela.

Agora, ainda no novo pacote /model, adicione a classe que realmente representa um item do menu principal, a classe MenuItem:

package thiengo.com.br.canalvinciusthiengo.model

/**
* Um item do menu principal, que fica ao fundo
* do aplicativo. Bottom menu.
*
* O objetivo desta classe (objetos desta classe)
* é manter os dados de identificação do item de
* menu principal, incluindo identificação do
* estado atual dele (selecionado ou não).
*
* @property id identificador único do item.
* @property label rótulo do item.
* @property icon ícone que ajuda a identificar
* o item.
* @property isSelected estado do item.
* @constructor cria um objeto completo do tipo
* [MenuItem].
*/
class MenuItem(
val id: Int,
val label: String,
val icon: Int,
var isSelected: MenuItemStatus = MenuItemStatus.NOT_SELECTED )

Persistência fixa

Se este não é o seu primeiro projeto de software com banco de dados, então certamente você deve vincular o termo "persistência" (ou "banco de dados") ao modelo de dados dinâmico.

Mas na verdade o termo abrange também os locais onde há dados estáticos.

E aqui em projeto teremos algumas "persistências estáticas", "persistências fixas" . O menu principal será inicializado com uma base desse modelo.

Assim, na raiz do projeto:

  • Crie o pacote /data;
  • Logo depois, dentro deste novo pacote, crie o pacote /fixed.

Não utilizei o termo static no lugar de fixed, pois esse é uma palavra reservada no Kotlin.

Agora, dentro do pacote /data/fixed, crie a "classe persistência" MenuItemsData como a seguir:

package thiengo.com.br.canalvinciusthiengo.data.fixed

import android.content.res.Resources
import thiengo.com.br.canalvinciusthiengo.R
import thiengo.com.br.canalvinciusthiengo.model.MenuItem
import thiengo.com.br.canalvinciusthiengo.model.MenuItemStatus

/**
* Contém os dados dos itens do menu principal
* do aplicativo.
*
* O objetivo desta classe é trabalhar como uma
* persistência local estática, fixa, que contém
* os dados de itens.
*
* Como esses dados praticamente não sofrem
* alterações depois do lançamento do aplicativo,
* então a melhor escolha foi o trabalho deles
* em uma classe estática (companion object)
* que trabalha como se fosse uma persistência
* de dados estáticos.
*/
class MenuItemsData {

companion object{
/**
* Retorna os itens do menu principal
* do app.
*
* @return lista não mutável de objetos
* [MenuItem].
*/
fun getItems( res: Resources )
= listOf(
MenuItem(
id = R.id.last_video,
label = res.getString( R.string.item_menu_last_video ),
icon = R.drawable.ic_circular_play,
isSelected = MenuItemStatus.SELECTED
),
MenuItem(
id = R.id.social_networks,
label = res.getString( R.string.item_menu_social_networks ),
icon = R.drawable.ic_social_networks
),
MenuItem(
id = R.id.play_lists,
label = res.getString( R.string.item_menu_play_lists ),
icon = R.drawable.ic_play_lists
),
MenuItem(
id = R.id.exclusive_groups,
label = res.getString( R.string.item_menu_exclusive_groups ),
icon = R.drawable.ic_groups
),
MenuItem(
id = R.id.about_channel,
label = res.getString( R.string.item_menu_about_channel ),
icon = R.drawable.ic_about
),
MenuItem(
id = R.id.books,
label = res.getString( R.string.item_menu_books ),
icon = R.drawable.ic_books
),
MenuItem(
id = R.id.courses,
label = res.getString( R.string.item_menu_courses ),
icon = R.drawable.ic_courses
),
MenuItem(
id = R.id.business,
label = res.getString( R.string.item_menu_business ),
icon = R.drawable.ic_business
)
)
}
}

 

Para "classes persistência" vamos manter o uso de companion object para diminuir a verbosidade de todo o código do projeto.

Assim não precisaremos utilizar a sintaxe de criação de objetos de classes que contém outros objetos que já sabemos que sempre estarão em memória.

ViewHolder

Agora, já próximo do final da configuração base do menu principal, vamos à classe de framework de lista que implementa o conhecido padrão ViewHolder.

Primeiro, no pacote /ui, crie um novo pacote de rótulo /adapter.

Dentro deste novo pacote crie a classe MenuViewHolder com o exato mesmo código a seguir:

package thiengo.com.br.canalvinciusthiengo.ui.adapter

import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import thiengo.com.br.canalvinciusthiengo.R
import thiengo.com.br.canalvinciusthiengo.model.MenuItem
import thiengo.com.br.canalvinciusthiengo.model.MenuItemStatus

/**
* Classe responsável por aplicar o padrão
* ViewHolder na lista de itens de menu principal
* do app.
*
* @property adapter adaptador de itens de menu.
* @property changeFragmentCallback contém o
* algoritmo de execução quando um novo item é
* acionado pelo usuário.
* @property itemView layout de item.
* @constructor cria um objeto completo do tipo
* [MenuViewHolder].
*/
class MenuViewHolder(
private val adapter: MenuAdapter,
private val changeFragmentCallback: (MenuItem)->Unit,
itemView: View
) : RecyclerView.ViewHolder( itemView ), View.OnClickListener {

/**
* Propriedades de layout, UI.
*/
private var ivIcon: ImageView
private var tvLabel: TextView

/**
* Bloco de inicialização da UI do layout
* de item de menu.
*/
init {
itemView.setOnClickListener( this )

ivIcon = itemView.findViewById( R.id.iv_icon )
tvLabel = itemView.findViewById( R.id.tv_label )
}

/**
* Define em UI os dados que devem ser
* apresentados em item de lista.
*
* @param item item de menu.
*/
fun setModel( item: MenuItem ) {
tvLabel.text = item.label
ivIcon.setImageResource( item.icon )
ivIcon.contentDescription = item.label

setStyle( item = item )
}

/**
* Define o estilo correto do ícone e do
* texto do item de menu. Estilo correto de
* acordo com o estado dele: selecionado;
* ou não selecionado.
*
* @param item item de menu.
*/
private fun setStyle( item: MenuItem ){

var itemColor = R.color.colorMenuItemNotSelected

if( item.isSelected == MenuItemStatus.SELECTED ){
itemColor = R.color.colorMenuItemSelected
}

val color = ContextCompat.getColor(
adapter.context,
itemColor
)

ivIcon.setColorFilter( color )
tvLabel.setTextColor( color )
}

override fun onClick( view: View ){
/* TODO */
}
}

 

Parece um código complexo, mas isso é devido ao tamanho dele (incluindo os comentários). Na verdade é um código simples, boilerplate, para que o padrão ViewHolder seja aplicado à parte UI do menu.

Note em setModel() como estamos invocando o método setStyle():

...
setStyle( item = item )
...

 

Estamos utilizando "argumento nomeado".

Uma característica de codificação Kotlin e que sempre que possível (quando a classe do método for em Kotlin) estaremos utilizando em nosso projeto de aplicativo.

Isso, pois a leitura do código fica facilitada.

Thiengo, não seria melhor utilizar o bloco condicional em setStyle() como expressão? Exatamente como a seguir:

...
val itemColor = if( item.isSelected == MenuItemStatus.SELECTED )
R.color.colorMenuItemSelected
else
R.color.colorMenuItemNotSelected
...

 

Ao invés de:

...
var itemColor = R.color.colorMenuItemNotSelected

if( item.isSelected == MenuItemStatus.SELECTED ){
itemColor = R.color.colorMenuItemSelected
}
...

 

É excelente conhecer também este recurso da linguagem Kotlin, mas para o trecho de código que você informou eu preferi seguir no modo "comum", sem utilizar o bloco condicional como expressão.

Pois no meu ponto de vista a leitura do código em si piorou quando o bloco condicional foi utilizado como expressão.

Note que não estou falando que este é um recurso de linguagem ruim. Na verdade ele é excelente, mas no código apontado anteriormente ele não se encaixou muito bem.

É isso.

Ainda falta o código do onClick(). Código responsável por:

  • Mudar o estado do novo item selecionado;
  • Mudar o estado do item que já estava selecionado;
  • Acionar a "mudança de fragmento" que estará no código do callback changeFragmentCallback

Assim, no onClick() de MenuViewHolder, coloque o seguinte código:

...
override fun onClick( view: View ){

val oldSelectedItem = adapter.items.first {
it.isSelected == MenuItemStatus.SELECTED
}
val indexOldSelectedItem = adapter.items.indexOf( oldSelectedItem )

/**
* Cláusula de guarda para que não haja
* ativação redundante de fragmento já
* em foreground.
*/
if( indexOldSelectedItem == adapterPosition ){
return
}

/**
* Garantindo que o item selecionado
* anteriormente tenha agora o status de
* item "não selecionado". Pois um novo
* item foi acionado.
*/
oldSelectedItem.apply {
isSelected = MenuItemStatus.NOT_SELECTED
}
adapter.notifyItemChanged( indexOldSelectedItem )

/**
* O algoritmo a seguir é responsável por
* colocar, no item acionado pelo usuário,
* o status e estilo adequados.
*/
adapter.items[ adapterPosition ].apply {
isSelected = MenuItemStatus.SELECTED
}
adapter.notifyItemChanged(
adapterPosition
)

changeFragmentCallback(
adapter.items[adapterPosition]
)
}
...

 

Para entender mais sobre o padrão de implementação Cláusula de Guarda, então entre no seguinte artigo (depois de terminar está terceira parte projeto): Padrão de Projeto Cláusula de Guarda.

Thiengo, por que você utilizou apply(), como no exemplo a seguir:

...
oldSelectedItem.apply {
isSelected = MenuItemStatus.NOT_SELECTED
}
...

 

Ao invés da comum atribuição de valor a uma propriedade (como abaixo)?

...
oldSelectedItem.isSelected = MenuItemStatus.NOT_SELECTED
...

 

Acredite:

Foi somente para encurtar o tamanho da linha de código e assim ela entrar "sem problemas" no artigo.

Desta forma eu fico com mais linhas de código, porém para a explicação e apresentação em artigo é melhor, pois elas ficam menores.

Ou seja, não existe motivos vinculados a performance de processamento, é somente estética 😎.

Um ponto que pode ser importante:

Possivelmente você deve ter notado que em métodos sobrescritos (com a definição override) eu não coloco comentários.

E é isso mesmo, pois na entidade em que ele foi declarado espera-se que exista a documentação completa do funcionamento dele.

Adapter

Enfim a classe adaptadora de itens.

Agora, no pacote /ui/adapter, crie a classe MenuAdapter com o código a seguir:

package thiengo.com.br.canalvinciusthiengo.ui.adapter

import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import thiengo.com.br.canalvinciusthiengo.R
import thiengo.com.br.canalvinciusthiengo.model.MenuItem

/**
* Classe adaptadora de itens do menu principal do
* aplicativo.
*
* Define qual ViewHolder, qual lista de dados e
* qual layout será utilizado no framework de
* lista [RecyclerView] do BottomMenu do app.
*
* @property context contexto do aplicativo.
* @property items itens de menu (dados em lista).
* @property changeFragmentCallback contém o
* algoritmo de execução quando um novo item é
* acionado pelo usuário.
* @constructor cria um objeto completo do tipo
* [MenuAdapter].
*/
class MenuAdapter(
val context: Context,
val items: List<MenuItem>,
private val changeFragmentCallback: (MenuItem)->Unit
) : RecyclerView.Adapter<MenuViewHolder>() {

companion object{
/**
* Constante que define o número de
* colunas do menu. No caso é apenas uma
* coluna para o menu principal em
* apresentação horizontal.
*/
const val NUMBER_COLUMNS = 1
}

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int ) : MenuViewHolder {

val layout = LayoutInflater
.from( context )
.inflate(
R.layout.menu_item,
parent,
false
)

return MenuViewHolder(
adapter = this,
changeFragmentCallback = changeFragmentCallback,
itemView = layout
)
}

override fun onBindViewHolder(
holder: MenuViewHolder,
position: Int ){

holder.setModel(
item = items[ position ]
)
}

override fun getItemCount()
= items.size
}

 

Novamente muito código boilerplate...

... que estamos, mesmo que sem uma data definida, se despedindo... ao menos desse modelo de configuração de framework de lista.

Todo esse trajeto somente para ter uma lista de itens em tela não mais será necessário.

O Jetpack Compose, mesmo que ainda em versão de desenvolvimento (depende da época em que você estiver consumindo este conteúdo), já tem uma API que não exige nem mesmo 10% do esforço atual que temos de fazer para colocarmos lista de itens em tela.

Agora falando sobre o código definido em MenuAdapter, você vai notar que algumas propriedades são privadas e outras são públicas:

...
class MenuAdapter(
val context: Context,
val items: List<MenuItem>,
private val changeFragmentCallback: (MenuItem)->Unit
) : ... {
...

 

Na verdade está é uma característica que teremos em todo o projeto.

Se uma entidade (propriedade, método, ...) não precisa ser pública, pois será utilizada somente na classe atual, então vamos defini-la como private.

Isso facilita consideravelmente toda a manutenção do código:

Deixar as entidades em código somente com acesso aos escopos que elas são necessárias.

Você pode ir além e trabalhar também com o tipo de acesso protected. Mas para esse primeiro release eu vou oscilar somente entre private e public.

Outro ponto que vale comentar é o frequente uso de callback em todo o projeto:

...
private val changeFragmentCallback: (MenuItem)->Unit
...

 

Com esse recurso de linguagem nós podemos enviar execuções de código para qualquer parte do projeto e com independência de Thread em execução (Principal ou não).

Caso você seja iniciante no Android, recomendo que você se acostume com callbacks e lambdas. Pois é algo utilizado com frequência em inúmeros projetos de software, não somente neste aqui.

Com isso podemos prosseguir com a configuração do menu em atividade principal.

Configurando em atividade

Com os códigos base de menu principal já definidos, podemos ir com segurança à configuração dele na MainActivity.

Confesso que vamos à configuração parcial, pois com a posterior adição dos fragmentos em projeto nós incluiremos ainda mais códigos de menu na atividade.

É isso... vamos primeiro aos códigos estáticos, o layout.

Layout

A está altura do projeto o layout activity_main.xml ficará com a seguinte estrutura:

Diagrama do layout activity_main.xml

Sendo assim, adicione o RecyclerView (e a linha divisória, View) a este layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.MainActivity">

<include layout="@layout/top_signature" />

<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="8dp"
android:background="@color/colorMenuSeparatorLine" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_menu"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="@color/colorMenuBackground"
android:padding="0dp" />
</LinearLayout>

 

Excelente.

Vamos aos códigos dinâmicos.

MainActivity

No código dinâmico da atividade vamos colocar os códigos de inicialização e uso do framework de menu principal.

Adicione à MainActivity o método initBottomMenu() com o código a seguir:

...
/**
* Inicializa o menu principal do aplicativo, o
* BottomMenu.
*/
private fun initBottomMenu(){

val layoutManager = GridLayoutManager(
this,
MenuAdapter.NUMBER_COLUMNS,
RecyclerView.HORIZONTAL,
false
)
rv_menu.layoutManager = layoutManager

rv_menu.setHasFixedSize( true )
rv_menu.adapter = MenuAdapter(
context = this,
items = MenuItemsData.getItems( res = resources ),
changeFragmentCallback = {
item -> { /* TODO */ }
}
)
}
...

 

Adicione também as importações corretas:

...
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
import thiengo.com.br.canalvinciusthiengo.data.fixed.MenuItemsData
import thiengo.com.br.canalvinciusthiengo.model.MenuItem
import thiengo.com.br.canalvinciusthiengo.ui.adapter.MenuAdapter
...

 

Assim, no onCreate() da atividade, adicione a invocação do método initBottomMenu() como a seguir:

...
override fun onCreate( ... ) {
...

initBottomMenu()
}
...

 

Done.

A configuração inicial de menu está apronta.

Neste ponto do projeto a configuração física dele em IDE deve estar como a seguir:

Configuração física do projeto Android

Assim podemos partir para o próximo artigo.

Próximo conteúdo

Mais um trecho importante do projeto finalizado com sucesso.

No próximo conteúdo nós vamos codificar as entidades comuns à todas as telas que contém o framework de lista RecyclerView:

Criando a Estrutura Base Das Telas Com Lista - Parte 4.

Então é isso.

Descanse um pouco, tome um café ☕ 🥧 🍪. E...

... te vejo na Parte 4 do projeto.

Se houverem dúvidas ou dicas deste terceiro conteúdo do aplicativo, então deixe nos comentários que logo eu lhe respondo.

Não esqueça de conhecer também o meu canal no YouTube (caso você ainda não conheça) e...

... não deixe de se inscrever na 📩 lista de e-mails para também garantir a versão em PDF não somente deste projeto de aplicativo Android, mas também de cada novo "conteúdo mini-curso".

Abraço.

Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba grátis conteúdos Android sem precedentes!
Email inválido

Relacionado

Data Binding Para Vinculo de Dados na UI AndroidData Binding Para Vinculo de Dados na UI AndroidAndroid
Android About Page API Para Construir a Tela SobreAndroid About Page API Para Construir a Tela SobreAndroid
Annotation Span Para Estilização de Texto no AndroidAnnotation Span Para Estilização de Texto no AndroidAndroid
Porque e Como Utilizar Vetores no AndroidPorque e Como Utilizar Vetores no AndroidAndroid

Compartilhar

Comentários Facebook

Comentários Blog

Para código / script, coloque entre [code] e [/code] para receber marcação especifica.
Forneça seu nome válido.
Forneça seu email válido.
Forneça o comentário.
Enviando, aguarde...