Início do Lado Tático e Barra de Topo Personalizada - YouTuber Android App - Parte 2
(2938) (10)
CategoriasAndroid, Design, Protótipo
AutorVinícius Thiengo
Vídeo aulas186
Tempo15 horas
ExercíciosSim
CertificadoSim
CategoriaEngenharia de Software
Autor(es)Kent Beck
EditoraNovatec
Edição1ª
Ano2024
Páginas112
Tudo bem?
Neste artigo vamos continuar com o nosso projeto de aplicativo Android para YouTubers. Aplicativo mobile completo.
Aqui vamos literalmente iniciar a parte tática do projeto, começar a codificar.
Vamos criar um simples app e já adicionar a nossa barra de topo personalizada.
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 segundo conteúdo do projeto de aplicativo Android para YouTubers:
- O que já temos até aqui:
- Iniciando um novo aplicativo:
- Assinatura de topo:
- Próximo conteúdo.
O que já temos até aqui
Se você chegou no projeto somente agora, saiba que está não é a primeira e nem mesmo a última parte, artigo já publicado.
Todo o roteiro está na listagem a seguir:
- Construa Um Aplicativo Android Completo Para YouTubers - Parte 1;
- Início do Lado Tático e Barra de Topo Personalizada - Parte 2 (você está aqui);
- Criando e Configurando o Menu Principal - Parte 3;
- Criando a Estrutura Base Das Telas Com Lista - Parte 4;
- Construindo os Fragmentos de Conteúdo Local - Parte 5;
- Banco de Dados Local Com a Room API - Parte 6;
- Construindo a Tela e a Lógica de Último Vídeo - Parte 7;
- Desenvolvendo a Tela e a Lógica de PlayLists - Parte 8;
- Vinculando Telas ao Menu Principal - Parte 9;
- Configurando Por Completo o Sistema de Notificação Push - Parte 10;
- Configurando a YouTube Data API Com a Biblioteca Retrofit - Parte 11;
- Configurando o WorkManager Para Requisições em Background - Parte 12;
- Testes e Resultados no Projeto Finalizado - Parte 13;
- Nós Temos Um Framework Em Mãos - Parte 14;
- Como e Onde Monetizar o Aplicativo Framework - Parte 15.
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.
Iniciando um novo aplicativo
Vamos primeiro criar um simples aplicativo no Android Studio IDE, com um atividade vazia, "Empty Activity".
Depois deste passo vamos prosseguir colocando no novo projeto todos os requisitos definidos no lado estratégico do projeto.
Com o seu ambiente de desenvolvimento aberto (Android Studio IDE), inicie um novo aplicativo Kotlin com as seguintes configurações:
- Nome da aplicação: Canal Vinícius Thiengo;
- API mínima: 21 (Android Lollipop) - mais de 94% dos aparelhos Android em mercado sendo atendidos;
- Atividade inicial: Empty Activity;
- Nome da atividade inicial: MainActivity;
- Para todos os outros campos, mantenha os valores já definidos por padrão.
Note que em "Package name" você deve preferencialmente utilizar o comum em seu ambiente de desenvolvimento. E não o meu, digo, o nome de pacote com o meu prefixo "thiengo.com.br".
Estou dando essa ênfase em "Package name", pois a partir desta segunda parte do projeto de aplicativo o nome raiz de pacote aparecerá em inúmeros pontos do código do app.
E se você estiver seguindo o projeto passo a passo, então essa será a única parte diferente entre o projeto dos conteúdos publicados aqui e à versão em seu ambiente de desenvolvimento.
Sendo assim, vamos aos arquivos de automação do projeto.
Configurações Gradle
A seguir a configuração inicial de ambos os arquivos Gradle do projeto.
Primeiro o arquivo Gradle Nível de Projeto, ou build.gradle (Project: Canal_Vinícius_Thiengo):
buildscript {
ext.kotlin_version = "1.4.0"
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Agora o arquivo 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 30
buildToolsVersion "30.0.0"
defaultConfig {
applicationId "thiengo.com.br.canalvinciusthiengo"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.0'
}
Para ambos os arquivos Gradle:
Se no momento em que você estiver implementando este projeto houverem versões mais atuais das bibliotecas, plugins e APIs referenciadas nesses arquivos de automação.
Então prossiga com as versões mais atuais, pois o projeto deverá funcionar sem problema algum.
Ao final dessa configuração, se o IDE exigir, sincronize o projeto.
Note que já adicionamos a biblioteca com.google.android.material.
Pois estaremos já nesta parte do projeto utilizando componentes visuais do Material Design (CardView, por exemplo) que estão dentro dela.
Strings de sistema
Nosso arquivo de Strings estáticas de projeto, ao menos neste início, estará bem "magrinho".
Em /res/values/strings.xml coloque:
<resources>
<string name="app_name">
Canal Vinícius Thiengo
</string>
</resources>
Lembrando que todas as Strings estáticas de projeto que podem sofrer tradução caso o aplicativo passe por internacionalização...
... todas essas devem entrar no arquivo de conteúdo estático próprio para Strings, strings.xml.
Configurações AndroidManifest
A seguir a configuração inicial de um dos principais arquivos de aplicativos Android, o AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="thiengo.com.br.canalvinciusthiengo">
<application
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=".ui.MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Você deve ter notado que na tag <activity> definimos que o aplicativo funcionará somente em modo portrait (na vertical), android:screenOrientation="portrait".
Está é uma limitação válida para este primeiro release. Tendo em mente que o propósito central do app é permitir que o usuário seja avisado sobre um novo vídeo no canal e então acione a abertura do vídeo no app nativo do YouTube.
Ou seja, o seguidor do canal que também é usuário do app não passará, a princípio, muitos minutos com o aplicativo aberto.
Vamos prosseguir, pois durante todo o projeto ainda voltaremos algumas vezes ao AndroidManifest.xml.
Ícones de abertura do app
Para os ícones de abertura de aplicativo, que ficam na área de apps do aparelho, também conhecidos como Launcher Icons.
Para esses teremos as duas versões esperadas e ainda utilizando imagens rasterizadas.
Primeiro o ícone em versão retangular, ic_launcher.png:
- /res/mipmap-mdpi/ic_launcher.png;
- /res/mipmap-hdpi/ic_launcher.png;
- /res/mipmap-xhdpi/ic_launcher.png;
- /res/mipmap-xxhdpi/ic_launcher.png;
- /res/mipmap-xxxhdpi/ic_launcher.png.
Então o ícone em versão circular, ic_launcher_round.png:
- /res/mipmap-mdpi/ic_launcher_round.png;
- /res/mipmap-hdpi/ic_launcher_round.png;
- /res/mipmap-xhdpi/ic_launcher_round.png;
- /res/mipmap-xxhdpi/ic_launcher_round.png;
- /res/mipmap-xxxhdpi/ic_launcher_round.png.
Não esqueça de fazer o download dos ícones acima e coloca-los nos corretos folders mipmap (sobrescrevendo os ícones atuais).
Os códigos XML de ícones adaptativos:
- /res/drawable/ic_launcher_background.xml;
- /res/drawable-v24/ic_launcher_foreground.xml;
- /res/mipmap-anydpi-v26/ic_launcher;
- /res/mipmap-anydpi-v26/ic_launche_round.
Esses podem ser removidos do projeto com segurança, pois não utilizaremos aqui.
Configurações de estilo
Agora a configuração inicial de cada um dos arquivos responsáveis pelo estilo, tema, do projeto.
Cores
Primeiro o simples (por enquanto) arquivo de cores.
Coloque as seguintes definições em /res/values/colors.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#FFFFFF</color>
<color name="colorPrimaryDark">#FFFFFF</color>
<color name="colorAccent">#595959</color>
</resources>
É isso mesmo que você notou: as cores definidas em colorPrimary e em colorDarkPrimary são as mesmas.
Por agora é um tanto difícil enxergar que essa é uma boa estratégia para o projeto que estamos desenvolvendo, mas no decorrer do conteúdo tudo vai se encaixar (se auto-explicar) como definido em protótipo estático.
Tema
Então a versão inicial simples do arquivo de definição de temas, estilos, em projeto.
Segue configuração de /res/values/styles.xml:
<resources>
<!-- Tema base do aplicativo. -->
<style
name="AppTheme"
parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
Curiosidade:
É possível abreviar o uso de parent (definição de tema ancestral) em tag <style> utilizando ponto (.).
Ou seja, a definição a seguir:
...
<style
name="Theme.AppCompat.Light.NoActionBar.AppTheme">
...
É equivalente à seguinte definição:
...
<style
name="AppTheme"
parent="Theme.AppCompat.Light.NoActionBar">
...
Tela de abertura (Splash Screen)
Agora vamos criar a popular e necessária tela de abertura de aplicativo, também conhecida como Launcher Screen ou Splash Screen.
Essa tela é importante, pois sempre que o aplicativo não está já em memória, então há um pequeno delay no carregamento do layout inicial.
E nesse delay o comum, caso nenhuma tela de abertura tenha sido definida, é uma tela toda branca (ou preta, depende de qual tema root está definido em app) sendo apresentada ao usuário.
Antes de continuar quero ressaltar que:
Diferente do que muitos (principalmente aqueles que estão iniciando no Android) possam pensar, não são todos os aplicativos que têm uma tela de abertura definida.
Consequentemente o valor padrão de tela de abertura (valor padrão de windowBackground) acaba sendo utilizado, deixando assim o app pouco profissional ao menos em termos de design.
Então é isso.
Vamos sim dar uma atenção especial a este detalhe, pois em termos de "entregar algo profissional" isso faz sim diferença no projeto.
Imagens da estrutura
Teremos duas imagens em nossa tela de abertura:
Imagem de centro de tela, com a logo do canal YouTube.
- /res/drawable-mdpi/launcher_screen_logo.png;
- /res/drawable-hdpi/launcher_screen_logo.png;
- /res/drawable-xhdpi/launcher_screen_logo.png;
- /res/drawable-xxhdpi/launcher_screen_logo.png;
- /res/drawable-xxxhdpi/launcher_screen_logo.png.
Imagem de fundo, informando quem (ou qual empresa) desenvolveu o aplicativo Android.
- /res/drawable-mdpi/powered_by.png;
- /res/drawable-hdpi/powered_by.png;
- /res/drawable-xhdpi/powered_by.png;
- /res/drawable-xxhdpi/powered_by.png;
- /res/drawable-xxxhdpi/powered_by.png.
Com os links informados anteriormente, realize o download de cada uma das versões das imagens e coloque-as em seus respectivos folders drawable.
Confesso que o processo de criar imagens não é nem de perto uma expertise minha. Mas posso lhe dar algumas dicas sobre isso.
Para as imagens de tela de abertura eu utilizei o Adobe Fireworks (software pago) e fui na "martelada" tentando vários tamanhos até encontrar um ideal de imagens e de texto.
Depois disso utilizei os recursos gratuitos:
- Fotoram para arredondar as bordas da imagem retangular de logo. Este recurso eu utilizei antes mesmo de construir a logo com imagem e texto;
- Native Script Image Buider para gerar todas as versões DPI de cada uma das duas imagens da tela de abertura. Este recurso foi o último que utilizei antes de colocar as imagens já formatadas no projeto dentro do Android Studio.
Apesar de eu ter utilizado o Adobe Fireworks, você pode utilizar inúmeros outros softwares gratuitos (o GIMP é um deles) e até mesmo free online, para construir as imagens de sua versão de app.
Definindo o layer-list
Com as imagens já definidas e migradas para o projeto de aplicativo, agora precisamos definir a estrutura que vai juntar tudo para ser carregada como "layout" de tela de abertura.
Sendo assim, no folder /res/drawable, crie o arquivo XML launcher_screen.xml com a seguinte definição:
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:opacity="opaque">
<!--
A definição android:opacity=”opaque” é
fundamental para evitar um flash em preto
durante a transição do tema de tela de
abertura em app.
Note que a ordem das tags <item> importa.
A que vem antes em arquivo é então o layer
mais abaixo na estrutura do layout.
-->
<!--
A cor de background. Preferencialmente a
mesma cor definida para a StatusBar.
A cor de StatusBar para essa nossa versão
de projeto será #595959.
-->
<item android:drawable="@color/colorStatusBar" />
<!--
A logo central na tela. Aqui estamos utilizando
uma com a seguinte definição em DP:
➙ width: 264dp;
➙ height: 60dp.
-->
<item>
<bitmap
android:gravity="center"
android:src="@drawable/launcher_screen_logo" />
</item>
<!--
Definição do PoweredBy, "Desenvolvido por", logo
ao fundo na tela. Precisamos trabalhar com um
texto em imagem, pois até o momento da construção
deste projeto não era possível colocar texto
String como uma das camadas em layer-list.
Aqui estamos utilizando a seguinte definição em
DP para essa imagem:
➙ width: 250dp;
➙ height: 20dp.
A definição android:bottom="58dp" é necessária, pois
caso contrário, devido a android:gravity="center_horizontal|bottom",
a imagem fica literalmente "colada" no fundo da
tela e assim, dependendo do aparelho (modelo), a
bottom bar padrão do Android fica sobre a imagem
de PoweredBy.
-->
<item android:bottom="58dp">
<bitmap
android:gravity="center_horizontal|bottom"
android:src="@drawable/powered_by" />
</item>
</layer-list>
Por favor, leia todos os comentários da estrutura XML anterior para assim entender o porquê de cada camada, <item>.
Definindo o tema
Sim, vamos precisar de um novo tema. Somente para carregamento da tela de abertura.
Sendo assim, em /res/values/styles.xml, adicione o novo tema AppTheme.Launcher logo no início desse arquivo e com a seguinte definição:
...
<!--
Tema da launcher screen. Carregado antes de tudo
para que não fique em tela uma definição de
windowBackground que é padrão e sem relação com
o design do app.
-->
<style name="AppTheme.Launcher">
<item name="android:windowBackground">
@drawable/launcher_screen
</item>
</style>
...
Atualizando o AndroidManifest
Ainda é preciso mudar o tema de <application> no AndroudManifest.xml.
Vamos colocar nosso novo tema AppTheme.Launcher como o tema do app, exatamente como a seguir:
<application
...
android:theme="@style/AppTheme.Launcher">
Por fim é preciso colocar no onCreate() da atividade principal do projeto, antes de super.onCreate(), a invocação do verdadeiro tema do app via setTheme().
Exemplo:
...
override fun onCreate( savedInstanceState: Bundle? ){
setTheme( R.style.AppTheme )
super.onCreate( savedInstanceState )
...
}
...
Enfim... sobre a tela de abertura é isso.
Na parte de códigos dinâmicos da atividade principal já teremos essa definição de setTheme().
Quando o app for carregado e não estiver já em memória, teremos a seguinte tela de abertura sendo apresentada:
Atividade principal
Para um projeto inicial com uma "Empty Activity" a atividade inicial e o layout dessa atividade são ambos bem simples.
Apesar dos inúmeros fragmentos que teremos em projeto, atividade nós teremos apenas uma. E já adianto aqui (spoiler), teremos também um Service.
É isso, vamos aos códigos da atividade. Começando pelo fácil: os códigos estáticos.
Layout XML
Como layout inicial temos em /res/layout/activity_main.xml a seguinte estrutura:
Simples, certo?
Então a versão em código deste layout, código XML:
<?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">
<!-- TODO -->
</LinearLayout>
Ainda voltaremos algumas vezes a este layout.
Classe Kotlin
Por fim, para a apresentação da versão inicial de projeto, vamos aos primeiros códigos Kotlin da atividade principal.
Primeiro, na raiz do pacote do projeto, crie o pacote /ui. Logo depois coloque (arraste) a classe MainActivity para dentro deste pacote.
Assim temos o código da atividade:
package thiengo.com.br.canalvinciusthiengo.ui
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import thiengo.com.br.canalvinciusthiengo.R
/**
* Atividade principal e única atividade do
* aplicativo.
*
* @constructor cria um objeto completo do tipo
* [MainActivity].
*/
class MainActivity : AppCompatActivity() {
override fun onCreate( savedInstanceState: Bundle? ) {
setTheme( R.style.AppTheme ) /* Devido ao uso de uma Splash Screen personalizada. */
super.onCreate( savedInstanceState )
setContentView( R.layout.activity_main )
}
}
Bem simples, certo?
Note que como estamos desenvolvendo um aplicativo que funcionará como uma espécie de framework... devido a isto teremos comentários em muitos trechos do projeto.
Comentários padrões de códigos Kotlin:
...
/**
* Atividade principal e única atividade do
* aplicativo.
*
* @constructor cria um objeto completo do tipo
* [MainActivity].
*/
...
Isso para que seja o menor possível o número de dúvidas enquanto um outro desenvolvedor estiver reutilizando o projeto para algum outro canal YouTube.
Agora podemos partir para os códigos específicos de domínio do aplicativo. Digo, alguns desses códigos ainda nesta segunda parte de nosso projeto Android.
Assinatura de topo
Vamos iniciar com a assinatura de topo do aplicativo:
Que diferente do que alguns possam imaginar:
Nós não estaremos utilizando componentes Android exclusivos para barra de topo e sim componentes comuns em layout de conteúdo.
Esse caminho foi o escolhido, pois infelizmente a Toolbar e a AppBarLayout ainda são "barreiras" para barras de topo muito diferentes do convencional, aquilo que prega o Material Design.
Estáticos de interface
Primeiro a parte "tranquila", a atualização dos recursos estáticos, XML, de projeto.
Arquivo de Strings
Em /res/values/strings.xml adicione as novas Strings a seguir:
...
<!-- MainActivity -->
<string name="channel_name">
Vinícius Thiengo
</string>
<string name="channel_desc">
Seu canal sobre Desenvolvimento de Aplicativos
Android e TI.
</string>
<string name="youtube_content_icon_content_desc">
Ícone do YouTube.
</string>
<string name="channel_thumb_content_desc">
Thumb do canal YouTube Vinícius Thiengo.
</string>
<string name="channel_toast_alert">
É preciso ter o aplicativo do YouTube instalado
para acessar o canal.
</string>
...
Algo que ainda não comentei, mas é válido principalmente se você estiver iniciando no desenvolvimento de aplicativos Android, é:
Todos os rótulos de recursos estáticos (em XML) e de recursos dinâmicos (em Kotlin - variáveis, classes, ...) serão definidos em inglês.
Pois esse é o idioma "comum" em desenvolvimento de software e também porque as APIs nativas são todas em inglês.
Seria muito bruto utilizar rótulos em português e ao mesmo tempo ter que consumir APIs com rótulos em inglês. Certamente ao menos a leitura do código ficaria dificultada.
Mas, os valores e os comentários em código, todos eles serão em português.
Dimens
Apesar de que vamos utilizar esse arquivo estático para apenas uma definição...
... apesar disso ele será útil, pois é uma definição de espaçamento que será utilizada em vários layouts do projeto.
Em /res/values crie o arquivo dimens.xml com a seguinte estrutura:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="standard_screen_side_margin">25dp</dimen>
</resources>
Se você é iniciante no Android deve estar sendo bem confuso ver alguns arquivos XML com o prólogo definido, <?xml version="1.0" encoding="utf-8"?>, e outros arquivos sem o prólogo.
No começo eu também achei, mas se o IDE oficial cria os arquivos assim e os projetos executam sem problemas... então vamos seguir assim.
Novas cores
Agora as cores. Em /res/values/colors.xml adicione as novas definições de cores como a seguir:
...
<!-- Top -->
<color name="colorStatusBar">#595959</color>
<color name="colorTopIcon">#FFFFFF</color>
<color name="colorTopWave">#555555</color>
<color name="colorTopText">#FFFFFF</color>
...
Thiengo, porque manter duas definições ou mais para um mesmo valor de cor? Como em colorTopIcon e colorTopText, onde ambas as definições têm o valor #FFFFFF (branco).
Boa pergunta!
Realmente são duas definições com os mesmos valores, porém são definições de contextos diferentes.
Ou seja, se for necessário trocar a cor apenas do ícone de topo (colorTopIcon) e a definição de cor for somente uma para os contextos "ícone de topo" (colorTopIcon) e "texto de topo" (colorTopText)...
... se isso ocorrer você terá que atualizar o código fonte de ao menos um layout na unha, terá que criar uma nova definição... olha a dor de cabeça.
O "reaproveitar código" tem muito mais haver com igualdade de contexto do que com igualdade de valor de propriedade.
Por isso é prudente manter valores repetidos quando os contextos são distintos e podem passar por atualizações diferentes.
StatusBar
Vamos também deixar a StatusBar do aparelho com a cor que "encaixa" com o que foi definido em protótipo estático:
Em /res/values/styles.xml adicione o trecho em destaque (com statusBarColor):
...
<style
name="AppTheme"
parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="android:statusBarColor">@color/colorStatusBar</item>
</style>
...
Ícone do YouTube
O ícone de topo do YouTube é um ícone vetorial, ic_youtube_logo.xml:
Realize o download dele e coloque-o no folder /res/drawable de sua versão de projeto.
Waves SVG
No topo, mesmo que não pareça, temos na verdade algumas imagens vetoriais de ondas (waves), fazendo com que o background de topo fique como a seguir:
São exatas quatro imagens vetoriais de onda com variados:
- Configuração de desenho em plano cartesiano;
- e Valor do canal alpha (transparência).
Realize o download de cada uma nos links a seguir:
- /res/drawable/bg_wave_01.xml;
- /res/drawable/bg_wave_02.xml;
- /res/drawable/bg_wave_03.xml;
- /res/drawable/bg_wave_04.xml.
E então coloque-as no folder /res/drawable.
Para que elas possam ser colocadas de maneira eficiente no topo, uma sobre a outra, vamos criar uma lista de camadas (layer list).
Ainda em /res/drawable crie o arquivo bg_top.xml com o código a seguir:
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_wave_01" />
<item android:drawable="@drawable/bg_wave_02" />
<item android:drawable="@drawable/bg_wave_03" />
<item android:drawable="@drawable/bg_wave_04" />
</layer-list>
Antes de partirmos para a "logo do canal", saiba que as waves em XML foram geradas no site GetWaves.IO e depois colocadas em projeto utilizando o Vector Asset Studio.
Logo do canal
A logo do canal que vem no topo...
... deve ser gerada em cinco versões tendo como base o tamanho 65dp x 65dp:
- /res/drawable-mdpi/channel_logo.png;
- /res/drawable-hdpi/channel_logo.png;
- /res/drawable-xhdpi/channel_logo.png;
- /res/drawable-xxhdpi/channel_logo.png;
- /res/drawable-xxxhdpi/channel_logo.png.
Realize o download das imagens acima e coloque-as nos folders drawable corretos em sua versão de projeto.
Provavelmente você terá que criar em /res os folders a seguir:
- /drawable-mdpi;
- /drawable-hdpi;
- /drawable-xhdpi;
- /drawable-xxhdpi;
- /drawable-xxxhdpi.
Eu utilizei o rápido e prático Generic Icon Generator para gerar as cinco versões da logo em 65dp.
Também como parte do resultado temos as cinco versões de /drawable. Basta copiar e colocar em /res.
Família de fontes
Note que a fonte do título do canal está em uma família diferente da convencional Roboto:
Em /res crie o folder /font e coloque dentro deste novo folder a seguinte família de fontes:
Layout
E por fim, na parte estática exclusiva da nossa barra de topo, o layout.
Que terá a seguinte estrutura:
Em /res/layout crie o XML top_signature.xml com a seguinte configuração:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_top"
android:paddingStart="@dimen/standard_screen_side_margin"
android:paddingTop="25dp"
android:paddingEnd="@dimen/standard_screen_side_margin"
android:paddingBottom="65dp">
<ImageView
android:id="@+id/iv_youtube_logo"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_gravity="end"
android:layout_marginBottom="15dp"
android:contentDescription="@string/youtube_content_icon_content_desc"
android:onClick="openYouTubeChannel"
android:src="@drawable/ic_youtube_logo"
app:tint="@color/colorTopIcon" />
<androidx.cardview.widget.CardView
android:id="@+id/cv_channel_logo"
android:layout_width="65dp"
android:layout_height="65dp"
android:layout_below="@+id/iv_youtube_logo"
android:layout_alignParentStart="true"
app:cardCornerRadius="8dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="65dp"
android:layout_height="65dp"
android:contentDescription="@string/channel_thumb_content_desc"
android:onClick="openYouTubeChannel"
android:scaleType="center"
android:src="@drawable/channel_logo" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/tv_channel_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/cv_channel_logo"
android:layout_marginStart="10dp"
android:layout_toEndOf="@id/cv_channel_logo"
android:ellipsize="end"
android:fontFamily="@font/quicksand_variable_font_wght"
android:maxLines="1"
android:onClick="openYouTubeChannel"
android:text="@string/channel_name"
android:textColor="@color/colorTopText"
android:textSize="19sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_channel_name"
android:layout_marginStart="10dp"
android:layout_toEndOf="@id/cv_channel_logo"
android:ellipsize="end"
android:maxLines="2"
android:onClick="openYouTubeChannel"
android:text="@string/channel_desc"
android:textColor="@color/colorTopText"
android:textStyle="normal" />
</RelativeLayout>
Note que já temos configurado em alguns componentes do layout acima o atributo android:onClick.
Criaremos o método openYouTubeChannel() logo logo na MainActivity.
Classe YouTube de configuração
Antes de irmos às configurações finais da atividade principal em projeto. Vamos primeiro iniciar uma das classes de configuração importantes em todo o aplicativo.
Na raiz do projeto crie o pacote /config.
Dentro deste novo pacote crie a classe abstrata YouTubeConfig com a seguinte configuração inicial:
package thiengo.com.br.canalvinciusthiengo.config
/**
* Classe que contém os principais dados estáticos de
* configuração de acesso à YouTube Data API. E também
* dados para acesso a recursos alternativos externos
* do YouTube.
*
* As classes internas ([Key], [Channel], [ApiV3] e [Notification])
* e também os rótulos de todos os companion object.
* Estes estão presentes em código somente para
* facilitar a leitura dele. Ou seja, em termos de
* regras de sintaxe esses não são obrigatórios.
*/
abstract class YouTubeConfig {
abstract class Channel {
companion object {
/**
* Constante com o identificador único do
* canal. Com esse ID é possível
* carregar da YouTube Data API os dados do
* canal correto.
*/
const val CHANNEL_ID = "UCG3gFuIkRF3PpNkRk3Wp6dw"
/**
* Constante com a URL do canal.
*/
const val CHANNEL_URL = "https://www.youtube.com/channel/$CHANNEL_ID"
}
}
}
Vamos seguir com essa estratégia de dados de configuração em projeto ao invés de simplesmente criarmos constantes em qualquer classe.
Pois assim a leitura do código que precisar de alguma das constantes definidas nesta classe...
... essa leitura ficará mais explicativa, pois a constante deverá ser acessada como YouTubeConfig.Channel.CHANNEL_URL, por exemplo.
Ao longo de todo o projeto voltaremos a esta classe para adicionarmos ainda mais configurações.
Se o canal YouTube que você estiver configurando em aplicativo não tiver o ID dele na url, então você pode utilizar o site YouTube Channel ID, Info & Stats para acessar esse ID de canal.
Não adianta utilizar o User Name do canal que aparece em algumas URLs do YouTube, é preciso o identificador único do canal.
Configuração na atividade principal
Por fim a configuração de topo na atividade principal.
No layout
A estrutura do layout /res/layout/activity_main.xml ficará como a seguir:
Agora em activity_main.xml temos o seguinte XML:
<?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" />
</LinearLayout>
Na atividade
Precisamos colocar na atividade principal o listener de clique já configurado em alguns componentes do layout de topo.
Listener de clique que vai permitir que o usuário do app tenha também acesso imediato ao canal no YouTube.
Sendo assim, na MainActivity adicione o método openYouTubeChannel() como a seguir:
...
/**
* Invoca o aplicativo do YouTube para que o usuário
* tenha acesso direto ao canal.
*
* Esse listener de clique está vinculado aos
* componentes visuais do topo do aplicativo.
*
* Caso o aplicativo nativo do YouTube e nem mesmo um
* navegador Web esteja instalado no aparelho (algo
* utópico), então uma mensagem de falha é apresentada
* ao usuário.
*
* @param view componente visual que teve o evento de
* toque (clique) disparado.
*/
fun openYouTubeChannel( view: View ){
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse( YouTubeConfig.Channel.CHANNEL_URL )
)
if( intent.resolveActivity( packageManager ) != null ){
startActivity( intent )
}
else{
Toast
.makeText(
this,
getString( R.string.channel_toast_alert ),
Toast.LENGTH_LONG
)
.show()
}
}
...
E está lá, nossa classe abstrata de configuração sendo utilizada:
...
Uri.parse( YouTubeConfig.Channel.CHANNEL_URL )
...
O que não é incomum ver por aí é o uso de "valor mágico" em código.
No caso anterior seria o valor bruto (inline) do argumento ao invés de uma referência (uma constante, por exemplo):
...
Uri.parse( "O valor inline da String URL" )
...
Evite o uso de valores mágicos a qualquer custo, pois em tempo de manutenção, mesmo que ainda em primeiro release, isso dá uma baita dor de cabeça.
Utilize constantes, enum, XML... mas evite valores mágicos.
Ainda faltam os imports das novas entidades adicionadas junto ao método openYouTubeChannel():
...
import android.content.Intent
import android.net.Uri
import android.view.View
import android.widget.Toast
import thiengo.com.br.canalvinciusthiengo.config.YouTubeConfig
...
É isso.
Neste ponto do software a configuração física dele em IDE deve estar como a seguir:
Para está segunda parte de nosso projeto de aplicativo Android profissional para YouTubers... chegamos ao fim.
Próximo conteúdo
Aos poucos nós vamos passo a passo desenvolvendo algo que ajuda, mesmo que uma parcela mínima (os YouTubers), a comunidade de não desenvolvedores.
No próximo artigo nós vamos continuar no lado tático do projeto, codificando o bottom menu principal:
➙ Criando e Configurando o Menu Principal - Parte 3.
Então é isso.
Dê uma descansada, tome um café ☕ 🍞. E...
... te vejo na Parte 3.
Se houverem dúvidas ou dicas desta segunda parte do projeto, 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 emails 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.
Comentários Facebook