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

Android Training Program - Portugal, Aula 5

Avatar for ATP Portugal 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

Avatar for ATP Portugal

ATP Portugal

November 18, 2020
Tweet

More Decks by ATP Portugal

Other Decks in Education

Transcript

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

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

    aula anterior ‱ RecyclerView ‱ Retrofit ‱ Glide ‱ Paging ‱ Kotlin para principiantes ‱ Sexta-Feira negra
  6. ‱ 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
  7. ‱ 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 ...
  8. atualizar atualizar atualizar atualizar desenha o ecrĂŁ desenha o ecrĂŁ

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

    ‱ JobScheduler ‱ DownloadManager ‱ AlarmManager ‱ Coroutines OperaçÔes assĂ­ncronas SoluçÔes
  11. 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
  12. 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
  13. ‱ É 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
  14. class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } Como utilizar? Criar uma Activity/Fragment MainActivity.kt
  15. 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
  16. <?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
  17. <?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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. ‱ 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
  31. 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 )
  32. Resultado da API "height": { "imperial": "9 - 11.5", "metric":

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

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

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

    utilizar? A interface DogApiClient.kt Permite realizar trabalho em background
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. ‱ 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
  49. ‱ 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
  50. ‱ Permitir uma transição suave e rápido em qualquer tipo

    de lista de imagens ‱ Eficiente a carregar, redimensionar e descarregar imagens facilmente Objetivos
  51. ‱ 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
  52. // 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)
  53. ‱ 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
  54. 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
  55. 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
  56. 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
  57. class ListViewModel: ViewModel() { private val repository = Repository() val

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

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

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

    breedsListByPage = Pager(PagingConfig(pageSize = 20), pagingSourceFactory = { DogPagingSource(repository) }).flow } Como utilizar? ViewModel ListViewModel.kt
  61. 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
  62. 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
  63. 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
  64. 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
  65. 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
  66. 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
  67. 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
  68. 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
  69. object Singleton { } Singleton ☝ public class Singleton {

    private static Singleton instance; private Singleton(){} public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
  70. interface Kennel { fun getDog(name: String) } val kennel =

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

    object : Kennel { override fun getDog(name: String) {...} } val kennel = Kennel { name -> ...} SAM (Single Abstract Method)
  72. 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>
  73. 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" />
  74. Dividir o ecrĂŁ Para ativar: 1. BotĂŁo direito sobre a

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

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

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

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

    shift + ⬆ + https://developer.android.com/studio/intro/keyboard-shortcuts ⬇
  79. Trabalho para casa ‱ Implementar a vista de detalhes para

    um cão ◩ Nome ◩ Imagem ◩ Detalhes
  80. Trabalho para casa ‱ Carregar num dos itens da RecyclerView

    ◩ Abre a vista de detalhes para aquele cão