Como Criar a UI de Configurações de Conta de Usuário - Android M-Commerce
(3459) (5)
CategoriasAndroid, Design, Protótipo
AutorVinÃcius Thiengo
VÃdeo aulas186
Tempo15 horas
ExercÃciosSim
CertificadoSim
CategoriaEngenharia de Software
Autor(es)Gene Kim, Jez Humble, John Willis, Patrick Debois
EditoraAlta Books
Edição1ª
Ano2018
Páginas464
Tudo bem?
Neste artigo vamos continuar com o projeto Android BlueShoes, nosso aplicativo de mobile-commerce. Aqui vamos a construção da interface de usuário da área de configurações de conta.
Vamos também a algumas atualizações importantes na atividade principal do projeto, preparando ela para também trabalhar com opções de menu gaveta que abrem outras atividades ao invés de somente fragmentos.
Antes de continuar com o projeto, não deixe de se inscrever 📩 na lista de emails do Blog para ter acesso exclusivo aos novos conteúdos desta série e também acesso a outros artigos e vídeos sobre desenvolvimento Android.
A seguir os tópicos do artigo:
- Estou iniciando no projeto Android BlueShoes;
- Estratégia para a tela de configurações de conta:
- Trabalhando a atividade de configurações:
- Atualização da atividade principal:
- Testes e resultados;
- Vídeos;
- Conclusão;
- Fontes.
Estou iniciando no projeto Android BlueShoes
Você chegou a está série de Android mobile-commerce somente agora? Então saiba que já existem outras 10 aulas disponíveis antes desta, aulas que devem ser estudas primeiro do que esta para que você não fique "perdido" no projeto:
- 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.
Estratégia para a tela de configurações de conta:
O roteiro adotado no desenvolvimento da tela de configurações de conta de usuário não será diferente de outros roteiros já praticados neste projeto.
Vamos, então, seguir:
- Primeiro as configurações estáticas que dispensam a necessidade de a atividade de configurações de conta já estar pronta;
- Depois os códigos dinâmicos da nova atividade de configurações de conta, incluindo os códigos estáticos que somente podem ser trabalhados depois da existência de alguns dos códigos dinâmicos;
- Por fim a atualização na atividade principal de projeto, pois é ela que permitirá o acesso a área de configurações de conta. Neste trecho teremos um pouco mais de trabalho do que o comum, principalmente em classes auxiliares à atividade principal.
Protótipo estático
A seguir o protótipo estático da tela de configurações de conta de usuário:
Opções de configuração |
Bem simples, certo? A dificuldade mesmo estará no modo de acesso a tela acima.
Trabalhando a atividade de configurações de conta
Bom, chegou o momento de "colocar a mão na massa", mesmo que seja iniciando pela parte simples: codificação estática.
Mas antes de prosseguir é importante lembrar que o projeto Android BlueShoes está gratuitamente disponível no repositório dele em: https://github.com/viniciusthiengo/blueshoes-kotlin-android.
Mas, de preferência, utilize os códigos do repositório somente se você enfrentar alguma dificuldade quando seguindo os artigos de construção do projeto, pois são os artigos que contém as explicações passo a passo do porquê de determinado algoritmo ou API em uso, além, claro, dos códigos completos.
Arquivo de cores
No arquivo de configurações de cores, /res/values/colors.xml, adicione as cores em destaque a seguir:
<?xml version="1.0" encoding="utf-8"?>
<resources>
...
<color name="colorTextBox">#EEEEEE</color>
<color name="colorLightGrey">#AAAAAA</color>
<color name="colorLighterGrey">#EFEFEF</color>
</resources>
Lembrando que os arquivos estáticos atualizados ou criados antes do desenvolvimento dos códigos de atividade são assim feitos, pois o protótipo estático nos fornece informações suficientes para seguirmos assim.
Arquivo de Strings
Para o arquivo de Strings temos de realizar algumas adições. Em /res/values/strings.xml adicione os trechos em destaque abaixo:
<resources>
...
<!-- AccountSettingsActivity -->
<string name="title_activity_account_settings">
Configurações de conta
</string>
<string name="connected">Conectado:</string>
<string name="setting_item_profile">Perfil</string>
<string name="setting_item_profile_desc">
Para atualização de nome e foto de perfil.
</string>
<string name="setting_item_login">Conexão (login)</string>
<string name="setting_item_login_desc">
Para atualização de e-mail ou senha de acesso.
</string>
<string name="setting_item_address">Endereços</string>
<string name="setting_item_address_desc">
Para adição, remoção ou atualização de endereços de entrega.
</string>
<string name="setting_item_credit_cards">Cartões de crédito</string>
<string name="setting_item_credit_cards_desc">
Para adição e remoção de cartões de crédito.
</string>
</resources>
Atualizando o pacote de domínio do problema
Como você deve ter percebido no protótipo estático, nós utilizaremos algum framework de lista na área de configurações de conta. Mais precisamente, o framework será o RecyclerView.
Com isso é uma escolha inteligente ampliarmos o nosso conjunto de classes de domínio de problema, adicionando a classe responsável por ser a origem dos objetos que vão conter os dados de cada item de lista.
Em /domain adicione a classe AccountSettingItem com o código a seguir:
class AccountSettingItem(
val label: String,
val description: String
)
Base de dados local para itens de configuração de conta
Com a nossa nova classe de domínio, podemos já colocar em algum ponto do projeto os dados que preencherão o framework de lista da UI de configurações de conta.
Lembrando que as opções desta lista são estáticas e se futuramente passarem por atualizações, essas virão em novas versões do aplicativo, ou seja, não faz sentido utilizar uma persistência de dados dinâmica para guardar esses dados.
Em /data crie a classe AccountSettingsItemsDataBase com o código abaixo:
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 )
),
AccountSettingItem(
context.getString( R.string.setting_item_login ),
context.getString( R.string.setting_item_login_desc )
),
AccountSettingItem(
context.getString( R.string.setting_item_address ),
context.getString( R.string.setting_item_address_desc )
),
AccountSettingItem(
context.getString( R.string.setting_item_credit_cards ),
context.getString( R.string.setting_item_credit_cards_desc )
)
)
}
}
Como desta vez não há a necessidade de mantermos ativos os objetos de lista, mesmo com a possibilidade de reconstrução da atividade de configurações de conta quando em execução, não estamos utilizando, por exemplo, a API SelectionTracker (ela não é útil aqui).
Assim podemos seguramente fazer o uso de companion object para retirarmos das classes clientes de AccountSettingsItemsDataBase a necessidade de uma inicialização e gerenciamento de objeto.
Classe adaptadora de itens de configuração
Sim, sim, sim! Ainda há mais entidades a serem criadas antes da atividade de configurações de conta. Neste ponto do artigo já não é novidade que estaremos utilizando o RecyclerView como o framework de lista nesta tela.
Sendo assim, vamos primeiro à construção do layout de item.
Em /res/layout adicione o XML account_settings_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"
android:paddingRight="16dp"
android:paddingEnd="16dp"
android:paddingLeft="16dp"
android:paddingStart="16dp"
android:paddingTop="18dp"
android:paddingBottom="18dp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:textStyle="bold"
android:textColor="@color/colorText"/>
<TextView
android:id="@+id/tv_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorLightGrey"/>
</LinearLayout>
Layout bem simples, certo? O diagrama dele comprova isso:
Agora, em /view, crie a classe adaptadora AccountSettingsItemsAdapter com o código a seguir:
class AccountSettingsItemsAdapter(
private val items: List<AccountSettingItem>
) :
RecyclerView.Adapter<AccountSettingsItemsAdapter.ViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
type: Int ): ViewHolder {
val layout = LayoutInflater
.from( parent.context )
.inflate(
R.layout.account_settings_item,
parent,
false
)
return ViewHolder( layout )
}
override fun onBindViewHolder(
holder: ViewHolder,
position: Int ) {
holder.setData( items[ position ] )
}
override fun getItemCount() = items.size
inner class ViewHolder( itemView: View ) :
RecyclerView.ViewHolder( itemView ){
private val tvLabel : TextView
private val tvDescription : TextView
init{
tvLabel = itemView.findViewById( R.id.tv_label )
tvDescription = itemView.findViewById( R.id.tv_description )
}
fun setData( item: AccountSettingItem ){
tvLabel.text = item.label
tvDescription.text = item.description
}
}
}
Assim podemos prosseguir à atividade de configurações de conta.
Criando a AccountSettingsActivity
Siga, no pacote /view do projeto:
- Clique com o botão direito do mouse;
- Acesse New;
- Então acesse Activity;
- Clique em Basic Activity;
- Na caixa de diálogo aberta:
- Em Activity Name coloque AccountSettingsActivity;
- Em Layout Name continue com activity_account_settings;
- Não marque a opção Launcher Activity;
- Não marque a opção Use a Fragment;
- Deixe Hierarchical Parent sem alteração;
- Em Package Name permaneça com"Nome do pacote".view;
- Em Source Language permaneça com Kotlin;
- Por fim clique em Finish.
Pode ser que uma tela de alerta seja apresentada. Caso sim, clique em Proceed anyway.
Ao final da criação da atividade AccountSettingsActivity nós teremos o seguinte código:
class AccountSettingsActivity :
AppCompatActivity() {
override fun onCreate( savedInstanceState: Bundle? ) {
super.onCreate( savedInstanceState )
setContentView( R.layout.activity_account_settings )
setSupportActionBar( toolbar )
fab.setOnClickListener { view ->
Snackbar
.make(
view,
"Replace with your own action",
Snackbar.LENGTH_LONG
)
.setAction(
"Action",
null
)
.show()
}
supportActionBar?.setDisplayHomeAsUpEnabled( true )
}
}
Seguramente remova o código do FloatingActionButton. Assim teremos:
class AccountSettingsActivity :
AppCompatActivity() {
override fun onCreate( savedInstanceState: Bundle? ) {
super.onCreate( savedInstanceState )
setContentView( R.layout.activity_account_settings )
setSupportActionBar( toolbar )
supportActionBar?.setDisplayHomeAsUpEnabled( true )
}
}
Agora coloque os códigos que permitem a volta à atividade anterior, MainActivity, que permitiu o acesso a área de configurações de conta:
class AccountSettingsActivity :
AppCompatActivity() {
override fun onCreate( ... ) {
...
supportActionBar?.setDisplayShowHomeEnabled( true )
}
override fun onOptionsItemSelected( item: MenuItem ): Boolean {
if( item.itemId == android.R.id.home ){
finish()
return true
}
return super.onOptionsItemSelected( item )
}
}
Assim adicione os códigos de inicialização da lista de opções de configurações de conta:
class AccountSettingsActivity :
AppCompatActivity() {
override fun onCreate( ... ) {
...
initItems()
}
...
/*
* Método que inicializa a lista de itens de configurações
* de conta.
* */
private fun initItems(){
rv_account_settings_items.setHasFixedSize( false )
val layoutManager = LinearLayoutManager( this )
rv_account_settings_items.layoutManager = layoutManager
val divider = DividerItemDecoration(
this,
layoutManager.getOrientation()
)
divider.setDrawable(
ContextCompat.getDrawable(
this,
R.drawable.light_grey_divider_line
)!!
)
rv_account_settings_items.addItemDecoration( divider )
rv_account_settings_items.adapter = AccountSettingsItemsAdapter(
AccountSettingsItemsDataBase.getItems( this )
)
}
}
Ops! E sobre o drawable light_grey_divider_line utilizado em ContextCompat.getDrawable()?
Sim, sim. Este é necessário para que possamos manter o uso de DividerItemDecoration, a maneira correta de colocar linhas divisoras entre os itens do RecyclerView, porém com a cor que quisermos.
Em /res/drawable adicione o XML light_grey_divider_line.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="rectangle">
<size
android:width="1dp"
android:height="1dp" />
<solid android:color="@color/colorLighterGrey" />
</shape>
E o layout principal?
Este é o nosso próximo assunto.
Layout principal
Na verdade o layout principal da atividade de configurações de conta é feito também por outros layouts.
Vamos iniciar com o layout de conteúdo, o que contém também o framework de lista. Em /res/layout crie (ou atualize) o XML content_account_settings.xml com o código abaixo:
<?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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_user_connected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:textColor="@color/colorText"
android:textStyle="bold"
android:background="@color/colorTextBox"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_account_settings_items"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="vertical"/>
</LinearLayout>
O TextView de topo é o que conterá o nome do usuário conectado:
A seguir o diagrama do layout anterior:
Agora o layout principal. Isso, pois o layout de topo, app_bar.xml, já existe em projeto. Em /res/layout atualize o XML activity_account_settings.xml com o código a seguir:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<include layout="@layout/app_bar" />
<include layout="@layout/content_account_settings" />
</android.support.design.widget.CoordinatorLayout>
Abaixo o simples diagrama do layout anterior:
Assim a atualização final em AccountSettingsActivity para que seja possível colocar o nome do usuário na área destinada a ele em tela. No onCreate() desta atividade adicione os códigos em destaque:
...
override fun onCreate( ... ) {
...
/*
* Colocando em tela o usuário conectado.
* */
val user = intent.getParcelableExtra<User>( User.KEY )
tv_user_connected.text = String.format(
"%s %s",
getString( R.string.connected ),
user.name
)
initItems()
}
...
Thiengo, por que o uso de getParcelableExtra<User>(), sendo que eu não lembro de a classe User ser um Parcelable?
Calma, ainda chegaremos a este ponto do projeto.
Configuração do AndroidManifest
Note que no AndroidManifest.xml a nova atividade deverá estar configurada como a seguir:
<?xml version="1.0" encoding="utf-8"?>
<manifest
...>
<application
...>
...
<activity
android:name=".view.AccountSettingsActivity"
android:label="@string/title_activity_account_settings"
android:theme="@style/AppTheme.NoActionBar"/>
</application>
</manifest>
User precisa ser um Parcelable
Lembra de sua dúvida sobre o User não ter a implementação da Interface Parcelable?
Então, este é o momento. Atualize a classe User para a nova configuração dela:
class User(
val name: String,
val image: Int,
val status: Boolean = false
) : Parcelable {
constructor(source: Parcel) : this(
source.readString(),
source.readInt(),
1 == source.readInt()
)
override fun describeContents() = 0
override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
writeString(name)
writeInt(image)
writeInt((if (status) 1 else 0))
}
companion object {
const val KEY = "user-key"
@JvmField
val CREATOR: Parcelable.Creator<User> = object : Parcelable.Creator<User> {
override fun createFromParcel(source: Parcel): User = User(source)
override fun newArray(size: Int): Array<User?> = arrayOfNulls(size)
}
}
}
Assim será possível enviar o objeto User, que está presente na MainActivity, para a AccountSettingsActivity, isso utilizando apenas uma Intent.
Atualização da atividade principal
Chegamos a um outro passo importante da adição da área de configurações de conta do usuário.
O acesso a está área é por meio do menu gaveta de usuário conectado. Já temos todos os códigos estáticos prontos, aqui trabalharemos somente códigos dinâmicos, Kotlin.
Mas há um sério problema que surge junto ao conhecimento de: a tela de configurações de conta é uma atividade e não um fragmento.
Problema! Desta vez é uma atividade
Pela tela de configurações de conta ser uma atividade, quase todos os algoritmos de item selecionado em menu gaveta não serão úteis. Mas mesmo assim o comportamento visual do menu gaveta deve ser consistente.
A primeira ideia em mente poderia ser: vamos apenas colocar um bloco condicional em onItemStateChanged() para saber quando chamar o algoritmo de mudança de fragmento e quando chamar o algoritmo de mudança de atividade.
Certo, mas somente assim provavelmente teríamos o seguinte resultado:
Veja como fica o menu gaveta na volta da atividade de configurações de conta. O item errado permanece selecionado.
Mas a simples ideia apresentada não está errada não, a solução é por ai mesmo, porém com ainda mais APIs de auxílio, incluindo uma API de persistência!
Proposta de solução
Como uma proposta de solução, sem necessitar de APIs externas ao Android SDK, podemos:
- Persistir em uma base local, antes de abrir uma atividade acionada por item de menu, o ID do fragmento aberto em tela atualmente;
- Salvar, também em uma base local, que a entidade que está sendo acionada é uma atividade e não um fragmento;
- Então abrir a atividade;
- Na volta da atividade acionada para a MainActivity, acessar o ID do último fragmento apresentado em tela e assim invocar novamente a seleção dele em menu gaveta;
- Também atualizar o estado do último item de menu gaveta acionado, digo, atualizar na persistência, deixando claro que não mais é uma atividade.
Os fluxogramas a seguir certamente vão facilitar o entendimento da estrutura da solução proposta. Primeiro o fluxograma de acionamento de fragmento ou atividade:
Assim o fluxograma de "volta a MainActivity":
Com isso vamos por a "mão na massa".
Configurando o SharedPreferences na base de itens de menu
Como base local utilizaremos o SharedPreferences, por ser simples e rápido.
A seguir a lista do que precisaremos do SharedPreferences:
- Salvar o ID do último item de fragmento apresentado em tela;
- Obter o ID do último item de fragmento apresentado em tela;
- Salvar se o item acionado atualmente é um item que aciona uma atividade;
- Obter se o último item acionado foi um item que aciona uma atividade.
No pacote /data do projeto nós já temos uma classe que contém dados de itens de menu gaveta, NavMenuItemsDataBase. Sendo assim vamos adicionar a ela os novos códigos necessários aos trabalhos com o SharedPreferences em nosso domínio de problema:
class NavMenuItemsDataBase( ... ) {
...
companion object{
const val SP_NAME = "SP_NAV_MENU"
const val SP_ITEM_ID_KEY = "item-id"
const val SP_IS_ACTIVITY_KEY = "is-activity"
}
private fun getSP( context: Context )
= context.getSharedPreferences(
SP_NAME,
Context.MODE_PRIVATE
)
/*
* Salva o ID do último item de menu selecionado que
* aciona um fragmento.
* */
fun saveLastSelectedItemFragmentID( context: Context, itemID: Long ){
val sp = getSP( context )
sp.edit().putLong( SP_ITEM_ID_KEY, itemID ).apply()
}
/*
* Retorna o ID do último item de menu selecionado que
* aciona um fragmento.
* */
fun getLastSelectedItemFragmentID( context: Context ) : Long {
val sp = getSP( context )
return sp.getLong( SP_ITEM_ID_KEY, 0 )
}
/*
* Salva se o último item de menu acionado foi ou não
* um item que aciona uma atividade.
* */
fun saveIsActivityItemFired( context: Context, isActivity: Boolean ){
val sp = getSP( context )
sp.edit()
.putBoolean( SP_IS_ACTIVITY_KEY, isActivity )
.apply()
}
/*
* Informa se o último item de menu acionado foi ou não
* um item que aciona uma atividade.
* */
fun wasActivityItemFired( context: Context ) : Boolean {
val sp = getSP( context )
return sp.getBoolean( SP_IS_ACTIVITY_KEY, false )
}
}
Assim vamos às atualizações reais na MainActivity.
Algoritmos de abertura da AccountSettingsActivity
É certo que utilizaremos algum objeto do tipo NavMenuItemsDataBase na MainActivity. Na verdade nós precisamos somente de um e já existe um na atividade principal, porém não como uma propriedade de classe:
...
private fun initNavMenu( ... ){
val navMenu = NavMenuItemsDataBase( this )
...
}
...
Vamos atualizar a MainActivity para que primeiro o navMenu se torne uma propriedade de classe. Assim, nesta classe adicione os códigos em destaque:
class MainActivity :
AppCompatActivity() {
...
lateinit var navMenu: NavMenuItemsDataBase
...
private fun initNavMenu( ... ){
navMenu = NavMenuItemsDataBase( this )
...
}
...
}
Agora precisamos invocar o método de navMenu que salva o ID do último item de fragmento acionado. Para isso vamos criar um novo método na MainActivity que vai conter todo o algoritmo de seleção de fragmento.
Crie o método itemCallFragment() com o código a seguir:
...
private fun itemCallFragment(
key: Long,
callbackRemoveSelection: ()->Unit
){
/*
* Para garantir que somente um item de lista se
* manterá selecionado, é preciso acessar o objeto
* de seleção da lista de itens de usuário conectado
* para então remover qualquer possível seleção
* ainda presente nela. Sempre haverá somente um
* item selecionado, mas infelizmente o método
* clearSelection() não estava respondendo como
* esperado, por isso a estratégia a seguir.
* */
callbackRemoveSelection()
navMenu.saveLastSelectedItemFragmentID( this, key )
if( !navMenu.wasActivityItemFired( this ) ){
/*
* Somente permiti a real seleção de um fragmento e o
* fechamento do menu gaveta se o item de menu anterior
* selecionado não tiver sido um que aciona uma atividade.
* Caso contrário o fragmento já em tela deve continuar
* e o menu gaveta deve permanecer aberto.
* */
val fragment = getFragment( key )
replaceFragment( fragment )
/*
* Fechando o menu gaveta.
* */
drawer_layout.closeDrawer( GravityCompat.START )
}
else{
navMenu.saveIsActivityItemFired( this, false )
}
}
...
Note que temos mais um bloco condicional dentro dos algoritmos de seleção de fragmento. Isso para garantir que o fragmento que já estava aberto não seja recarregado quando a volta for de uma atividade acionada por um item de menu.
Esse recarregamento seria desnecessário e em alguns casos serviria somente para consumir banda de Internet do usuário.
Agora precisamos do algoritmo de itens de menu que acionam atividades.
Na MainActivity adicione o método itemCallActivity() com o código a seguir:
...
private fun itemCallActivity(
key: Long,
callbackRemoveSelection: ()->Unit
){
callbackRemoveSelection()
lateinit var intent : Intent
when( key ){
R.id.item_settings.toLong() -> {
intent = Intent(
this,
AccountSettingsActivity::class.java
)
intent.putExtra( User.KEY, user )
}
}
navMenu.saveIsActivityItemFired( this, true )
startActivity( intent )
}
...
Assim precisamos de um método que informa quando o item de menu acionado é ou não um item que aciona uma atividade. Ainda na MainActivity adicione o método isActivityCallInMenu() com o código abaixo:
...
/*
* Alguns itens do menu gaveta de usuário conectado acionam
* a abertura de uma atividade e não a abertura de um novo
* fragmento, dessa forma o método abaixo será útil em
* lógicas de negócio para informar quais são os itens que
* acionam atividades.
* */
fun isActivityCallInMenu( key: Long )
= when( key ){
R.id.item_settings.toLong() -> true
else -> false
}
...
Assim podemos partir para o novo código do método onItemStateChanged() da classe SelectObserverNavMenuItems, uma classe interna a MainActivity:
...
override fun onItemStateChanged(
key: Long,
selected: Boolean ) {
super.onItemStateChanged( key, selected )
/*
* Padrão Cláusula de Guarda para não seguirmos
* com o processamento em caso de item perdendo
* seleção. O processamento posterior ao condicional
* abaixo é somente para itens obtendo a seleção,
* selected = true.
* */
if( !selected ){
return
}
if( isActivityCallInMenu( key ) ) {
itemCallActivity( key, callbackRemoveSelection )
}
else {
itemCallFragment( key, callbackRemoveSelection )
}
}
...
Por fim precisamos do algoritmo responsável por acionar o último item de fragmento ativo antes do acionamento de um item de atividade.
Na MainActivity coloque o método onResume() com a seguinte configuração:
...
override fun onResume() {
super.onResume()
/*
* Se o último item de menu gaveta selecionado foi um
* que aciona uma atividade, então temos de colocar a
* seleção de item correta em menu gaveta, item que
* estava selecionado antes do acionamento do item que
* invoca uma atividade.
* */
if( navMenu.wasActivityItemFired( this ) ){
selectNavMenuItems.select(
navMenu.getLastSelectedItemFragmentID( this )
)
}
}
...
Somente o objeto de seleção de item selectNavMenuItems é que deve estar no algoritmo de onResume(), pois é nesta parte da lista de itens de menu gaveta que estão os itens que acionam fragmentos.
Assim podemos partir para os testes e resultados.
Testes e resultados
Com o Android Studio aberto, vá ao menu de topo e siga para "Build", logo depois clique em "Rebuid project". Ao final do rebuild execute o projeto em seu aparelho ou emulador Android de testes.
Note que o objeto user, presente na MainActivity, deve estar com a configuração de um usuário conectado (terceiro argumento como true):
...
val user = User(
"Thiengo Vinícius",
R.drawable.user,
true /* Usuário conectado. */
)
...
Acessando a área de configurações de conta de usuário e voltando a MainActivity, temos:
Com isso finalizamos mais um importante trecho da interface de usuário de nosso projeto Android de mobile-commerce.
Não esqueça de se inscrever na 📩 lista de emails do Blog, caso você ainda não seja inscrito, 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 criação da tela de configurações de conta de usuário e a atualização da atividade principal:
O projeto também pode ser acessado pelo GitHub em: https://github.com/viniciusthiengo/blueshoes-kotlin-android.
Conclusão
O desenvolvimento da tela de configurações de conta de usuário foi simples, certo? Porém os algoritmos que permitem o acesso a essa tela não foram tão triviais.
Mas com os recursos que conhecemos do Android foi possível trabalhar uma solução ainda viável sem remover códigos que estavam, a principio, dando também uma "dor de cabeça" (os códigos da SelectionTracker API principalmente).
De qualquer forma, a tela foi construída sem problemas e assim já podemos, "por que não?", seguirmos aos formulários do projeto vinculados a essa tela.
Caso você tenha dúvidas ou dicas para este projeto, incluindo uma melhor solução do que a utilizando a SharedPreferences API, deixe logo abaixo nos comentários ou me envie por e-mail, também respondo a dúvidas por lá.
Curtiu o conteúdo? Não esqueça de compartilha-lo. E, por fim, não deixe de se inscrever na 📩 lista de emails.
Abraço.
Fontes
Set drawable for DividerItemDecoration - Resposta de Jonathan Vicente
Comentários Facebook