Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

● 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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

● 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

Slide 5

Slide 5 text

#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 ✅

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

http://bit.ly/atp2020-live

Slide 9

Slide 9 text

http://bit.ly/atp2020-codelabs

Slide 10

Slide 10 text

http://bit.ly/kahoot-aula5

Slide 11

Slide 11 text

Resumo da Aula #4

Slide 12

Slide 12 text

LiveData ViewModel Room

Slide 13

Slide 13 text

● 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

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

● 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 ...

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

● 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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Aula #5

Slide 22

Slide 22 text

RecyclerView

Slide 23

Slide 23 text

● É 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

Slide 24

Slide 24 text

RecyclerView Activity RecyclerView LayoutManager Adapter ViewHolder data RecyclerView LayoutManager Adapter ViewHolder data Adapter ViewHolder data

Slide 25

Slide 25 text

RecyclerView Activity RecyclerView LayoutManager Adapter ViewHolder data RecyclerView LayoutManager Adapter ViewHolder data Adapter ViewHolder data

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Como utilizar? Definir a RecyclerView no layout... res/layout/activity_main.xml

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

... Como utilizar? … e como cada item deve ser

Slide 32

Slide 32 text

RecyclerView RecyclerView LayoutManager Adapter ViewHolder data Activity RecyclerView LayoutManager Adapter ViewHolder data Adapter ViewHolder data

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

RecyclerView Activity RecyclerView LayoutManager Adapter ViewHolder data RecyclerView LayoutManager Adapter ViewHolder data Adapter ViewHolder data

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

MainActivity.kt

Slide 47

Slide 47 text

Retrofit

Slide 48

Slide 48 text

● 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

Slide 49

Slide 49 text

O Retrofit transforma uma API HTTP numa interface

Slide 50

Slide 50 text

The Dog API

Slide 51

Slide 51 text

The Dog API

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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 )

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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 )

Slide 58

Slide 58 text

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 )

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

interface DogApiClient { @GET("breeds") fun getBreeds() : Call> } Como utilizar? A interface DogApiClient.kt Permite realizar trabalho em background

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

Glide

Slide 74

Slide 74 text

● 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

Slide 75

Slide 75 text

● 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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

● 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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

// 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)

Slide 87

Slide 87 text

Paging

Slide 88

Slide 88 text

● 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

Slide 89

Slide 89 text

Paging 3 Repository PagingSource RemoteMediator ViewModel Pager PagingDataAdapter Flow

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

Passaporte para a Google

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

VS Ronda 4

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

Sexta-Feira negra Photo by Xiaolong Wong on Unsplash

Slide 114

Slide 114 text

Aplicar uma máscara sobre uma view ⚪

Slide 115

Slide 115 text

Aplicar uma máscara sobre uma view ⚪

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

Trabalho Para Casa ‍‍

Slide 122

Slide 122 text

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

Slide 123

Slide 123 text

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

Slide 124

Slide 124 text

Dúvidas?

Slide 125

Slide 125 text

Continuamos a responder no discord

Slide 126

Slide 126 text

Obrigado ‍♀

Slide 127

Slide 127 text

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