$30 off During Our Annual Pro Sale. View Details »

Android Training Program - Portugal, Aula 5

ATP Portugal
November 18, 2020

Android Training Program - Portugal, Aula 5

Aula #5: Listas, listas e mais listas 🥞

Por vezes temos de carregar e processar dados bastante pesados. Como é que o conseguimos fazer sem que a nossa aplicação não bloqueie?

- RecyclerView
- Bibliotecas externas
- Retrofit
- Glide
- Paging 3

ATP Portugal

November 18, 2020
Tweet

More Decks by ATP Portugal

Other Decks in Education

Transcript

  1. Android
    Training
    Program
    PORTUGAL
    Aula #5
    Listas, listas e mais listas

    View Slide

  2. ● Sejam excelentes uns para os outros
    ● Fale mais alto se vir ou ouvir alguma coisa
    ● O assédio não é tolerado
    ● Pratique "Sim e" um ao outro
    Código de conduta
    Mais informações: http://bit.ly/2IhF0l3

    View Slide

  3. Andres-Leonardo
    Martinez-Ortiz
    Google
    Carlos Mota
    Formador
    Renato Almeida
    Formador
    @davilagrau @cafonsomota @tallnato
    Equipa
    Daniela Ferreira
    Gestora de
    comunidades

    View Slide

  4. ● 12 aulas
    ● 1h30 cada aula
    ● ~1 aula por semana
    ● 14 Outubro a 16 Dezembro
    ● YouTube live
    ● Suporte assíncrono contínuo via Discord/email
    ● Todo o código disponível no GitHub
    Photo by Arif Riyanto on Unspla
    O programa

    View Slide

  5. #0
    14 de Outubro
    Pronto para
    começar
    #1
    21 de Outubro
    Bem-vindos ao
    Android
    #2
    28 de Outubro
    Fundações I
    #3
    04 de Novembro
    Fundações II
    #4
    11 de Novembro
    Fundações III
    #5
    18 de Novembro
    Listas, listas e
    mais listas
    #6
    25 de Novembro
    Jetpack,
    Jetpack,
    Jetpack!
    #7 - #8
    02 - 03 de Dezembro
    Firebase
    #9 - #10
    09 - 10 de Dezembro
    MLKit &
    TensorFlow
    #11
    16 de Dezembro
    Resumo
    Semana Semana
    Calendário
    ✅ ✅ ✅ ✅
    Direto

    View Slide

  6. Sumário
    Photo by Mika Baumeister on Unsplash
    ● Resumo da aula anterior
    ● RecyclerView
    ● Retrofit
    ● Glide
    ● Paging
    ● Kotlin para principiantes
    ● Sexta-Feira negra

    View Slide

  7. http://events.withgoogle.com/atp2020
    [email protected]
    http://bit.ly/atp2020-youtube
    http://bit.ly/atp2020-discord
    Links

    View Slide

  8. http://bit.ly/atp2020-live

    View Slide

  9. http://bit.ly/atp2020-codelabs

    View Slide

  10. http://bit.ly/kahoot-aula5

    View Slide

  11. Resumo da Aula #4

    View Slide

  12. LiveData
    ViewModel
    Room

    View Slide

  13. ● Permite separar os dados da sua representação gráfica
    ● Está associado a uma Activity ou Fragment
    ● Continua a ser executado mesmo que a aplicação não esteja visível
    ● Permanece em memória quando uma Activity é reconstruída
    ● Não deve aceder à interface gráfica da aplicação
    ViewModel

    View Slide

  14. View Slide

  15. ● Padrão de desenvolvimento de software
    ● Um objeto (Observable) notifica os interessados (Observers)
    ○ Quando este valor é alterado
    ● Os interessados subscrevem para receber as atualizações
    Observável?
    Observable
    Observer
    Observer
    Observer
    ...

    View Slide

  16. atualizar atualizar atualizar atualizar
    desenha o ecrã desenha o ecrã desenha o ecrã
    objetivo: 60fps
    UI-thread
    Operações assíncronas

    View Slide

  17. ● A aplicação não é fluida
    ○ Alguns frames podem não ser desenhados
    ● ANR podem ser lançados pelo sistema
    ○ ANR = Activity Not Responding
    ● Má experiência para o utilizador
    Operações assíncronas
    Counter isn’t responding

    View Slide

  18. ● Service
    ● Threads
    ● IntentService
    ● AsyncTasks
    ● WorkManager
    ● JobScheduler
    ● DownloadManager
    ● AlarmManager
    ● Coroutines
    Operações assíncronas
    Soluções

    View Slide

  19. Allow Snapchat to access
    this device’s location?
    Ao instalar todas as
    permissões requisitadas
    são dadas
    Funcionalidades do sistemas
    precisam de autorização
    explícita do utilizador
    As permissões vão sendo cada vez mais restritivas:
    - Apenas enquanto estamos a utilizar a aplicação
    - Permitir apenas uma única vez
    Android ... Android 6.0 … Android 10 Android 11

    View Slide

  20. Android 11
    ● Permissão única
    ● O utilizador tem de
    permitir o acesso
    contínuo à localização
    ○ Maior segurança
    ○ Poupança de bateria
    ● Revogação de permissões
    para aplicações que não
    são utilizadas

    View Slide

  21. Aula #5

    View Slide

  22. RecyclerView

    View Slide

  23. ● É a evolução da ListView
    ○ Com uma maior performance e flexibilidade
    ● Permite criar uma lista de objetos facilmente
    ● Esta lista tanto pode ser horizontal como vertical
    ○ Dependendo do LayoutManager definido
    ● É possibilidade adicionar animações (incríveis) por cada item modificado
    RecyclerView

    View Slide

  24. RecyclerView
    Activity
    RecyclerView
    LayoutManager
    Adapter
    ViewHolder
    data
    RecyclerView
    LayoutManager
    Adapter
    ViewHolder
    data
    Adapter
    ViewHolder
    data

    View Slide

  25. RecyclerView
    Activity
    RecyclerView
    LayoutManager
    Adapter
    ViewHolder
    data
    RecyclerView
    LayoutManager
    Adapter
    ViewHolder
    data
    Adapter
    ViewHolder
    data

    View Slide

  26. dependencies {
    ...
    implementation "androidx.recyclerview:recyclerview:1.1.0"
    }
    Como utilizar?
    Importar a biblioteca
    app/build.gradle

    View Slide

  27. class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    }
    }
    Como utilizar?
    Criar uma Activity/Fragment
    MainActivity.kt

    View Slide

  28. class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    }
    }
    Como utilizar?
    Criar uma Activity/Fragment
    MainActivity.kt
    ⚠ Não esquecer de a definir no AndroidManifest.xml

    View Slide

  29. android:id="@+id/rv_feed"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
    Como utilizar?
    Definir a RecyclerView no layout...
    res/layout/activity_main.xml

    View Slide


  30. ...
    android:id="@+id/iv_user_image"
    android:layout_width="35dp"
    android:layout_height="35dp"
    android:layout_weight="0"
    android:contentDescription="@string/description_user_profile"
    ndroid:scaleType="centerCrop"/>
    ...

    Como utilizar?
    … e como cada item deve ser
    res/layout/item_story.xml

    View Slide


  31. ...
    android:id="@+id/iv_user_image"
    android:layout_width="35dp"
    android:layout_height="35dp"
    android:layout_weight="0"
    android:contentDescription="@string/description_user_profile"
    ndroid:scaleType="centerCrop"/>
    ...

    Como utilizar?
    … e como cada item deve ser

    View Slide

  32. RecyclerView
    RecyclerView
    LayoutManager
    Adapter
    ViewHolder
    data
    Activity
    RecyclerView
    LayoutManager
    Adapter
    ViewHolder
    data
    Adapter
    ViewHolder
    data

    View Slide

  33. private fun setup() {
    findViewById(R.id.rv_feed).apply {
    setHasFixedSize(true)
    layoutManager = LinearLayoutManager(context)
    adapter = FeedAdapter(Dogs.dogs)
    }
    }
    Como utilizar?
    Definir a RecyclerView na Activity
    MainActivity.kt

    View Slide

  34. private fun setup() {
    findViewById(R.id.rv_feed).apply {
    setHasFixedSize(true)
    layoutManager = LinearLayoutManager(context)
    adapter = FeedAdapter(Dogs.dogs)
    }
    }
    Como utilizar?
    Definir a RecyclerView na Activity
    MainActivity.kt

    View Slide

  35. private fun setup() {
    findViewById(R.id.rv_feed).apply {
    setHasFixedSize(true)
    layoutManager = LinearLayoutManager(context)
    adapter = FeedAdapter(Dogs.dogs)
    }
    }
    Como utilizar?
    Definir a RecyclerView na Activity
    MainActivity.kt

    View Slide

  36. private fun setup() {
    findViewById(R.id.rv_feed).apply {
    setHasFixedSize(true)
    layoutManager = LinearLayoutManager(context)
    adapter = FeedAdapter(Dogs.dogs)
    }
    }
    Como utilizar?
    Definir a RecyclerView na Activity
    MainActivity.kt

    View Slide

  37. private fun setup() {
    findViewById(R.id.rv_feed).apply {
    setHasFixedSize(true)
    layoutManager = LinearLayoutManager(context, HORIZONTAL, false))
    adapter = FeedAdapter(Dogs.dogs)
    }
    }
    Como utilizar?
    Definir a RecyclerView na Activity
    MainActivity.kt

    View Slide

  38. private fun setup() {
    findViewById(R.id.rv_feed).apply {
    setHasFixedSize(true)
    layoutManager = LinearLayoutManager(context, HORIZONTAL, false))
    adapter = FeedAdapter(Dogs.dogs)
    }
    }
    Como utilizar?
    Definir a RecyclerView na Activity
    MainActivity.kt

    View Slide

  39. private fun setup() {
    findViewById(R.id.rv_feed).apply {
    setHasFixedSize(true)
    layoutManager = LinearLayoutManager(context, HORIZONTAL, false))
    adapter = FeedAdapter(Dogs.dogs)
    }
    }
    Como utilizar?
    Definir a RecyclerView na Activity
    MainActivity.kt

    View Slide

  40. RecyclerView
    Activity
    RecyclerView
    LayoutManager
    Adapter
    ViewHolder
    data
    RecyclerView
    LayoutManager
    Adapter
    ViewHolder
    data
    Adapter
    ViewHolder
    data

    View Slide

  41. class FeedAdapter constructor(val dogs: List) :
    RecyclerView.Adapter() {
    override fun onCreateViewHolder(group: ViewGroup, type: Int): MainViewHolder
    override fun getItemCount(): Int
    override fun onBindViewHolder(holder: MainViewHolder, position: Int)
    }
    Como utilizar?
    Cria o Adapter
    FeedAdapter.kt

    View Slide

  42. class FeedAdapter constructor(val dogs: List) :
    RecyclerView.Adapter() {
    override fun onCreateViewHolder(group: ViewGroup, type: Int): MainViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return MainViewHolder(inflater.inflate(R.layout.item_feed, parent, false))
    }
    }
    Como utilizar?
    Cria o Adapter
    FeedAdapter.kt

    View Slide

  43. class FeedAdapter constructor(val dogs: List) :
    RecyclerView.Adapter() {
    override fun onCreateViewHolder(group: ViewGroup, type: Int): MainViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return MainViewHolder(inflater.inflate(R.layout.item_feed, parent, false))
    }
    override fun getItemCount() = dogs.size
    }
    Como utilizar?
    Cria o Adapter
    FeedAdapter.kt

    View Slide

  44. class FeedAdapter constructor(val dogs: List) : RecyclerView.Adapter() {
    override fun onCreateViewHolder(group: ViewGroup, type: Int): MainViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return MainViewHolder(inflater.inflate(R.layout.item_feed, parent, false))
    }
    override fun getItemCount() = dogs.size
    override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
    val feed = dogs[position]
    holder.userImage.setImageResource(feed.picture)
    holder.userName.text = feed.name
    holder.image.setImageResource(feed.picture)
    }
    Como utilizar?
    Cria o Adapter
    FeedAdapter.kt

    View Slide

  45. class FeedAdapter constructor(val dogs: List) : RecyclerView.Adapter() {
    override fun onCreateViewHolder(group: ViewGroup, type: Int): MainViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return MainViewHolder(inflater.inflate(R.layout.item_feed, parent, false))
    }
    override fun getItemCount() = dogs.size
    override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
    val feed = dogs[position]
    holder.userImage.setImageResource(feed.picture)
    holder.userName.text = feed.name
    holder.image.setImageResource(feed.picture)
    }
    class MainViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val userImage = itemView.iv_user_image!!
    val userName = itemView.tv_user_name!!
    val image = itemView.iv_image!!
    }
    }
    Como utilizar?
    Cria o Adapter
    FeedAdapter.kt

    View Slide

  46. MainActivity.kt

    View Slide

  47. Retrofit

    View Slide

  48. ● Cliente REST fortemente tipado
    ● Facilita a transferência de JSON através de um webservice
    ○ JSON ou outro tipos de dados
    Retrofit
    https://github.com/square/retrofit

    View Slide

  49. O Retrofit transforma uma API HTTP numa interface

    View Slide

  50. The Dog API

    View Slide

  51. The Dog API

    View Slide

  52. The Dog API
    interface DogApiClient {
    @GET("breeds")
    fun getBreeds() : List
    }

    View Slide

  53. The Dog API
    interface DogApiClient {
    @GET("breeds")
    fun getBreeds() : List
    }

    View Slide

  54. Resultado da API
    {
    "bred_for": "Small rodent hunting,
    lapdog",
    "breed_group": "Toy",
    "height": {
    "imperial": "9 - 11.5",
    "metric": "23 - 29"
    },
    "id": 1,
    "life_span": "10 - 12 years",
    "name": "Affenpinscher",
    "origin": "Germany, France",
    "temperament": "Stubborn, Curious,
    Playful, Adventurous, Active,
    Fun-loving",
    "weight": {
    "imperial": "6 - 13",
    "metric": "3 - 6"
    }
    }
    data class Dog (
    val bred_for : String,
    val breed_group : String,
    val height : Height,
    val id : Int,
    val life_span : String,
    val name : String,
    val origin : String,
    val temperament : String,
    val weight : Weight
    )
    data class Height (
    val imperial : String,
    val metric : String
    )
    data class Weight (
    val imperial : String,
    val metric : String
    )

    View Slide

  55. Resultado da API
    "height": {
    "imperial": "9 - 11.5",
    "metric": "23 - 29"
    }
    data class Height (
    val imperial : String,
    val metric : String
    )

    View Slide

  56. Resultado da API
    "weight": {
    "imperial": "6 - 13",
    "metric": "3 - 6"
    }
    data class Weight (
    val imperial : String,
    val metric : String
    )

    View Slide

  57. Resultado da API
    {
    "bred_for": "Small rodent hunting,
    lapdog",
    "breed_group": "Toy",
    "height": {...},
    "id": 1,
    "life_span": "10 - 12 years",
    "name": "Affenpinscher",
    "origin": "Germany, France",
    "temperament": "Stubborn, Curious,
    Playful, Adventurous, Active,
    Fun-loving",
    "weight": {...}
    },
    ...
    data class Dog (
    val bred_for : String,
    val breed_group : String,
    val height : Height,
    val id : Int,
    val life_span : String,
    val name : String,
    val origin : String,
    val temperament : String,
    val weight : Weight
    )

    View Slide

  58. Agora mais bonito ✨
    {
    "bred_for": "Small rodent hunting,
    lapdog",
    "breed_group": "Toy",
    "height": {...},
    "id": 1,
    "life_span": "10 - 12 years",
    "name": "Affenpinscher",
    "origin": "Germany, France",
    "temperament": "Stubborn, Curious,
    Playful, Adventurous, Active,
    Fun-loving",
    "weight": {...}
    },
    ...
    data class Dog(
    @Json(name = "bred_for")
    val bredFor: String,
    @Json(name = "breed_group")
    val breedGroup: String,
    val height: Height,
    val id: Int,
    @Json(name = "life_span")
    val lifeSpan: String,
    val name: String,
    val origin: String,
    val temperament: String,
    val weight: Weight
    )

    View Slide

  59. ● Biblioteca de conversão de JSON para Kotlin e Java
    ● Optimizada para Android
    ● Compatível Retrofit
    Moshi

    View Slide

  60. dependencies {
    ...
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
    }
    Como utilizar?
    Importar a biblioteca
    app/build.gradle

    View Slide

  61. dependencies {
    ...
    def retrofitVersion = '2.9.0'
    implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
    implementation "com.squareup.retrofit2:converter-moshi:$retrofitVersion"
    }
    Como utilizar?
    Importar a biblioteca
    app/build.gradle

    View Slide

  62. interface DogApiClient {
    @GET("breeds")
    fun getBreeds() : Call>
    }
    Como utilizar?
    A interface
    DogApiClient.kt

    View Slide

  63. interface DogApiClient {
    @GET("breeds")
    fun getBreeds() : Call>
    }
    Como utilizar?
    A interface
    DogApiClient.kt

    Permite realizar
    trabalho em
    background

    View Slide

  64. class ApiProvider {
    private val retrofit = Retrofit.Builder()
    .baseUrl("https://api.thedogapi.com/v1")
    .addConverterFactory(MoshiConverterFactory.create())
    .build()
    fun getDogApi(): DogApiClient {
    return retrofit.create()
    }
    }
    Como utilizar?
    A interface
    ApiProvider.kt

    View Slide

  65. class ApiProvider {
    private val retrofit = Retrofit.Builder()
    .baseUrl("https://api.thedogapi.com/v1/")
    .addConverterFactory(MoshiConverterFactory.create())
    .build()
    fun getDogApi(): DogApiClient {
    return retrofit.create()
    }
    }
    Como utilizar?
    A interface
    ApiProvider.kt

    Por causa da
    inferência de tipo
    não precisamos de
    colocar o tipo

    View Slide

  66. private val provider = ApiProvider()
    private val dogApi = provider.getDogApi()
    fun getDogs() {
    dogApi.getBreeds()
    .enqueue(object : Callback> {
    override fun onResponse(call: Call>, response: Response>) {
    if (response.isSuccessful) {
    val dogs = response.body()
    Log.d(TAG, "dogs $dogs")
    }
    }
    override fun onFailure(call: Call>, t: Throwable) {
    Log.e(TAG, "Error loading dogs", t)
    }
    })
    }
    Como utilizar?
    Obter os cães

    View Slide

  67. private val provider = ApiProvider()
    private val dogApi = provider.getDogApi()
    fun getDogs() {
    dogApi.getBreeds()
    .enqueue(object : Callback> {
    override fun onResponse(call: Call>, response: Response>) {
    if (response.isSuccessful) {
    val dogs = response.body()
    Log.d(TAG, "dogs $dogs")
    }
    }
    override fun onFailure(call: Call>, t: Throwable) {
    Log.e(TAG, "Error loading dogs", t)
    }
    })
    }
    Como utilizar?
    Obter os cães

    View Slide

  68. private val provider = ApiProvider()
    private val dogApi = provider.getDogApi()
    fun getDogs() {
    dogApi.getBreeds()
    .enqueue(object : Callback> {
    override fun onResponse(call: Call>, response: Response>) {
    if (response.isSuccessful) {
    val dogs = response.body()
    Log.d(TAG, "dogs $dogs")
    }
    }
    override fun onFailure(call: Call>, t: Throwable) {
    Log.e(TAG, "Error loading dogs", t)
    }
    })
    }
    Como utilizar?
    Obter os cães

    View Slide

  69. private val provider = ApiProvider()
    private val dogApi = provider.getDogApi()
    fun getDogs() {
    dogApi.getBreeds()
    .enqueue(object : Callback> {
    override fun onResponse(call: Call>, response: Response>) {
    if (response.isSuccessful) {
    val dogs = response.body()
    Log.d(TAG, "dogs $dogs")
    }
    }
    override fun onFailure(call: Call>, t: Throwable) {
    Log.e(TAG, "Error loading dogs", t)
    }
    })
    }
    Como utilizar?
    Obter os cães

    View Slide

  70. private val provider = ApiProvider()
    private val dogApi = provider.getDogApi()
    fun getDogs() {
    dogApi.getBreeds()
    .enqueue(object : Callback> {
    override fun onResponse(call: Call>, response: Response>) {
    if (response.isSuccessful) {
    val dogs = response.body()
    Log.d(TAG, "dogs $dogs")
    }
    }
    override fun onFailure(call: Call>, t: Throwable) {
    Log.e(TAG, "Error loading dogs", t)
    }
    })
    }
    Como utilizar?
    Obter os cães

    View Slide

  71. private val provider = ApiProvider()
    private val dogApi = provider.getDogApi()
    fun getDogs() {
    dogApi.getBreeds()
    .enqueue(object : Callback> {
    override fun onResponse(call: Call>, response: Response>) {
    if (response.isSuccessful) {
    val dogs = response.body()
    Log.d(TAG, "dogs $dogs")
    }
    }
    override fun onFailure(call: Call>, t: Throwable) {
    Log.e(TAG, "Error loading dogs", t)
    }
    })
    }
    Como utilizar?
    Obter os cães

    View Slide

  72. dogs [Dog(bredFor=null, breedGroup=null, height=Height(imperial=9 - 11.5, metric=23 - 29),
    id=1, lifeSpan=null, name=Affenpinscher, origin=Germany, France, temperament=Stubborn,
    Curious, Playful, Adventurous, Active, Fun-loving, weight=Weight(imperial=6 - 13, metric=3
    - 6)), Dog(bredFor=null, breedGroup=null, height=Height(imperial=25 - 27, metric=64 - 69),
    id=2, lifeSpan=null, name=Afghan Hound, origin=Afghanistan, Iran, Pakistan,
    temperament=Aloof, Clownish, Dignified, Independent, Happy, weight=Weight(imperial=50 - 60,
    metric=23 - 27)), Dog(bredFor=null, breedGroup=null, height=Height(imperial=30, metric=76),
    id=3, lifeSpan=null, name=African Hunting Dog, origin=, temperament=Wild, Hardworking,
    Dutiful, weight=Weight(imperial=44 - 66, metric=20 - 30)), Dog(bredFor=null,
    breedGroup=null, height=Height(imperial=21 - 23, metric=53 - 58), id=4, lifeSpan=null,
    name=Airedale Terrier, origin=United Kingdom, England, temperament=Outgoing, Friendly,
    Alert, Confident, Intelligent, Courageous, weight=Weight(imperial=40 - 65, metric=18 -
    29)), Dog(bredFor=null, breedGroup=null, height=Height(imperial=28 - 34, metric=71 - 86),
    id=5, lifeSpan=null, name=Akbash Dog, origin=, temperament=Loyal, Independent...
    Resultado

    View Slide

  73. Glide

    View Slide

  74. ● Glide é uma biblioteca de carregamento de imagens para Android
    ● Rápida e eficiente
    ● Código aberto, disponível no GitHub
    Glide
    https://github.com/bumptech/glide

    View Slide

  75. ● Descarrega, descodifica e mostra
    ○ Imagens
    ○ GIF’s
    ● Permite também redimensionar imagens
    ● Cache automática e simplificada das imagens
    ● Expõe uma API flexível e simples de utilizar
    Vantagens

    View Slide

  76. ● Permitir uma transição suave e rápido em qualquer tipo de lista de imagens
    ● Eficiente a carregar, redimensionar e descarregar imagens facilmente
    Objetivos

    View Slide

  77. Exemplo
    Glide.with(activity)
    .load("https://placedog.net/500")
    .into(imageView)

    View Slide

  78. Exemplo GIF
    Glide.with(activity)
    .load("https://media.giphy.com/media/xTiTnf9SCIVk8HIvE4/giphy.gif")
    .into(imageView)

    View Slide

  79. Placeholder
    Glide.with(activity)
    .load("https://placedog.net/500")
    .placeholder(R.drawable.ic_camera)
    .into(imageView)

    View Slide

  80. Error
    Glide.with(activity)
    .load("url_que_não_tem_imagem")
    .error(R.drawable.ic_error)
    .into(imageView)

    View Slide

  81. Transformações
    Glide.with(activity)
    .load("https://placedog.net/500")
    .transform(CircleCrop())
    .into(imageView)

    View Slide

  82. Transformações
    Glide.with(activity)
    .load("https://placedog.net/500")
    .centerCrop()
    .into(imageView)

    View Slide

  83. Cache
    Glide.with(activity)
    .load("https://placedog.net/500")
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .into(imageView)

    View Slide

  84. ● DiskCacheStrategy.ALL
    ○ Faz cache de tudo
    ● DiskCacheStrategy.AUTOMATIC
    ○ Tenta escolher a melhor estratégia de forma inteligente de acordo com a origem
    ● DiskCacheStrategy.DATA
    ○ Faz cache da imagem original no disco antes de a descodificar
    ● DiskCacheStrategy.NONE
    ○ Não faz cache de nada
    ● DiskCacheStrategy.RESOURCE
    ○ Faz cache da imagem depois de algum processamento
    Estratégia de cache

    View Slide

  85. dependencies {
    ...
    implementation "com.github.bumptech.glide:glide:4.11.0"
    }
    Como utilizar?
    Importar a biblioteca
    app/build.gradle

    View Slide

  86. // Em Activities
    Glide.with(activity)
    // Em Fragments
    Glide.with(fragment)
    // Ou com o Context
    Glide.with(context)
    Como utilizar?
    No código
    Glide.with( )
    .load(url)
    .into(imageView)

    View Slide

  87. Paging

    View Slide

  88. ● Versão melhorada e otimizada do seu antecessor - Paging 2
    ● Suporte direto para Kotlin e Flow
    ● Permite carregamentos assíncronos
    ● Muito mais simples de integrar
    ● Facilidade de adicionar um item de carregamento
    ○ Que indica o utilizador que novos dados estão a ser descarregados
    Paging 3

    View Slide

  89. Paging 3
    Repository
    PagingSource
    RemoteMediator
    ViewModel
    Pager PagingDataAdapter
    Flow

    View Slide

  90. dependencies {
    ...
    implementation "androidx.paging:paging-runtime:3.0.0-alpha03"
    }
    Como utilizar?
    Importar a biblioteca
    app/build.gradle

    View Slide

  91. class DogPagingSource(private val repository: Repository) :
    PagingSource() {
    override suspend fun load(params: LoadParams): LoadResult {
    val nextPage = params.key ?: 1
    val breeds = repository.getPagedBreedsList(nextPage)
    return LoadResult.Page(
    data = breeds,
    prevKey = if (nextPage == 1) null else nextPage -1,
    nextKey = nextPage + 1
    )
    }
    }
    Como utilizar?
    PagingSource
    DogsPagingSource.kt

    View Slide

  92. class DogPagingSource(private val repository: Repository) :
    PagingSource() {
    override suspend fun load(params: LoadParams): LoadResult {
    val nextPage = params.key ?: 1
    val breeds = repository.getPagedBreedsList(nextPage)
    return LoadResult.Page(
    data = breeds,
    prevKey = if (nextPage == 1) null else nextPage -1,
    nextKey = nextPage + 1
    )
    }
    }
    Como utilizar?
    PagingSource
    DogsPagingSource.kt

    View Slide

  93. class DogPagingSource(private val repository: Repository) :
    PagingSource() {
    override suspend fun load(params: LoadParams): LoadResult {
    val nextPage = params.key ?: 1
    val breeds = repository.getPagedBreedsList(nextPage)
    return LoadResult.Page(
    data = breeds,
    prevKey = if (nextPage == 1) null else nextPage -1,
    nextKey = nextPage + 1
    )
    }
    }
    Como utilizar?
    PagingSource
    DogsPagingSource.kt

    View Slide

  94. class ListViewModel: ViewModel() {
    private val repository = Repository()
    val breedsListByPage = Pager(PagingConfig(pageSize = 20), pagingSourceFactory = {
    DogPagingSource(repository)
    }).flow
    }
    Como utilizar?
    ViewModel
    ListViewModel.kt

    View Slide

  95. class ListViewModel: ViewModel() {
    private val repository = Repository()
    val breedsListByPage = Pager(PagingConfig(pageSize = 20), pagingSourceFactory = {
    DogPagingSource(repository)
    }).flow
    }
    Como utilizar?
    ViewModel
    ListViewModel.kt

    View Slide

  96. class ListViewModel: ViewModel() {
    private val repository = Repository()
    val breedsListByPage = Pager(PagingConfig(pageSize = 20), pagingSourceFactory = {
    DogPagingSource(repository)
    }).flow
    }
    Como utilizar?
    ViewModel
    ListViewModel.kt

    View Slide

  97. class ListViewModel: ViewModel() {
    private val repository = Repository()
    val breedsListByPage = Pager(PagingConfig(pageSize = 20), pagingSourceFactory = {
    DogPagingSource(repository)
    }).flow
    }
    Como utilizar?
    ViewModel
    ListViewModel.kt

    View Slide

  98. class ListActivity : AppCompatActivity() {
    val viewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
    ...
    lifecycleScope.launch{
    viewModel.breedsListByPage.collect {
    val adapter = findViewById(R.id.rv_breeds).adapter as BreedsAdapter
    adapter.submitData(it)
    }
    }
    }
    }
    Como utilizar?
    Activity
    ListActivity.kt

    View Slide

  99. class ListActivity : AppCompatActivity() {
    val viewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
    ...
    lifecycleScope.launch{
    viewModel.breedsListByPage.collect {
    val adapter = findViewById(R.id.rv_breeds).adapter as BreedsAdapter
    adapter.submitData(it)
    }
    }
    }
    }
    Como utilizar?
    Activity
    ListActivity.kt

    View Slide

  100. class ListActivity : AppCompatActivity() {
    val viewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
    ...
    lifecycleScope.launch{
    viewModel.breedsListByPage.collect {
    val adapter = findViewById(R.id.rv_breeds).adapter as BreedsAdapter
    adapter.submitData(it)
    }
    }
    }
    }
    Como utilizar?
    Activity
    ListActivity.kt

    View Slide

  101. class BreedsAdapter() :
    ListAdapter() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BreedsViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return BreedsViewHolder(inflater.inflate(R.layout.item_breed, parent, false))
    }
    override fun onBindViewHolder(holder: BreedsViewHolder, position: Int) {
    val breed = getItem(position)
    holder.breed.text = breed!!.name
    }
    }
    Como utilizar?
    Adapter
    BreedsAdapter.kt

    View Slide

  102. class BreedsAdapter() :
    ListAdapter() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BreedsViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return BreedsViewHolder(inflater.inflate(R.layout.item_breed, parent, false))
    }
    override fun onBindViewHolder(holder: BreedsViewHolder, position: Int) {
    val breed = getItem(position)
    holder.breed.text = breed!!.name
    }
    }
    Como utilizar?
    Adapter
    BreedsAdapter.kt

    View Slide

  103. class BreedsAdapter() :
    PagingDataAdapter() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BreedsViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return BreedsViewHolder(inflater.inflate(R.layout.item_breed, parent, false))
    }
    override fun onBindViewHolder(holder: BreedsViewHolder, position: Int) {
    val breed = getItem(position)
    holder.breed.text = breed!!.name
    }
    }
    Como utilizar?
    Adapter
    BreedsAdapter.kt

    View Slide

  104. class BreedsAdapter() :
    PagingDataAdapter() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BreedsViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return BreedsViewHolder(inflater.inflate(R.layout.item_breed, parent, false))
    }
    override fun onBindViewHolder(holder: BreedsViewHolder, position: Int) {
    val breed = getItem(position)
    holder.breed.text = breed!!.name
    }
    }
    Como utilizar?
    Adapter
    BreedsAdapter.kt

    View Slide

  105. class BreedsAdapter() :
    PagingDataAdapter() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BreedsViewHolder {
    val inflater = LayoutInflater.from(parent.context)
    return BreedsViewHolder(inflater.inflate(R.layout.item_breed, parent, false))
    }
    override fun onBindViewHolder(holder: BreedsViewHolder, position: Int) {
    val breed = getItem(position)
    holder.breed.text = breed!!.name
    }
    }
    Como utilizar?
    Adapter
    BreedsAdapter.kt

    View Slide

  106. Passaporte para
    a Google

    View Slide

  107. Abre o Android
    Studio e vamos
    começar a
    programar
    ‍‍

    View Slide

  108. VS
    Ronda 4

    View Slide

  109. object Singleton {
    }
    Singleton ☝
    public class Singleton {
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
    if(instance == null){
    instance = new Singleton();
    }
    return instance;
    }
    }

    View Slide

  110. interface Kennel {
    fun getDog(name: String)
    }
    val kennel = object : Kennel {
    override fun getDog(name: String) {...}
    }
    SAM (Single Abstract Method)

    View Slide

  111. interface Kennel {
    fun getDog(name: String)
    }
    val kennel = object : Kennel {
    override fun getDog(name: String) {...}
    }
    val kennel = Kennel { name -> ...}
    SAM (Single Abstract Method)

    View Slide

  112. class ClassWithAHugeNameThatDoestReallyMatter
    typealias ShortName =
    ClassWithAHugeNameThatDoestReallyMatter
    typealias Dogs = List
    val dogs: Dogs = mutableListOf()
    TypeAlias

    View Slide

  113. Sexta-Feira
    negra
    Photo by Xiaolong Wong on Unsplash

    View Slide

  114. Aplicar uma máscara sobre uma view


    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    android:topRightRadius="25dp"
    android:topLeftRadius="25dp"
    android:bottomRightRadius="25dp"
    android:bottomLeftRadius="25dp"/>

    View Slide

  115. Aplicar uma máscara sobre uma view


    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval" />

    View Slide

  116. Dividir o ecrã

    Para ativar:
    1. Botão direito sobre a tab que queremos dividir
    2. “Split Vertically”

    View Slide

  117. Android Studio

    Mover linhas de código - Bloco
    ctrl shift
    +

    +
    https://developer.android.com/studio/intro/keyboard-shortcuts

    View Slide

  118. Android Studio

    Mover linhas de código - Bloco
    cmd shift
    +

    +
    https://developer.android.com/studio/intro/keyboard-shortcuts

    View Slide

  119. Android Studio

    Mover linhas de código - Bloco
    cmd shift
    +

    +
    https://developer.android.com/studio/intro/keyboard-shortcuts

    View Slide

  120. Android Studio

    Mover linhas de código - Bloco
    cmd shift
    +

    +
    https://developer.android.com/studio/intro/keyboard-shortcuts

    View Slide

  121. Trabalho Para Casa
    ‍‍

    View Slide

  122. Trabalho para casa
    ● Implementar a vista de detalhes para um cão
    ○ Nome
    ○ Imagem
    ○ Detalhes

    View Slide

  123. Trabalho para casa
    ● Carregar num dos itens da RecyclerView
    ○ Abre a vista de detalhes para aquele cão

    View Slide

  124. Dúvidas?

    View Slide

  125. Continuamos a
    responder no
    discord

    View Slide

  126. Obrigado
    ‍♀

    View Slide

  127. Android
    Training
    Program
    PORTUGAL
    Aula #6
    Jetpack, jetpack, jetpack
    Próxima aula: 25 de Novembro

    View Slide