Android About Page API Para Construir a Tela Sobre
(5254) (3)
CategoriasAndroid, Design, Protótipo
AutorVinÃcius Thiengo
VÃdeo aulas186
Tempo15 horas
ExercÃciosSim
CertificadoSim
CategoriaDesenvolvimento Web
Autor(es)Robert C. Martin
EditoraAlta Books
Edição1ª
Ano2023
Páginas416
Tudo bem?
Neste artigo vamos, passo a passo, estudar a API Android About Page, uma das APIs mais utilizadas para a construção da tela Sobre, ou tela de Informação, de aplicativos Android.
A API é simples e nos permite a construção da tela de Info em poucos minutos. Como projeto de exemplo vamos ao desenvolvimento da tela Sobre de um aplicativo Android de corretor de imóveis:
Antes de prosseguir, saiba que aqui utilizaremos o termo "API" como sinônimo de "biblioteca" (library).
E não esqueça de se inscrever 📩 na lista de e-mails do Blog para ter acesso aos conteúdos exclusivos sobre desenvolvimento Android.
A seguir os tópicos que estaremos abordando:
- Tela de informações - Sobre;
- Biblioteca Android About Page:
- Instalação da biblioteca;
- Tela simples, sem componentes sociais e de contato;
- Estrutura e modificação do layout;
- Enquadramento da imagem;
- Alinhamento da descrição;
- Redes, email, site e grupos;
- Item customizado;
- Sobrescrevendo o listener de clique;
- Pontos negativos;
- Pontos positivos;
- Considerações finais;
- Outras bibliotecas.
- Projeto Android:
- Construindo a about page screen:
- Slides;
- Vídeos;
- Conclusão;
- Fontes.
Tela de informações - Sobre
Apesar de não ser uma exigência na maioria dos projetos de aplicativos Android e também de não estar dentre as telas mais acessadas dentro dos apps, uma das maneiras mais simples de mostrar o seu trabalho é por meio de uma página Sobre no aplicativo.
Nessa tela entram informações da empresa, ou do desenvolvedor, além de vários pontos para contato. Pontos como: telefone; redes sociais; email; e até mesmo um ativador de rota em mapa.
Porém sabemos da correria do dia a dia no desenvolvimento de apps, devido a isso e também devido a importância das telas Sobre, alguns desenvolvedores criaram APIs que facilitam a colocação desse tipo de tela nos aplicativos, algo que não leva nem mesmo 10 minutos.
Obviamente, principalmente se você cria aplicativos para clientes, que em algum momento será exigido de ti uma tela Sobre personalizada, construída do zero, por exemplo.
Mas para os primeiros releases, sabendo da necessidade de entrega do aplicativo na data estipulada, utilizar uma API de terceiro que mantém uma qualidade aceitável, essa é uma excelente escolha de projeto, como informado: ao menos nos primeiros releases do aplicativo.
Biblioteca Android About Page
A API Android About Page é uma das mais utilizadas para o desenvolvimento de telas Sobre. Foi criada pelo marroquino Mehdi Sakout e tem, no momento da construção deste artigo, mais de 1600 estrelas no GitHub.
Como já informei em alguns artigos aqui do Blog: ter mais de 1000 estrelas é um ótimo parâmetro para utilizar uma API.
O outro parâmetro é a data do último commit, no caso da Android About Page o último foi em Março de 2018, data aceitável, tendo em mente que este tipo de API não tem muitos algoritmos críticos que necessitam de constante melhoria.
A Android About Page permite inúmeros elementos em tela e é de simples instalação e configuração em qualquer domínio de problema.
Instalação da biblioteca
A API está presente no JCenter, Maven e JitPack, com isso, no Gradle Nível de Aplicativo, ou build.gradle (Module: app), devemos somente colocar a referência à última versão estável da API, como a seguir:
...
dependencies {
...
implementation 'com.github.medyo:android-about-page:1.2.4'
}
Na época da construção deste conteúdo a última versão estável era a 1.2.4.
Tela simples, sem componentes sociais e de contato
Para uma tela bem simples, apenas com imagem principal e descrição, você pode invocar a API como a seguir:
...
val aboutPage = AboutPage( this )
.setImage( R.drawable.car )
.setDescription( "Simples descrição para a about page de teste do artigo." )
.create()
setContentView( aboutPage )
...
O método setContentView() será utilizado para que todo o layout da API seja colocado em tela, logo, temos que a construção e vinculo de um objeto AboutPage tende a vir no onCreate() quando o contexto é de atividade, ou no onCreateView() quando o contexto é de fragmento.
Executando o projeto com o código anterior, temos:
Estrutura e modificação do layout
Infelizmente a Android About Page API ainda é limitada em termos de customização de layout, principalmente em pontos simples, como:
- Enquadramento de imagem;
- Alinhamento de texto de descrição.
Há inúmeras opções, hardcoded, para que seja possível atualizar os pontos informados acima. A mais simples, segundo testes realizados, é entendendo a estrutura de layout da API e então acessando os componentes do layout, pelos seus IDs, e atualizando-os em código dinâmico.
Até o momento da construção deste conteúdo a estrutura XML do layout about_page.xml era como a seguir:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/about_About.wrapper">
<LinearLayout
style="@style/about_About.wrapper">
<LinearLayout
style="@style/about_sub_wrapper">
<ImageView
android:id="@+id/image"
style="@style/about_image"/>
<TextView
android:id="@+id/description"
style="@style/about_description"/>
</LinearLayout>
<View style="@style/about_separator"/>
<LinearLayout
android:id="@+id/about_providers"
style="@style/about_sub_wrapper"
android:padding="0dp">
<!-- Aqui entram os outros itens possíveis na About Page. -->
</LinearLayout>
</LinearLayout>
</ScrollView>
Há uma série de estilos definidos no arquivo styles.xml da biblioteca, mas confesso que tentar qualquer atualização por meio de sobrescrita de estilo tende a exigir mais linhas de código do que acessar um componente por ID e então atualiza-lo em código dinâmico.
A seguir o diagrama da estrutura anterior:
Para ficar em dia com a estrutura do layout principal da Android About Page API, mantenha acesso a este layout em: about_page.xml. Com isso podemos facilmente atualizar os dois principais componentes apresentados do layout da biblioteca.
Enquadramento da imagem
Primeiro saiba que caso em sua About Page não tenha a necessidade de uma imagem de topo como proposto pela API, basta não invocar o método setImage().
Outro ponto importante é saber o tamanho exato da imagem para a correta replicação nos folders drawable.
Na documentação oficial a imagem de exemplo tem 500dp de largura. Mas realizando alguns testes foi encontrado como tamanho exato a medição de 375dp para a largura. A altura seguirá a proporção necessária de acordo com a imagem em uso.
No código a seguir temos o enquadramento da imagem, que tem 500dp de largura, para que ela não exceda o espaço de topo e de fundo:
...
val aboutPage = AboutPage( this )
.setImage( R.drawable.car )
.setDescription( "Simples descrição para a about page de teste do artigo." )
.create()
setContentView( aboutPage )
/*
* Todas as atualizações dinâmicas de Views devem vir
* após a invocação de setContentView().
* */
/*
* CENTER_CROP para que o enquadramento seja exato ao
* espaço de conteúdo do ImageView em layout.
* */
val image = findViewById<ImageView>( R.id.image )
image.scaleType = ImageView.ScaleType.CENTER_CROP
/*
* Removendo a margem padrão de topo e atualizando a
* margem de fundo para ser compatível com as margens
* laterais de 16dp.
* */
val layoutParams = image.layoutParams as LinearLayout.LayoutParams
layoutParams
.setMargins(
0, /* Esquerda. */
0, /* Topo. */
0, /* Direita. */
(16 * resources.getDisplayMetrics().density).toInt() /* Fundo. */
)
...
Executando o projeto com o código anterior, temos:
A seguir duas boas ferramentas online e gratuitas para trabalho com imagens no Android:
- Pixplicity | DP/PX converter: para a rápida conversão de DPs para pixels e assim entender os tamanhos de imagens que você precisará, ao menos nos folders drawable;
- NativeScript Image Builder: para gerar as imagens com os tamanhos corretos. Carregue sempre uma imagem PNG e com o tamanho XXXHDPI para que todas as outras sejam geradas corretamente.
Alinhamento da descrição
Primeiro saiba que se você não fornecer qualquer descrição, uma descrição padrão é utilizada. Eu sei, este comportamento está mais para bug do que para algo comum.
Então, em caso de não ter uma descrição, é preciso acessar o TextView de descrição e então mudar a visibilidade dele, como a seguir:
...
val description = findViewById<TextView>( R.id.description )
description.visibility = View.GONE
...
Thiengo, mas e seu eu fornecesse uma String vazia ao método setDescription()?
Neste caso a descrição padrão da API é a que seria utilizada.
Voltando o foco no alinhamento... com o código a seguir conseguimos modificar o alinhamento padrão que é central para um alinhamento à esquerda:
...
val aboutPage = AboutPage( this )
.setImage( R.drawable.car )
.setDescription( "Simples descrição para a about page de teste do artigo." )
.create()
setContentView( aboutPage )
val description = findViewById<TextView>( R.id.description )
description.gravity = Gravity.START
...
Executando o projeto de testes com o código anterior, temos:
Redes, email, site e grupos
A biblioteca Android About Page fornece algumas interfaces para o vinculo das principais redes sociais, email, Web site, página na Google Play Store e GitHub. Veja o código a seguir:
...
val aboutPage = AboutPage( this )
.setImage( R.drawable.car )
.setDescription( "Simples descrição para a about page de teste do artigo." )
/*
* O método addGroup() permite a adição de um cabeçalho
* antes das opções. Mas fique ciente que ele é
* adicionado exatamente na posição de chamada no
* Builder do objeto AboutPage.
* */
.addGroup( "Entre em contato" )
.addEmail( "thiengocalopsita@gmail.com" )
.addWebsite( "https://www.thiengo.com.br" )
.addGroup( "Nossas mídias sociais" )
/*
* O código para acesso ao canal no YouTube não está mais
* atualizado, pois ainda é utilizada a URL
* "https://www.youtube.com/channel/" ao invés de
* "https://www.youtube.com/user/". Sendo assim a melhor
* opção para canal do YouTube é criar um objeto Element
* com o listener de clique sobrescrito ou então fornecer
* uma nova Intent.
* */
.addYoutube( "thiengoCalopsita" )
.addFacebook( "thiengoCalopsita" )
.addTwitter( "thiengoCalops" )
.addInstagram( "cbf_futebol" )
.addGroup( "Para você desenvolvedor" )
.addGitHub( "viniciusthiengo" )
.addPlayStore( "br.thiengocalopsita" )
.create()
setContentView( aboutPage )
...
Como também comentado no código anterior, o addYoutube() está com um código interno ainda antigo, sendo assim nenhum canal é aberto quando utilizando este método.
Como solução temos a possibilidade de criar uma opção personalizada utilizando um objeto Element, algo que discutiremos na próxima seção.
Note que a ordem empregada em código para os métodos add é a ordem de apresentação em tela.
Executando o projeto com o código anterior e clicando na opção do Facebook, temos:
Se o aplicativo não estiver instalado no device, a versão Web da rede acionada é aberta no navegador do aparelho.
Um ponto negativo para o método addGroup() é a não possibilidade de mudar o alinhamento do texto, mas é possível resolver isso utilizando um Element neutro, o que faremos ainda na próxima seção.
Você deve ter notado que os rótulos já estão todos em português. Até o momento da construção deste artigo a Android About Page API tinha suporte para 30 idiomas, incluindo o português do Brasil.
Caso você queira colocar os seus próprios rótulos, os métodos add têm uma sobrecarga onde o segundo argumento é o rótulo utilizado. Veja o código a seguir:
...
val aboutPage = AboutPage( this )
.setImage( R.drawable.car )
.setDescription( "Simples descrição para a about page de teste do artigo." )
/*
* De todos os método add, somente os addItem() e addGroup()
* é que não têm a sobrecarga com um segundo argumento.
* */
.addFacebook( "thiengoCalopsita", "Acesse nosso Facebook" )
.create()
setContentView( aboutPage )
...
Executando o código anterior, temos:
Item customizado
Ainda há inúmeras opções de redes não atendidas de maneira nativa pela interface publica da Android About Page, sendo assim, para adicionarmos a versão correta do YouTube, por exemplo, podemos ter o seguinte código:
...
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse( "https://youtube.com/user/thiengoCalopsita" )
)
intent.setPackage( "com.google.android.youtube" )
val youTube = Element()
/*
* Definição do rótulo do novo item.
* */
.setTitle("Acesse nosso canal")
/*
* Aqui foi aproveitado o ícone do YouTube que já vem
* na Android About Page API: R.drawable.about_icon_youtube.
* Mas você pode fornecer o seu respeitando as
* dimensões 24dp x 24dp.
* */
.setIconDrawable( R.drawable.about_icon_youtube )
/*
* Cor de preenchimento do ícone. Aqui também utilizei a
* referência de cor já fornecida pela API, mas você poderia
* fornecer a sua cor definida em /res/values/colors.xml.
* */
.setIconTint( R.color.about_youtube_color )
/*
* Alinhamento do bloco com o ícone e rótulo. Aqui
* definido como alinhado à esquerda (START).
* */
.setGravity( Gravity.START )
/*
* Intent que será acionado assim que o Element for
* ativado pelo toque do usuário.
* */
.setIntent( intent )
val aboutPage = AboutPage( this )
.setImage( R.drawable.car )
.setDescription( "Simples descrição para a about page de teste do artigo." )
.addFacebook( "thiengoCalopsita", "Acesse nosso Facebook" )
.addItem( youTube )
.create()
setContentView( aboutPage )
...
Executando o código anterior e acionando a opção de YouTube, temos:
Atualizando o código de redes da seção anterior, para ao invés de utilizar addGroup() utilizar addItem() e assim termos cabeçalhos centralizados:
...
val aboutPage = AboutPage( this )
.setImage( R.drawable.car )
.setDescription( "Simples descrição para a about page de teste do artigo." )
.addItem(
Element()
.setTitle( "Entre em contato" )
.setGravity( Gravity.CENTER )
)
.addEmail( "thiengocalopsita@gmail.com" )
.addWebsite( "https://www.thiengo.com.br" )
.addItem(
Element()
.setTitle( "Nossas mídias sociais" )
.setGravity( Gravity.CENTER )
)
.addFacebook( "thiengoCalopsita" )
.addTwitter( "thiengoCalops" )
.addInstagram( "cbf_futebol" )
.addItem(
Element()
.setTitle( "Para você desenvolvedor" )
.setGravity( Gravity.CENTER )
)
.addGitHub( "viniciusthiengo" )
.addPlayStore( "br.thiengocalopsita" )
.create()
setContentView( aboutPage )
...
Executando o código anterior, temos:
O ponto negativo do código com setIntent() é que se o aplicativo não estiver no aparelho do usuário, sabendo que setPackage() foi configurado no objeto Intent, nada ocorre, nem mesmo uma mensagem solicitando a instalação do app.
Como uma melhor solução temos a possibilidade de sobrescrita do listener de clique.
Sobrescrevendo o listener de clique
Para sobrescrever o listener de clique e assim fornecer mais opções quando um aplicativo não estiver no aparelho, faça como a seguir:
...
val youTube = Element()
.setTitle( "Acesse nosso canal" )
.setIconDrawable( R.drawable.about_icon_youtube )
.setIconTint( R.color.about_youtube_color )
.setGravity( Gravity.START )
/*
* Sobrescrevendo o listener de clique. A outra opção
* de sobrescrita é fornecendo um objeto do tipo
* View.OnClickListener como argumento do método
* setOnClickListener().
* */
.setOnClickListener {
var intent = Intent(
Intent.ACTION_VIEW,
Uri.parse( "https://youtube.com/user/thiengoCalopsita" )
)
intent.setPackage( "com.google.android.youtube" )
/*
* Se o aplicativo do YouTube não estiver presente
* no aparelho, então tente a abertura da página
* via outro aplicativo, que provavelmente será o
* navegador principal do device.
* */
if( intent.resolveActivity( packageManager ) == null ){
/* Sem a configuração de package. */
intent = Intent(
Intent.ACTION_VIEW,
Uri.parse( "https://youtube.com/user/thiengoCalopsita" )
)
}
startActivity( intent )
}
val aboutPage = AboutPage( this )
.setImage( R.drawable.car )
.setDescription( "Simples descrição para a about page de teste do artigo." )
.addItem( youTube )
.create()
setContentView( aboutPage )
...
Executando o aplicativo em um aparelho sem o app do YouTube, mas com o Chrome sendo o navegador padrão, temos:
Note que todo o código para o trabalho com a opção de YouTube também é válido para o envio de SMS, para o WhatsApp e outros aplicativos, digo, os códigos desta seção e o da seção anterior.
Pontos negativos
- Características simples, que poderiam ser alteradas com a simples chamada de métodos específicos da API, como o alinhamento da descrição ou o alinhamento do cabeçalho de grupo, características assim exigem acesso a Views via findViewById() ou à criação de elementos customizados;
- É preciso atualização, ao menos, para a adição de mais opções de aplicativos, como o WhatsApp, por exemplo. Além da correção do código do YouTube;
- Poderia ter uma opção de vídeo ao invés de somente imagem;
- A documentação é bem simples e em nenhum ponto fala sobre a ordenação das opções de acordo com o posicionamento de cada método add();
- Há métodos de mudança de cor de ícone, de uso aleatório de cor em ícone e de definição de valor que são muito específicos de domínio e poderiam ser removidos.
Pontos positivos
- Realmente é muito simples adicionar uma about page completa com a Android About Page API, diferente de algumas outras bibliotecas de tela Sobre, que exigem a leitura de uma documentação extensa;
- A opção de criar um elemento customizado, contendo até mesmo um listener de clique personalizado, essa opção faz com que muitos problemas de algoritmo antigo ou faltante sejam facilmente contornados;
- O trabalho com a ordenação de acordo com o fornecimento dos métodos add() facilita a construção da about page de maneira personalizada.
Considerações finais
Para qualquer aplicativo, como o conteúdo sobre compartilhamento discutido aqui no Blog, é importante ter a tela de informações extras, a tela de Sobre.
É por lá que o contato com a empresa / desenvolvedor é facilitado, além de um pequeno resumo sobre a trajetória desse.
A Android About Page API, apesar dos problemas ainda existentes, é a biblioteca mais simples para a construção rápida e robusta de uma about page.
Outras bibliotecas
A seguir algumas outras bibliotecas, também populares, de about page:
- About Libraries - o foco é na divulgação das bibliotecas utilizadas no desenvolvimento do aplicativo;
- Material About - biblioteca de about page mais genérica, certamente a concorrente direta da Android About Page API;
- Várias outras opções de about API no Android-Arsenal.
Projeto Android
Como projeto de exemplo teremos parte de um aplicativo de um corretor de imóveis. Mais precisamente teremos exatamente a tela de Sobre do app para ser desenvolvida.
Com isso conseguiremos colocar a Android About Page API dentro de um contexto real.
O projeto está dividido em duas partes:
- Na primeira parte vamos ao desenvolvimento da parte do aplicativo ainda sem a About Page API;
- Na segunda parte adicionaremos a API em estudo para a fácil inserção de uma tela Sobre.
O projeto de exemplo está presente no GitHub a seguir: https://github.com/viniciusthiengo/thiengo-corretor-de-imoveis-kotlin-android.
Não deixe de acompanhar o tutorial do aplicativo, pois ainda mais pontos do desenvolvimento Android serão abordados.
Protótipo estático
A seguir as telas do protótipo estático do projeto:
Tela de entrada | Menu gaveta |
Tela Sobre - primeira metade | Tela Sobre - segunda metade |
Iniciando o projeto
Em seu Android Studio inicie um novo projeto Kotlin:
- Nome da aplicação: Thiengo - Corretor de Imóveis;
- API mínima: 16 (Android Jelly Bean);
- Atividade inicial: Navigation Drawer Activity;
- Nome da atividade inicial: MainActivity;
- Para todos os outros campos, mantenha os valores já definidos por padrão.
Ao final da primeira parte do projeto teremos a seguinte arquitetura no Android Studio:
Configurações Gradle
A seguir as configurações do Gradle Nível de Projeto, ou build.gradle (Project: ThiengoCorretordeImveis):
buildscript {
ext.kotlin_version = '1.3.0'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Então a configuração do Gradle Nível de Aplicativo, ou build.gradle (Module: app):
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
defaultConfig {
applicationId "thiengo.com.br.thiengo_corretordeimveis"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
}
Configurações AndroidManifest
A seguir as configurações do AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="thiengo.com.br.thiengo_corretordeimveis">
<application
android:hardwareAccelerated="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Configurações de estilo
Para as definições de tema e estilo, vamos iniciar com o arquivo de cores, /res/values/colors.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#9E9E9E</color>
<color name="colorPrimaryDark">#616161</color>
<color name="colorAccent">#093F8C</color>
<color name="colorNavigationView">#F5F5F6</color>
<color name="colorItemNormal">#777777</color>
<color name="colorPhoneLogo">#888888</color>
<color name="colorWhatsAppLogo">#2BB201</color>
<color name="colorLinkedInLogo">#0077B5</color>
</resources>
Importante: para evitar a demasia de código na segunda parte do projeto, nesta seção já colocaremos todos os dados estáticos que serão referenciados somente na segunda parte da construção do app de exemplo.
Agora o simples arquivo de dimensões, /res/values/dimens.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
Então o arquivo de Strings, /res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Thiengo - Corretor de Imóveis</string>
<string name="navigation_drawer_open">Abrir menu gaveta</string>
<string name="navigation_drawer_close">Fechar menu gaveta</string>
<string name="whatsapp_needed_info">
Instale o WhatsApp em seu aparelho.
</string>
<string name="label_all_properties">Todos os imóveis</string>
<string name="label_new_releases">Lançamentos</string>
<string name="label_below_market_value">Abaixo do valor de mercado</string>
<string name="label_alphaville">Alphaville</string>
<string name="label_contact">Contactar corretor agora</string>
<string name="label_about">Sobre o corretor</string>
<string name="about_title">Sobre o corretor</string>
<string name="about_description">
Thiengo é corretor de imóveis a 10 anos atendendo nos mais
diferentes tipos de segmentos e classes.
\n\n
Ganhou 14 prêmios de melhor vendedor e de vendedor mais
influente no ramo de imóveis em condomínios fechados.
\n\n
Certamente Thiengo tem o melhor imóvel para você e sua
família, ou então para as suas metas de investimento de
longo prazo.
</string>
<string name="about_contact_group_title">Entre em contato</string>
<string name="about_work_group_title">Mais sobre meu trabalho</string>
<string name="about_phone_title">Ligar</string>
<string name="about_label_email">Meu melhor e-mail</string>
<string name="about_label_website">Visite meu Web site</string>
<string name="about_label_youtube">Tudo sobre imóveis</string>
<string name="about_label_facebook">Super promoções</string>
<string name="about_label_twitter">Atualização diária</string>
<string name="about_whats_app_title">Lhe respondo agora</string>
<string name="about_linked_in_title">Minhas recomendações</string>
</resources>
Assim o arquivo geral de definição de estilo, tema, do aplicativo, /res/values/styles.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
Estilo padrão, aplicado em todo o projeto.
-->
<style
name="AppTheme"
parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowBackground">@drawable/background</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<!--
Para que a barra de topo padrão não seja utilizada e
assim somente o AppBarLayout junto ao Toolbar possam ser
usados.
-->
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<!--
Para o correto enquadramento do AppBarLayout.
-->
<style
name="AppTheme.AppBarOverlay"
parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
<!--
Utilizado para a correta apresentação de menus de pop-up
em barra de topo.
-->
<style
name="AppTheme.PopupOverlay"
parent="ThemeOverlay.AppCompat.Light"/>
</resources>
Então o arquivo com algumas definições de estilo para aparelhos com o Android API 21, Lollipop, ou superior. Segue /res/values-v21/styles.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
Para que a barra de topo padrão não seja utilizada e
assim somente o AppBarLayout junto ao Toolbar possam ser
usados. Somando a isso a aplicação de transparência na
statusBar.
-->
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
Fragmento da tela Sobre
Com os códigos dinâmicos, Kotlin, vamos iniciar com a classe AboutFragment que representa a tela sobre do aplicativo, porém ela ainda está sem as configurações de about page. Coloque essa nova classe na raiz do projeto:
class AboutFragment : Fragment() {
companion object {
const val NUMBER = "27999887766"
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
/* TODO */
return TextView( activity )
}
/*
* Método responsável por conter o algoritmo que invoca
* o código de atualização de título da atividade.
* */
override fun onResume() {
super.onResume()
(activity as MainActivity)
.updateActivityTitle( getString( R.string.about_title ) )
}
}
Vamos seguir com um fragmento, pois a atividade principal terá um menu gaveta, e no contexto Android os menus gaveta são comumente utilizados junto a fragmentos.
O NUMBER é fictício e será referenciado duas vezes em projeto, sendo assim, tendo em mente que os códigos na about page praticamente não sofrem com constantes atualizações, foi optado por deixar esse dado no fragmento acima.
Atividade principal
Para a MainActivity vamos iniciar com o XML de conteúdo, /res/layout/app_bar_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="@+id/fl_frag_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
Assim o diagrama do layout anterior:
Agora o layout principal, que também referencia o anterior. Segue /res/layout/activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@color/colorNavigationView"
android:fitsSystemWindows="true"
app:itemBackground="@drawable/nav_item_background"
app:itemIconTint="@drawable/nav_icon_text"
app:itemTextColor="@drawable/nav_icon_text"
app:menu="@menu/activity_main_drawer"/>
</android.support.v4.widget.DrawerLayout>
Antes de partirmos para o diagrama do layout anterior, ainda temos de estudar os arquivos adicionados via <NavigationView>. Começando pelos de estilo.
A seguir o arquivo responsável por manter a cor de background correta no item, quando selecionado ou não. Segue /res/drawable/nav_item_background.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<!--
A ordem dos itens de um arquivo <selector> deve ser
seguida de forma estrita, pois caso contrário os efeitos
esperados não ocorrerão.
-->
<!-- Estado "Selecionado" -->
<item
android:drawable="@color/colorAccent"
android:state_checked="true" />
<!-- Estado "Pressionado" -->
<item
android:drawable="@color/colorAccent"
android:state_pressed="true" />
<!-- Estado "Normal", não selecionado -->
<item android:drawable="@android:color/transparent" />
</selector>
Agora o arquivo XML responsável por manter as cores corretas do ícone e do texto do item selecionado, ou não, de menu gaveta. Segue /res/drawable/nav_icon_text.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Estado "Selecionado" -->
<item
android:color="@android:color/white"
android:state_checked="true" />
<!-- Estado "Pressionado" -->
<item
android:color="@android:color/white"
android:state_pressed="true" />
<!-- Estado "Normal", não selecionado -->
<item android:color="@color/colorItemNormal" />
</selector>
Com os arquivos de estilo para itens do menu gaveta apresentados anteriormente, temos o seguinte resultado:
Agora o arquivo de itens de menu, /res/menu/activity_main_drawer.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_all_properties"
android:icon="@drawable/ic_office_building_black_24dp"
android:title="@string/label_all_properties"/>
<item
android:id="@+id/nav_new_releases"
android:icon="@drawable/ic_brightness_7_black_24dp"
android:title="@string/label_new_releases"/>
<item
android:id="@+id/nav_below_market_value"
android:icon="@drawable/ic_chart_bar_black_24dp"
android:title="@string/label_below_market_value"/>
<item
android:id="@+id/nav_alphaville"
android:icon="@drawable/ic_shield_home_black_24dp"
android:title="@string/label_alphaville"/>
<item
android:id="@+id/nav_contact"
android:icon="@drawable/ic_whatsapp_black_24dp"
android:title="@string/label_contact"/>
<item
android:id="@+id/nav_about"
android:checked="true"
android:icon="@drawable/ic_information_black_24dp"
android:title="@string/label_about"/>
</group>
</menu>
Assim o diagrama do layout activity_main.xml:
Agora o código Kotlin da MainActivity:
class MainActivity :
AppCompatActivity(),
NavigationView.OnNavigationItemSelectedListener {
override fun onCreate( savedInstanceState: Bundle? ) {
super.onCreate( savedInstanceState )
setContentView( R.layout.activity_main )
setSupportActionBar( toolbar )
val toggle = ActionBarDrawerToggle(
this,
drawer_layout,
toolbar,
R.string.navigation_drawer_open,
R.string.navigation_drawer_close
)
drawer_layout.addDrawerListener( toggle )
toggle.syncState()
nav_view.setNavigationItemSelectedListener( this )
aboutPage()
}
override fun onBackPressed() {
if( drawer_layout.isDrawerOpen( GravityCompat.START ) ){
drawer_layout.closeDrawer( GravityCompat.START )
}
else {
super.onBackPressed()
}
}
override fun onNavigationItemSelected( item: MenuItem ): Boolean {
when (item.itemId) {
R.id.nav_contact -> {
whatsAppHelp()
}
else -> {
aboutPage()
}
}
drawer_layout.closeDrawer( GravityCompat.START )
/*
* Para não mudar o item selecionado em menu gaveta. Status
* útil para nosso exemplo de única tela.
* */
return false
}
/*
* Método ouvidor para permitir que o usuário entre em contato
* com o WhatsApp correto com apenas um acionamento em tela.
* */
fun whatsAppHelp(){
/* O número abaixo é fictício. */
val whatsAppUri = Uri.parse( "smsto:${AboutFragment.NUMBER}" )
val intent = Intent( Intent.ACTION_SENDTO, whatsAppUri )
intent.setPackage( "com.whatsapp" )
/*
* Garantindo que a Intent somente será acionada se o
* aplicativo WhatsApp estiver presente no aparelho.
* */
if( intent.resolveActivity( packageManager ) != null ){
startActivity( intent )
}
else{
Toast
.makeText(
this,
getString(R.string.whatsapp_needed_info),
Toast.LENGTH_SHORT
)
.show()
}
}
/*
* Método responsável por invocar a AboutFragment como
* o fragmento atual em tela.
* */
private fun aboutPage(){
val transaction = supportFragmentManager.beginTransaction()
val fragment = AboutFragment()
transaction.replace( R.id.fl_frag_container, fragment )
transaction.commit()
}
/*
* Método responsável por conter o código de atualização
* de título da atividade. Seria invocado em todos os
* fragmentos relacionados aos itens de menu gaveta.
* */
fun updateActivityTitle( title: String ){
toolbar.title = title
}
}
Construindo a about page screen
O que temos até o momento é o seguinte:
Na AboutFragment teremos como parte do conteúdo:
- Imagem principal da logo do corretor de imóveis - enquadrada corretamente;
- Pequena descrição sobre a trajetória do corretor - alinhada à esquerda;
- Como canais de contato - na ordem apresentada:
- Telefone;
- WhatsApp;
- Email;
- Site.
- Como mídias sociais - também na ordem apresentada:
- Também terá de ter itens de cabeçalho centralizados.
Instalando a biblioteca
No Gradle Nível de Aplicativo, build.gradle (Module: app), adicione a referência a seguir e sincronize o projeto:
...
dependencies {
...
implementation 'com.github.medyo:android-about-page:1.2.4'
}
Adicionando itens nativos
No método onCreateView() da AboutFragment vamos primeiro adicionar o que não precisa de configuração extra, como no caso de criação de objetos Element. Segue:
...
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val aboutPage = AboutPage( activity )
.setImage( R.drawable.thiengo_corretor ) /* 375dp. */
.setDescription( getString(R.string.about_description) )
.addEmail(
"thiengocalopsita@gmail.com",
getString(R.string.about_label_email)
)
.addWebsite(
"https://www.thiengo.com.br",
getString(R.string.about_label_website)
)
.addFacebook(
"thiengoCalopsita",
getString(R.string.about_label_facebook)
)
.addTwitter(
"thiengoCalops",
getString(R.string.about_label_twitter)
)
.create()
return aboutPage
}
...
O básico que poderia ser adicionado sem muito código extra foi adicionado.
Enquadrando imagem e alinhando descrição
Ainda no onCreateView() adicione o código em destaque:
...
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
...
/*
* Colocando o posicionamento do texto de descrição à
* esquerda, sendo que o padrão é no centro.
* */
val description = aboutPage.findViewById<TextView>( R.id.description )
description.gravity = Gravity.START
/*
* Acessando o ImageView principal da AboutPage API
* para remover a margem de topo e colocar uma margem
* de 16dp no fundo.
* */
val image = aboutPage.findViewById<ImageView>( R.id.image )
val layoutParams = image.layoutParams as LinearLayout.LayoutParams
layoutParams.topMargin = 0
layoutParams.bottomMargin = (16 * activity!!.resources.getDisplayMetrics().density).toInt() /* Colocando em DP. */
return aboutPage
}
...
Itens de cabeçalho
Para itens de cabeçalho centralizados, utilizaremos objetos Element e o método addItem(). Em AboutFragment adicione o método a seguir:
...
private fun getItemGroup( titleId: Int )
= Element()
.setTitle( getString( titleId ) )
.setGravity( Gravity.CENTER )
...
Agora no onCreateView() adicione os códigos em destaque:
...
val aboutPage = AboutPage( activity )
.setImage( R.drawable.thiengo_corretor ) /* 375dp. */
.setDescription( getString(R.string.about_description) )
.addItem(
getItemGroup( R.string.about_contact_group_title )
)
.addEmail(
"thiengocalopsita@gmail.com",
getString(R.string.about_label_email)
)
.addWebsite(
"https://www.thiengo.com.br",
getString(R.string.about_label_website)
)
.addItem(
getItemGroup( R.string.about_work_group_title )
)
.addFacebook(
"thiengoCalopsita",
getString(R.string.about_label_facebook)
)
.addTwitter(
"thiengoCalops",
getString(R.string.about_label_twitter)
)
.create()
...
Itens de contato
Para a categoria de itens de contato, ainda faltam: telefone; e WhatsApp. Na AboutFragment adicione os métodos a seguir:
...
private fun getItemPhone()
= Element()
.setTitle( getString(R.string.about_phone_title) )
.setIconTint( R.color.colorPhoneLogo )
.setGravity( Gravity.START )
.setIconDrawable( R.drawable.ic_phone_in_talk_black_24dp )
.setOnClickListener {
val intent = Intent( Intent.ACTION_DIAL )
intent.setData( Uri.parse("tel:$WHATS_APP_NUMBER") )
startActivity( intent )
}
private fun getItemWhatsApp()
= Element()
.setTitle( getString(R.string.about_whats_app_title) )
.setIconTint( R.color.colorWhatsAppLogo )
.setGravity( Gravity.START )
.setIconDrawable( R.drawable.ic_whatsapp_black_24dp )
.setOnClickListener {
(activity as MainActivity).whatsAppHelp()
}
...
Agora no onCreateView() adicione os códigos em destaque:
...
val aboutPage = AboutPage( activity )
.setImage( R.drawable.thiengo_corretor ) /* 375dp. */
.setDescription( getString(R.string.about_description) )
.addItem(
getItemGroup( R.string.about_contact_group_title )
)
.addItem( getItemPhone() )
.addItem( getItemWhatsApp() )
.addEmail(
"thiengocalopsita@gmail.com",
getString(R.string.about_label_email)
)
.addWebsite(
"https://www.thiengo.com.br",
getString(R.string.about_label_website)
)
.addItem(
getItemGroup( R.string.about_work_group_title )
)
.addFacebook(
"thiengoCalopsita",
getString(R.string.about_label_facebook)
)
.addTwitter(
"thiengoCalops",
getString(R.string.about_label_twitter)
)
.create()
...
Itens sociais
Para as mídias sociais ainda não adicionadas (YouTube e LinkedIn) vamos a construção de métodos específicos a elas. Na AboutFragment adicione os métodos e constantes a seguir:
...
companion object {
...
const val YOU_TUBE_URL = "https://youtube.com/user/thiengoCalopsita"
const val LINKED_IN_ID = "vinícius-thiengo-5179b180"
}
...
private fun getItemYouTube()
= Element()
.setTitle( getString(R.string.about_label_youtube) )
.setIconTint( R.color.about_youtube_color )
.setIconDrawable( R.drawable.about_icon_youtube )
.setOnClickListener {
var intent = Intent(
Intent.ACTION_VIEW,
Uri.parse( YOU_TUBE_URL )
)
intent.setPackage( "com.google.android.youtube" )
/*
* Caso o aplicativo no YouTube não esteja
* instalado, abre o canal via navegador
* mobile.
* */
if( intent.resolveActivity( activity!!.packageManager ) == null ){
intent = Intent(
Intent.ACTION_VIEW,
Uri.parse( YOU_TUBE_URL )
)
}
activity!!.startActivity( intent )
}
private fun getItemLinkedIn()
= Element()
.setTitle( getString(R.string.about_linked_in_title) )
.setIconTint( R.color.colorLinkedInLogo )
.setGravity( Gravity.START )
.setIconDrawable( R.drawable.ic_linkedin_box_black_24dp )
.setOnClickListener {
var intent = Intent(
Intent.ACTION_VIEW,
Uri.parse( "linkedin://profile/$LINKED_IN_ID" )
)
intent.setPackage( "com.linkedin.android" )
/*
* Caso o aplicativo no LinkedIn não esteja
* instalado, abre o perfil via navegador
* mobile.
* */
if( intent.resolveActivity( activity!!.packageManager ) == null ){
intent = Intent(
Intent.ACTION_VIEW,
Uri.parse( "http://www.linkedin.com/profile/view?id=$LINKED_IN_ID" )
)
}
activity!!.startActivity( intent )
}
...
Agora no onCreateView() adicione os códigos em destaque:
...
val aboutPage = AboutPage( activity )
.setImage( R.drawable.thiengo_corretor ) /* 375dp. */
.setDescription( getString(R.string.about_description) )
.addItem(
getItemGroup( R.string.about_contact_group_title )
)
.addItem( getItemPhone() )
.addItem( getItemWhatsApp() )
.addEmail(
"thiengocalopsita@gmail.com",
getString(R.string.about_label_email)
)
.addWebsite(
"https://www.thiengo.com.br",
getString(R.string.about_label_website)
)
.addItem(
getItemGroup( R.string.about_work_group_title )
)
.addItem( getItemYouTube() )
.addFacebook(
"thiengoCalopsita",
getString(R.string.about_label_facebook)
)
.addTwitter(
"thiengoCalops",
getString(R.string.about_label_twitter)
)
.addItem( getItemLinkedIn() )
.create()
...
Testes e resultados
Com o Android Studio aberto, vá em "Build", então em "Rebuid project". Ao final do rebuild execute o aplicativo em seu aparelho Android de testes.
Acessando a opção de YouTube, temos:
Com isso finalizamos um simples, mas importante conteúdo para aplicativos Android. A biblioteca Android About Page foi coberta de acordo com o que era útil. Qualquer dúvida ou sugestão, deixe nos comentários que logo eu lhe respondo.
Não esqueça de se inscrever na 📩 lista de e-mails do Blog para receber em primeira mão os conteúdos exclusivos sobre desenvolvimento Android.
Se inscreva também no canal do Blog em: YouTube Thiengo.
Slides
Abaixo os slides com o passo a passo de uso da Android About Page API:
Vídeos
Abaixo os vídeos com a atualização da about screen do aplicativo de corretor de imóveis:
Para acessar o projeto de exemplo entre no GitHub dele em: https://github.com/viniciusthiengo/thiengo-corretor-de-imoveis-kotlin-android.
Conclusão
Como já informado no decorrer do artigo e vídeo: o uso de uma tela Sobre em aplicativos Android aumenta não somente as chances de a empresa ou o desenvolvedor receber mais contatos profissionais, mas também passa um "maior ar" de profissionalismo ao projeto de app.
Certamente nos primeiros releases de qualquer projeto de aplicativo Android não é viável focar também no desenvolvimento, do zero, da tela Sobre.
Bibliotecas de terceiros como a Android About Page facilitam em muito a adição de uma about screen robusta, isso em poucos minutos.
Com isso finalizamos o artigo. Caso você tenha dúvidas ou sugestões, deixe abaixo nos comentários, logo abaixo.
Curtiu o conteúdo? Não esqueça de compartilha-lo. E por fim, não deixe de se inscrever na 📩 lista de e-mails.
Abraço.
Fontes
Documentação oficial Android About Page API
Android set the gravity for a TextView programmatically - Resposta de radiofrequency e z3ntu
Sending an Intent to browser to open specific URL [duplicate] - Resposta de aioobe
How to set margin of ImageView using code, not xml - Resposta de Key e Ahmed Salman Tahir
Alterando telas no Navigation Drawer - Resposta de Arubu
How can I open Linkedin application from my android app? - Resposta de philips77
Comentários Facebook