Construindo a Tela e a Lógica de Último Vídeo - YouTuber Android App - Parte 7
(2354)
CategoriasAndroid, Design, Protótipo
AutorVinícius Thiengo
Vídeo aulas186
Tempo15 horas
ExercíciosSim
CertificadoSim
CategoriaEngenharia de Software
Autor(es)Vaughn Vernon
EditoraAlta Books
Edição1ª
Ano2024
Páginas160
Tudo bem?
Neste artigo vamos continuar com o nosso projeto de aplicativo Android para YouTubers.
Nesta parte sete do projeto nós vamos construir passo a passo toda a configuração da principal tela do aplicativo, a tela de "Último vídeo" liberado em canal:
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.
A seguir os tópicos que estaremos abordando neste sétimo conteúdo do projeto de aplicativo Android para YouTubers:
O que já temos até aqui
Se você chegou no projeto somente agora, saiba que este não é o primeiro e nem mesmo o último artigo já publicado sobre essa proposta de aplicativo Android.
Todo o roteiro de construção do projeto 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;
- 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 (você está aqui);
- 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 que estaremos desenvolvendo... para isso é inteligente seguir cada um dos conteúdos na ordem apresentada na lista anterior.
Repositório
Para ter acesso a todos os códigos fontes do projeto já finalizado, entre no repositório GitHub dele em:
➙ GitHub do projeto de aplicativo Android para YouTuber.
Fragmento de Último vídeo
Assim podemos partir para o principal fragmento do projeto de app para YouTubers.
Fragmento responsável por apresentar o último vídeo disponibilizado em canal e assim incentivar o clique no player:
A proposta do player é justamente essa:
Incentivar o clique e assim levar o usuário a assistir ao vídeo. Consequentemente aumentando o engajamento dos seguidores no canal.
O vídeo será aberto ou no app oficial do YouTube ou no site oficial via navegador Web mobile.
Na pior das hipóteses, algo utópico, não haverá nenhum aplicativo capaz de abrir o vídeo e assim uma mensagem Toast será apresentada ao usuário para que ele instale algum app compatível com a necessidade "assistir ao vídeo".
Então é isso, vamos aos códigos.
Estáticos de interface
Vamos iniciar com a parte estática, XML. Que por sinal são os trechos desta tela de último vídeo que mais têm códigos.
Rótulos
No arquivo de Strings, mais precisamente o arquivo /res/values/strings.xml, adicione as seguintes novas definições:
...
<!-- LastVideoFragment -->
<string name="last_video_content_title">
Último vídeo liberado
</string>
<string name="last_video_play_content_desc">
Ícone de play do vídeo.
</string>
<string name="volume_icon_content_desc">
Ícone de controle volume.
</string>
<string name="head_phones_icon_content_desc">
Ícone de fones de ouvido.
</string>
<string name="video_resolution_icon_content_desc">
Ícone de controle de resolução (HD).
</string>
<string name="like_icon_content_desc">
Ícone de like, gostei do conteúdo.
</string>
<string name="touch_to_watch">
Toque para assistir
</string>
<string name="leave_a_comment">
Deixe seu comentário
</string>
<string name="last_video_toast_alert">
É preciso ou o aplicativo do YouTube ou um
navegador Web para que o vídeo \"%s\" comece.
</string>
...
Cores
No arquivo de cores, /res/values/colors.xml, adicione as seguintes novas definições de cores:
...
<!-- Video player -->
<color name="colorVideoPlayerBackground">#000000</color>
<color name="colorVideoPlayerForeground">#BB000000</color>
<color name="colorVideoPlayerIcon">#FFFFFF</color>
<!-- YouTube block -->
<color name="colorVideoPlayerYBBackground">#DDDDDD</color>
<color name="colorVideoPlayerYBText">#666666</color>
<!-- Comment block -->
<color name="colorVideoPlayerCBBackground">#B7E1C2</color>
<color name="colorVideoPlayerCBText">#666666</color>
...
Um detalhe importante, na verdade uma "lembrança":
O arquivo de definição de cores certamente será o que mais passará por atualizações quando você for reutilizar esse "projeto framework" para atender a algum outro canal cliente ou o seu próprio canal YouTube.
Por isso nós vamos dar uma atenção especial a este XML, colocando comentários para melhor identificar o que será atualizado em layout se houverem mudanças de cores.
Ícones vetoriais
Vamos agora adicionar todos os ícones vetoriais que precisaremos nesta tela de projeto. Serão poucos.
No arquivo /res/drawable, de sua versão de projeto, adicione os ícones a seguir (faça o download deles):
Ícone de play do vídeo, ic_play_button.xml:
Ícone de volume do vídeo, ic_volume.xml:
Ícone de head phone, ic_head_phone.xml:
Ícone de resolução do vídeo, ic_hd_video.xml:
Ícone de compartilhamento, like do vídeo, ic_like.xml:
Ícone da logo colorida do YouTube, ic_youtube_mini_color.xml:
Ícone colorido de caixa de comentários, ic_comment_color.xml:
Lembrando que esses ícones estão presentes em tela, pois fazem parte da estratégia de: incentivar o toque do usuário e assim conseguir com que ele realmente entre no vídeo pelo YouTube.
Novos estilos
Além do estilo de título de conteúdo (AppTheme.Title) já definido em projeto e que também estaremos utilizando nesta tela de "último vídeo"...
... além disso nesta tela teremos ainda mais estilos para que o código de layout XML fique consideravelmente menor e fácil de aplicar manutenção.
Em /res/values/styles.xml adicione os novos estilos a seguir:
...
<style name="AppTheme.VideoPlayerIcons">
<item name="android:layout_width">20dp</item>
<item name="android:layout_height">20dp</item>
<item name="android:tint">@color/colorVideoPlayerIcon</item>
</style>
<style name="AppTheme.VideoPlayerBand">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_weight">1</item>
<item name="android:drawablePadding">10dp</item>
<item name="android:ellipsize">end</item>
<item name="android:gravity">center_vertical</item>
<item name="android:maxLines">1</item>
<item name="android:paddingStart">10dp</item>
<item name="android:paddingTop">5dp</item>
<item name="android:paddingEnd">10dp</item>
<item name="android:paddingBottom">5dp</item>
<item name="android:textColor">@color/colorVideoPlayerYBText</item>
<item name="android:textSize">12sp</item>
<item name="android:textStyle">bold</item>
<item name="android:background">@drawable/bg_video_player_band</item>
</style>
<style name="AppTheme.VideoPlayerTextsBelow">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginStart">@dimen/standard_screen_side_margin</item>
<item name="android:layout_marginEnd">@dimen/standard_screen_side_margin</item>
<item name="android:layout_marginTop">10dp</item>
<item name="android:textColor">@color/colorContentText</item>
<item name="android:ellipsize">end</item>
<item name="android:maxLines">2</item>
</style>
...
Somente tente imaginar se o estilo AppTheme.VideoPlayerBand não tivesse sido isolado em styles.xml...
... a quantidade de código (15 linhas) que teríamos repetido em layout!
E sim, ainda é preciso criar o arquivo bg_video_player_band.xml no folder /res/drawable.
É este arquivo de recursos desenháveis que nos permite ter também as bordas arredondadas nas pequenas caixas informativas abaixo do vídeo player:
Então, em /res/drawable, adicione o XML bg_video_player_band.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">
<solid android:color="@color/colorVideoPlayerYBBackground" />
<corners android:radius="4dp" />
</shape>
Note que a cor padrão em bg_video_player_band.xml é a cor da primeira caixa informativa:
Porém a caixa de comentários também utiliza o mesmo background... com o atributo android:backgroundTint definido em estrutura para mudar apenas a cor definida no arquivo de background:
...
<TextView
...
style="@style/AppTheme.VideoPlayerBand"
...
android:backgroundTint="@color/colorVideoPlayerCBBackground"
... />
...
Prática comum para evitar a definição desnecessária de dois arquivos de background com praticamente a mesma configuração, mudando somente a cor.
Layout
Assim o layout do fragmento de vídeo. Primeiro o diagrama dele:
Agora, em /res/layout, crie o arquivo XML fragment_last_video.xml com o seguinte código estático:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.fragment.LastVideoFragment">
<TextView
style="@style/AppTheme.Title"
android:text="@string/last_video_content_title" />
<LinearLayout
android:id="@+id/ll_last_video_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="10dp"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="@+id/cv_last_video"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="@dimen/standard_screen_side_margin"
android:layout_marginEnd="@dimen/standard_screen_side_margin"
android:layout_weight="1"
app:cardCornerRadius="8dp"
app:cardElevation="10dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--
ImageView responsável por conter o
banner do vídeo como background do
player em tela.
-->
<ImageView
android:id="@+id/iv_last_video_thumb"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorVideoPlayerBackground"
android:scaleType="centerCrop" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorVideoPlayerForeground"
android:padding="20dp">
<ImageView
android:id="@+id/iv_last_video_play"
android:layout_width="34dp"
android:layout_height="34dp"
android:layout_centerInParent="true"
android:contentDescription="@string/last_video_play_content_desc"
android:scaleType="fitCenter"
android:src="@drawable/ic_play_button"
app:tint="@color/colorVideoPlayerIcon" />
<ImageView
android:id="@+id/iv_volume"
style="@style/AppTheme.VideoPlayerIcons"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:contentDescription="@string/volume_icon_content_desc"
android:src="@drawable/ic_volume" />
<ImageView
style="@style/AppTheme.VideoPlayerIcons"
android:layout_alignParentBottom="true"
android:layout_marginStart="10dp"
android:layout_toEndOf="@+id/iv_volume"
android:contentDescription="@string/head_phones_icon_content_desc"
android:src="@drawable/ic_head_phone" />
<ImageView
style="@style/AppTheme.VideoPlayerIcons"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:contentDescription="@string/video_resolution_icon_content_desc"
android:src="@drawable/ic_hd_video" />
<ImageView
style="@style/AppTheme.VideoPlayerIcons"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:contentDescription="@string/like_icon_content_desc"
android:src="@drawable/ic_like" />
</RelativeLayout>
</FrameLayout>
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/standard_screen_side_margin"
android:layout_marginTop="15dp"
android:layout_marginEnd="@dimen/standard_screen_side_margin"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_touch_to_watch"
style="@style/AppTheme.VideoPlayerBand"
android:drawableStart="@drawable/ic_youtube_mini_color"
android:text="@string/touch_to_watch" />
<TextView
android:id="@+id/tv_comment"
style="@style/AppTheme.VideoPlayerBand"
android:layout_marginStart="5dp"
android:backgroundTint="@color/colorVideoPlayerCBBackground"
android:drawableStart="@drawable/ic_comment_color"
android:text="@string/leave_a_comment"
android:textColor="@color/colorVideoPlayerCBText" />
</LinearLayout>
<TextView
android:id="@+id/tv_last_video_title"
style="@style/AppTheme.VideoPlayerTextsBelow"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_last_video_desc"
style="@style/AppTheme.VideoPlayerTextsBelow"
android:layout_marginTop="5dp" />
</LinearLayout>
</LinearLayout>
Sim, este é um dos pontos de projeto que podem ser melhorados em futuros releases.
Mas confesso que não é nem de perto um ponto crítico, pois o layout é carregado apenas um vez, nós não o estamos utilizando dentro de algum framework de lista.
Sendo assim, ao menos para mim, perder muito tempo nesta parte do projeto é perder tempo com "vaidade". Isso ao menos nos primeiros releases.
Carregamento remoto de banner
Com a definição do layout anterior, mais precisamente na parte:
...
<!--
ImageView responsável por conter o
banner do vídeo como background do
player em tela.
-->
<ImageView
android:id="@+id/iv_last_video_thumb"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorVideoPlayerBackground"
android:scaleType="centerCrop" />
...
Com está definição já temos que esta próximo o momento de utilizarmos a URI referenciada na propriedade thumbUrl (da classe LastVideo) para carregarmos em projeto uma imagem remota.
Sendo assim vamos configurar a API que considero a mais eficiente para este tipo de tarefa: "carregamento remoto de imagem". A Picasso API.
No Gradle Nível de Aplicativo, ou build.gradle (Module: app), adicione em dependencies a seguinte definição:
...
dependencies {
...
/*
* Picasso API para carregamento de imagens remotas
* de maneira eficiente.
* */
implementation 'com.squareup.picasso:picasso:2.71828'
}
...
Se na época em que você estiver implementando o projeto houver uma versão mais atual da Picasso API, então utilize a versão mais atual, pois o projeto deverá continuar funcionando sem problemas.
Ao final sincronize o projeto.
Agora no AndroidManifest.xml vamos adicionar a permissão de Internet e uma definição de "confiança na origem" da requisição remota:
...
<uses-permission android:name="android.permission.INTERNET" />
<application
...
android:usesCleartextTraffic="true">
...
No Android Oreo, API 27, e anteriores o valor de usesCleartextTraffic é true por padrão.
Este atributo, em resumo (bem resumido), quando com o valor true informa que:
A requisição remota partindo do aplicativo pode acontecer sem receios, pois a fonte requisitada é segura.
Como estaremos sempre requisitando dados dos servidores do Google (YouTube), até mesmo as imagens. Então tranquilamente podemos ter essa definição em nosso aplicativo.
E já lhe adianto que se android:usesCleartextTraffic="true" não for definido em projeto como fizemos anteriormente...
... neste caso, em algumas ocasiões, as imagens não serão carregadas mesmo com o uso da Picasso API.
LastVideoFragment
Por fim a nossa configuração de fragmento da tela de último vídeo.
Configuração um pouco maior do que as realizadas nos fragmentos já apresentados até este ponto de nosso aplicativo Android.
Vamos iniciar com o básico e assim ir acrescentando o que for necessário.
Em /ui/fragment crie o fragmento LastVideoFragment com o seguinte código fonte:
package thiengo.com.br.canalvinciusthiengo.ui.fragment
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.fragment_last_video.*
import thiengo.com.br.canalvinciusthiengo.R
import thiengo.com.br.canalvinciusthiengo.data.dynamic.UtilDatabase
import thiengo.com.br.canalvinciusthiengo.data.fixed.LastVideoData
import thiengo.com.br.canalvinciusthiengo.model.LastVideo
/**
* Contém toda a UI de último vídeo disponível
* no canal YouTube do app.
*
* @constructor cria um objeto completo do tipo
* [LastVideoFragment].
*/
class LastVideoFragment : Fragment() {
companion object {
/**
* Constante com o identificador único do
* fragmento [LastVideoFragment] para que
* ele seja encontrado na pilha de fragmentos
* e assim não seja necessária a construção
* de mais de um objeto deste fragmento em
* memória enquanto o aplicativo estiver em
* execução.
*/
const val KEY = "LastVideoFragment_key"
}
/**
* [lastVideo] sempre inicia com algum dado válido
* de "último vídeo" liberado, mesmo que nenhum
* vídeo tenha sido ainda enviado ao app.
*/
private var lastVideo: LastVideo = LastVideoData.getInitialVideo()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle? ): View? {
return inflater.inflate(
R.layout.fragment_last_video,
container,
false
)
}
}
Apesar de lastVideo já ter algum dado inicial, ele ainda não está sendo configurado em tela, em layout.
Definição de UI em tela
Sendo assim, em LastVideoFragment, adicione os métodos setUiModel() e descriptionStatus() como a seguir:
...
/**
* Responsável pela configuração dos dados de
* "último vídeo disponível" em tela.
*
* Como é possível que a invocação deste método
* ocorra fora da Thread Principal, então é
* importante sempre ter o código de atualização
* de vídeo dentro de runOnUiThread().
*
* Outro ponto importante é garantir que não
* haverá NullPointerException caso os dados
* cheguem em método quando a UI não mais está no
* foreground (primeiro plano). Assim o operador
* not null (?.) é utilizado com frequência.
*
* @param lVideo último vídeo liberado em canal.
*/
private fun setUiModel( lVideo: LastVideo? ){
if( lVideo != null ){
activity?.runOnUiThread{
lastVideo = lVideo
try{
Picasso
.get()
.load( lVideo.thumbUrl )
.into( iv_last_video_thumb )
iv_last_video_thumb?.contentDescription = lVideo.title
}
catch( e: Exception ){}
tv_last_video_title?.text = lVideo.title
descriptionStatus( description = lVideo.description )
}
}
}
/**
* Garante que o componente visual de apresentação
* de descrição do vídeo somente estará em tela
* caso exista descrição (alguns vídeos não têm).
*
* @param description descrição do vídeo.
*/
private fun descriptionStatus( description: String ){
if( description.isNotEmpty() ){
tv_last_video_desc?.text = description
tv_last_video_desc?.visibility = View.VISIBLE
}
else{
tv_last_video_desc?.visibility = View.GONE
}
}
...
O dado de descrição esta isolado para configuração no método descriptionStatus(), pois como informado no trecho de definição dele na classe LastVideo: esse dado é opcional.
Note que em setUiModel() nós já estamos com o código de atualização de componentes visuais garantido em Thread Principal:
...
activity?.runOnUiThread{
...
}
...
Isso, pois este método também será invocado a partir de algoritmos de execução em banco de dados local, Room.
Ou seja, algoritmos que estão fora da Thread Principal e com dados de atualização de UI.
Note também que para todos os componentes visuais nós estamos utilizando o operador "not null", (?.).
Isso para garantir que não teremos um NullPointerException quando os dados de carregamento assíncrono (os que vêm do banco de dados local) forem entregues somente quando o fragmento LastVideoFragment não mais está em foreground.
Isso pode ocorrer se o usuário, por exemplo, logo na abertura do app trocar de tela, utilizando o menu principal para isto.
Com a utilização do operador not null nós conseguimos de maneira simples driblar esse possível problema.
Com frequência também estaremos utilizando o operador "force NullPointerException" (!!) junto a propriedade activity.
Isso, pois a definição dela em código nativo Java deixa claro que ela pode ser null.
Porém nós conhecemos o fluxo do código e assim sabemos que ela nunca será utilizada em algum método do ciclo de vida do fragmento onde ela seria null.
Mas tome cuidado com esse comportamento.
Estamos fazendo aqui, pois o código total do app é pequeno e sim: nós conhecemos todo o fluxo do código.
Voltando aos fontes...
... agora precisamos definir o primeiro ponto onde setUiModel() é invocado.
Adicione-o no método de ciclo de vida onActivityCreated() do fragmento de último vídeo, como a seguir:
...
override fun onActivityCreated( savedInstanceState: Bundle? ) {
super.onActivityCreated( savedInstanceState )
setUiModel( lVideo = lastVideo )
}
...
Thiengo, e o listener de clique?
Ouvidor de clique e abertura de vídeo
Se você voltar ao layout do fragmento LastVideoFragment você vai perceber que não há ao menos um listener de clique definido em nenhuma parte do layout.
E isso, a definição de listener de clique, é necessária para que seja possível abrir o vídeo em aplicativo do YouTube.
Sendo assim, ainda no mesmo fragmento, adicione os métodos openVideoOnYouTube() e setListeners() como a seguir:
...
/**
* Configura os listeners de alguns componentes
* visuais em tela.
*/
private fun setListeners(){
ll_last_video_container?.setOnClickListener{
openVideoOnYouTube()
}
}
/**
* Invoca o aplicativo do YouTube para que o usuário
* tenha acesso ao último vídeo liberado no canal.
*
* Caso o dado de URI presente no objeto [lastVideo] seja
* inválido para a abertura do app nativo do YouTube
* ou abertura da versão dele em app de navegador Web,
* então uma mensagem de falha é apresentada.
*/
private fun openVideoOnYouTube(){
val intent = Intent(
Intent.ACTION_VIEW,
lastVideo.webUri()
)
/*
* É utópico, mas pode ocorrer de não haver instalado
* no aparelho do usuário o aplicativo do YouTube e
* nem mesmo um navegador Web.
*
* Sendo assim, ao invés de gerar uma exceção, nós
* avisamos ao usuário a necessidade de instalar o
* aplicativo adequado.
* */
if( intent.resolveActivity( activity!!.packageManager ) == null ){
Toast
.makeText(
activity,
String.format(
getString( R.string.last_video_toast_alert ),
lastVideo.title
),
Toast.LENGTH_LONG
)
.show()
return
}
activity!!.startActivity( intent )
}
...
O LinearLayout de id ll_last_video_container é justamente o LinearLayout container de todo o conteúdo de vídeo:
Criamos um método setListeners(), pois nele também haverá outra definição de listener (spoiler). Definição que será colocada nos próximos conteúdos deste projeto.
Agora é definir o setListeners() no local correto.
Aqui, no método onActivityCreated():
...
override fun onActivityCreated( savedInstanceState: Bundle? ) {
super.onActivityCreated( savedInstanceState )
setListeners()
setUiModel( lVideo = lastVideo )
}
...
Ainda falta o acesso aos dados direto do banco de dados.
Dados da base de dados local
Apesar da propriedade lastVideo já iniciar com dados:
...
private var lastVideo = LastVideoData.getInitialVideo()
...
A ideia principal é que esses dados sejam carregados da base local, pois na configuração de banco de dados local de nosso projeto a persistência Room (SQLite) já é iniciada com alguns dados.
Mas ai vem um segredo (problema):
No primeiro carregamento do banco de dados local, quando ele é criado, a entrega dos dados inseridos em inicialização é lenta e não podemos apostar que ela ocorrerá com certeza em um período curto de tempo.
Sendo assim, a estratégia que adotamos aqui para superar esse problema de primeiro carregamento foi:
Vamos iniciar a propriedade principal de preenchimento de UI já com dados estáticos em projeto e ao mesmo tempo vamos também solicitar dados da base local.
O que vamos fazer nesta seção é adicionar a parte que falta "(...) solicitar dados da base local".
No fragmento LastVideoFragment adicione o método de ciclo de vida onCreate() como a seguir:
...
override fun onCreate( savedInstanceState: Bundle? ) {
super.onCreate( savedInstanceState )
/**
* Para garantir que o banco de dados local
* será acessado apenas na primeira vez que
* o fragmento é carregado.
*
* Sendo assim o usuário poderá mudar de tabs
* (itens de menu) que não haverá novos
* carregamentos somente devido à mudança de
* tab. Isso se o objeto de fragmento for
* retido em memória.
*/
UtilDatabase
.getInstance( context = activity!!.applicationContext )
.getLastVideo{
setUiModel( lVideo = it )
}
}
...
Primeiro:
setUiModel(), quando invocado em getLastVideo(), está sendo invocado fora da Thread Principal.
Agora é evidente a necessidade de uso de activity?.runOnUiThread() em setUiModel().
Segundo:
Estamos realizando o acesso ao banco de dados local no método onCreate(), pois na lógica de negócio que construiremos na atividade principal para acesso aos fragmentos de opções de menu...
... nessa lógica de negócio nós vamos reter os fragmentos em memória.
Retenção que vai permitir a não criação desnecessária de fragmentos já criados.
Ou seja, para cada fragmento em projeto o método onCreate() será invocado somente uma vez enquanto o aplicativo se mantém em execução.
No caso dos dados locais de LastVideo, precisamos do acesso a eles somente uma única vez.
Tendo em mente que não estamos utilizando os componentes de arquitetura do Android, é importante invocar o acesso ao banco de dados local no método correto do ciclo de vida do fragmento.
Aqui o método onCreate().
Importante:
Como fundamento Android, domine os métodos de ciclo de vida das APIs Activity e Fragment.
Até este ponto do projeto temos a seguinte configuração física (pacotes das entidades de código dinâmico que foram adicionadas) em IDE:
Próximo conteúdo
Finalizamos a configuração inicial do principal fragmento do projeto, LastVideoFragment.
No próximo conteúdo vamos a construção do fragmento de PlayLists do canal. Um dos fragmentos opcionais, mas que pode ajudar no engajamento do usuário ao canal.
Segue o link para acesso ao próximo conteúdo:
➙ Desenvolvendo a Tela e a Lógica de PlayLists - Parte 8.
Então é isso.
Descanse. Tome um café ☕ 🍰 ♟. E...
... te vejo na Parte 8 do projeto.
Se houverem dúvidas ou dicas deste sétimo conteúdo do aplicativo, então deixe nos comentários que logo eu lhe respondo.
Não esqueça de conhecer também o meu canal no YouTube (caso você ainda não conheça) e...
... não deixe de se inscrever na 📩 lista de e-mails para também garantir a versão em PDF não somente deste projeto de aplicativo Android, mas também de cada novo "conteúdo mini-curso".
Abraço.
Comentários Facebook