Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Training Program - Portugal, Aula 5

9e9eb825c69d719f2d3c32bdd3bc971e?s=47 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

9e9eb825c69d719f2d3c32bdd3bc971e?s=128

ATP Portugal

November 18, 2020
Tweet

Transcript

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

    listas
  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
  3. Andres-Leonardo Martinez-Ortiz Google Carlos Mota Formador Renato Almeida Formador @davilagrau

    @cafonsomota @tallnato Equipa Daniela Ferreira Gestora de comunidades
  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
  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 ✅
  6. Sumário Photo by Mika Baumeister on Unsplash • Resumo da

    aula anterior • RecyclerView • Retrofit • Glide • Paging • Kotlin para principiantes • Sexta-Feira negra
  7. http://events.withgoogle.com/atp2020 ✉ atp-suporte@googlegroups.com http://bit.ly/atp2020-youtube http://bit.ly/atp2020-discord Links

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

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

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

  11. Resumo da Aula #4

  12. LiveData ViewModel Room

  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
  14. None
  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 ...
  16. atualizar atualizar atualizar atualizar desenha o ecrã desenha o ecrã

    desenha o ecrã objetivo: 60fps UI-thread Operações assíncronas
  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
  18. • Service • Threads • IntentService • AsyncTasks • WorkManager

    • JobScheduler • DownloadManager • AlarmManager • Coroutines Operações assíncronas Soluções
  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
  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
  21. Aula #5

  22. RecyclerView

  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
  24. RecyclerView Activity RecyclerView LayoutManager Adapter ViewHolder data RecyclerView LayoutManager Adapter

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

    ViewHolder data Adapter ViewHolder data
  26. dependencies { ... implementation "androidx.recyclerview:recyclerview:1.1.0" } Como utilizar? Importar a

    biblioteca app/build.gradle
  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
  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
  29. <androidx.recyclerview.widget.RecyclerView 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
  30. <?xml version="1.0" encoding="utf-8"?> <LinearLayout ... <ImageView 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"/> ... </LinearLayout> Como utilizar? … e como cada item deve ser res/layout/item_story.xml
  31. <?xml version="1.0" encoding="utf-8"?> <LinearLayout ... <ImageView 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"/> ... </LinearLayout> Como utilizar? … e como cada item deve ser
  32. RecyclerView RecyclerView LayoutManager Adapter ViewHolder data Activity RecyclerView LayoutManager Adapter

    ViewHolder data Adapter ViewHolder data
  33. private fun setup() { findViewById<RecyclerView>(R.id.rv_feed).apply { setHasFixedSize(true) layoutManager = LinearLayoutManager(context)

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

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

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

    adapter = FeedAdapter(Dogs.dogs) } } Como utilizar? Definir a RecyclerView na Activity MainActivity.kt
  37. private fun setup() { findViewById<RecyclerView>(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
  38. private fun setup() { findViewById<RecyclerView>(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
  39. private fun setup() { findViewById<RecyclerView>(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
  40. RecyclerView Activity RecyclerView LayoutManager Adapter ViewHolder data RecyclerView LayoutManager Adapter

    ViewHolder data Adapter ViewHolder data
  41. class FeedAdapter constructor(val dogs: List<Dog>) : RecyclerView.Adapter<FeedAdapter.MainViewHolder>() { 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
  42. class FeedAdapter constructor(val dogs: List<Dog>) : RecyclerView.Adapter<FeedAdapter.MainViewHolder>() { 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
  43. class FeedAdapter constructor(val dogs: List<Dog>) : RecyclerView.Adapter<FeedAdapter.MainViewHolder>() { 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
  44. class FeedAdapter constructor(val dogs: List<Dog>) : RecyclerView.Adapter<FeedAdapter.MainViewHolder>() { 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
  45. class FeedAdapter constructor(val dogs: List<Dog>) : RecyclerView.Adapter<FeedAdapter.MainViewHolder>() { 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
  46. MainActivity.kt

  47. Retrofit

  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
  49. O Retrofit transforma uma API HTTP numa interface

  50. The Dog API

  51. The Dog API

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

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

    List<Dog> }
  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 )
  55. Resultado da API "height": { "imperial": "9 - 11.5", "metric":

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

    "3 - 6" } data class Weight ( val imperial : String, val metric : String )
  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 )
  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 )
  59. • Biblioteca de conversão de JSON para Kotlin e Java

    • Optimizada para Android • Compatível Retrofit Moshi
  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
  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
  62. interface DogApiClient { @GET("breeds") fun getBreeds() : Call<List<Dog>> } Como

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

    utilizar? A interface DogApiClient.kt Permite realizar trabalho em background
  64. class ApiProvider { private val retrofit = Retrofit.Builder() .baseUrl("https://api.thedogapi.com/v1") .addConverterFactory(MoshiConverterFactory.create())

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

    .build() fun getDogApi(): DogApiClient { return retrofit.create<DogApiClient>() } } Como utilizar? A interface ApiProvider.kt Por causa da inferência de tipo não precisamos de colocar o tipo
  66. private val provider = ApiProvider() private val dogApi = provider.getDogApi()

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

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

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

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

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

    fun getDogs() { dogApi.getBreeds() .enqueue(object : Callback<List<Dog>> { override fun onResponse(call: Call<List<Dog>>, response: Response<List<Dog>>) { if (response.isSuccessful) { val dogs = response.body() Log.d(TAG, "dogs $dogs") } } override fun onFailure(call: Call<List<Dog>>, t: Throwable) { Log.e(TAG, "Error loading dogs", t) } }) } Como utilizar? Obter os cães
  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
  73. Glide

  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
  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
  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
  77. Exemplo Glide.with(activity) .load("https://placedog.net/500") .into(imageView)

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

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

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

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

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

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

  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
  85. dependencies { ... implementation "com.github.bumptech.glide:glide:4.11.0" } Como utilizar? Importar a

    biblioteca app/build.gradle
  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)
  87. Paging

  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
  89. Paging 3 Repository PagingSource RemoteMediator ViewModel Pager PagingDataAdapter Flow<PagingData>

  90. dependencies { ... implementation "androidx.paging:paging-runtime:3.0.0-alpha03" } Como utilizar? Importar a

    biblioteca app/build.gradle
  91. class DogPagingSource(private val repository: Repository) : PagingSource<Int, Breed>() { override

    suspend fun load(params: LoadParams<Int>): LoadResult<Int, Breed> { 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
  92. class DogPagingSource(private val repository: Repository) : PagingSource<Int, Breed>() { override

    suspend fun load(params: LoadParams<Int>): LoadResult<Int, Breed> { 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
  93. class DogPagingSource(private val repository: Repository) : PagingSource<Int, Breed>() { override

    suspend fun load(params: LoadParams<Int>): LoadResult<Int, Breed> { 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
  94. class ListViewModel: ViewModel() { private val repository = Repository() val

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

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

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

    breedsListByPage = Pager(PagingConfig(pageSize = 20), pagingSourceFactory = { DogPagingSource(repository) }).flow } Como utilizar? ViewModel ListViewModel.kt
  98. class ListActivity : AppCompatActivity() { val viewModel by viewModels<ListViewModel>() override

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

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

    fun onCreate(savedInstanceState: Bundle?) { ... lifecycleScope.launch{ viewModel.breedsListByPage.collect { val adapter = findViewById<RecyclerView>(R.id.rv_breeds).adapter as BreedsAdapter adapter.submitData(it) } } } } Como utilizar? Activity ListActivity.kt
  101. class BreedsAdapter() : ListAdapter<Breed, BreedsAdapter.BreedsViewHolder>() { 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
  102. class BreedsAdapter() : ListAdapter<Breed, BreedsAdapter.BreedsViewHolder>() { 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
  103. class BreedsAdapter() : PagingDataAdapter<Breed, BreedsAdapter.BreedsViewHolder>() { 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
  104. class BreedsAdapter() : PagingDataAdapter<Breed, BreedsAdapter.BreedsViewHolder>() { 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
  105. class BreedsAdapter() : PagingDataAdapter<Breed, BreedsAdapter.BreedsViewHolder>() { 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
  106. Passaporte para a Google

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

  108. VS Ronda 4

  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; } }
  110. interface Kennel { fun getDog(name: String) } val kennel =

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

    object : Kennel { override fun getDog(name: String) {...} } val kennel = Kennel { name -> ...} SAM (Single Abstract Method)
  112. class ClassWithAHugeNameThatDoestReallyMatter typealias ShortName = ClassWithAHugeNameThatDoestReallyMatter typealias Dogs = List<Dog>

    val dogs: Dogs = mutableListOf() TypeAlias
  113. Sexta-Feira negra Photo by Xiaolong Wong on Unsplash

  114. Aplicar uma máscara sobre uma view ⚪ <?xml version="1.0" encoding="utf-8"?>

    <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="@color/colorPrimary" /> <corners android:topRightRadius="25dp" android:topLeftRadius="25dp" android:bottomRightRadius="25dp" android:bottomLeftRadius="25dp"/> </shape>
  115. Aplicar uma máscara sobre uma view ⚪ <?xml version="1.0" encoding="utf-8"?>

    <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" />
  116. Dividir o ecrã Para ativar: 1. Botão direito sobre a

    tab que queremos dividir 2. “Split Vertically”
  117. Android Studio ⌨ Mover linhas de código - Bloco ctrl

    shift + ⬆ + https://developer.android.com/studio/intro/keyboard-shortcuts ⬇
  118. Android Studio ⌨ Mover linhas de código - Bloco cmd

    shift + ⬆ + https://developer.android.com/studio/intro/keyboard-shortcuts ⬇
  119. Android Studio ⌨ Mover linhas de código - Bloco cmd

    shift + ⬆ + https://developer.android.com/studio/intro/keyboard-shortcuts ⬇
  120. Android Studio ⌨ Mover linhas de código - Bloco cmd

    shift + ⬆ + https://developer.android.com/studio/intro/keyboard-shortcuts ⬇
  121. Trabalho Para Casa ‍‍

  122. Trabalho para casa • Implementar a vista de detalhes para

    um cão ◦ Nome ◦ Imagem ◦ Detalhes
  123. Trabalho para casa • Carregar num dos itens da RecyclerView

    ◦ Abre a vista de detalhes para aquele cão
  124. Dúvidas?

  125. Continuamos a responder no discord

  126. Obrigado ‍♀

  127. Android Training Program PORTUGAL Aula #6 Jetpack, jetpack, jetpack Próxima

    aula: 25 de Novembro