127
CRIANDO APP ANDROID UTILIZANDO KOTLIN Luiz Santana / André Cardoso

Criando app Android utilizando Kotlin

Embed Size (px)

Citation preview

CRIANDO APP ANDROID UTILIZANDO KOTLIN

Luiz Santana / André Cardoso

QUAL É O PROBLEMA?

- Java 8 no Android N

- Streams - API level 23 ou RxJava

- Lambdas, referências à métodos, non-capturing - API level 23 ou Retrolambda

- Try with resources - API level 19 ou Retrolambda

- Métodos default em interfaces - API level 23

Android Moderninho

- Não dá pra adicionar métodos à tipos existentes - *Util em todo lugar

- Nulls - NPEs em todo lugar

- Java é verboso

- APIs do Android são complexas - Herança e nulls em todo lugar

Limitações do Java no Android

KOTLIN?

- JetBrains

- JVM

- Concisa, Segura, Versátil, Interoperável

package hellofun main(args: Array<String>) { println("Hello TDC!") }

fun welcome(name: String, event: String): String { return "Hello ${name}, welcome to ${event}!" }

Functions

fun welcome(name: String, event: String) = "Hello ${name}, welcome to ${event}!"

Functions

fun main(args: Array<String>) { val greeting = welcome("André", "TDC") println(greeting)}

Functions

fun main(args: Array<String>) { val greeting = welcome("André", "TDC") println(greeting) greeting = welcome("Luiz", "TDC") println(greeting)}

Variáveis

fun main(args: Array<String>) { var greeting = welcome("André", "TDC") println(greeting) greeting = welcome("Luiz", "TDC") println(greeting)}

Variáveis

fun welcome(name: String, event: String = "TDC") = "Hello ${name}, welcome to ${event}!"

Parâmetros Default

fun main(args: Array<String>) { val greeting = welcome("André") println(greeting)}

Parâmetros Default

fun main(args: Array<String>) { val greeting = welcome(name = "Luiz", event = "I/O") println(greeting)}

Parâmetros Default

fun welcome(name: String = "Developer", event: String = "TDC") = "Hello ${name}, welcome to ${event}!"

Parâmetros Default

fun main(args: Array<String>) { val greeting = welcome(event = “Android Meetup") println(greeting)}

Parâmetros Default

fun main(args: Array<String>) { val greeting = welcome() println(greeting)}

Parâmetros Default

val lyrics = """ |Jamais a natureza |Reuniu tanta beleza |Jamais algum poeta |Teve tanto pra cantar! “"".trimMargin() println(lyrics) val month = "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)"var datePattern = """\d{2} ${month} \d{4}"""val today = "13 MAY 2016".matches(Regex(datePattern))println(today)

String Templates

val famousPeople = arrayListOf<String>("Einstein", "Newton", "MC Biel") val forSure = famousPeople is java.util.ArrayListprintln(forSure)

Collections

val famousPeople = arrayListOf<String>("Einstein", "Newton", "MC Bin Laden") val pairs = famousPeople.map { Pair(it, it == "MC Bin Laden") } pairs.forEach { val (name, singer) = it val verb = if (singer) "is" else "is not" println("${name} ${verb} a singer") }

Lambdas

val pairs = famousPeople.map { Pair(it, it == "MC Bin Laden") }

Equality

pairs.forEach { val (name, singer) = it val verb = if (singer) "is" else "is not" println("${name} ${verb} a singer") }

Destructors e if expressions

fun String.isSinger() = this == "MC Bin Laden"

Extension Functions

val famousPeople = arrayListOf<String>("Einstein", "Newton", "MC Bin Laden") famousPeople.forEach { val verb = if (it.isSinger()) "is" else "is not" println("${it} ${verb} a singer") }

Extension Functions

fun SQLiteDatabase.transaction(code: SQLiteDatabase.() -> Unit) { try { beginTransaction() code() setTransactionSuccessful() } catch (e: TransactionAbortException) { // Do nothing, just stop the transaction } finally { endTransaction() }} fun SQLiteDatabase.delete(tableName: String, whereClause: String = "", vararg args: Pair<String, Any>): Int { …}

Extension Functions

db.transaction { db.delete("people", "is_singer = ?", "is_singer" to true) }

Extension Functions

fun sendEmailToClient(client: Client?, message: String?, mailer: Mailer) { val email = client?.personalInfo?.email ?: "[email protected]" if (message != null) { mailer.sendMessage(email, message) }}

Tipos Null

fun sendEmailToClient(client: Client?, message: String?, mailer: Mailer) { val email = client?.personalInfo?.email ?: "[email protected]" if (message != null) { mailer.sendMessage(email, message) }}

Null Safe Calls

fun sendEmailToClient(client: Client?, message: String?, mailer: Mailer) { val email = client?.personalInfo?.email ?: "[email protected]" if (message != null) { mailer.sendMessage(email, message) }}

Elvis

fun sendEmailToClient(client: Client?, message: String?, mailer: Mailer) { val email = client?.personalInfo?.email ?: "[email protected]" if (message != null) { mailer.sendMessage(email, message) }}

Smart Cast

fun eval(expr: Expr): Int = when (expr) { is Num -> expr.value is Sum -> eval(expr.left) + eval(expr.right) else -> throw IllegalArgumentException("Unknown expression") }interface Exprclass Num(val value: Int) : Exprclass Sum(val left: Expr, val right: Expr) : Expr

Smart Cast

class Person constructor(name: String) {} class Singer(name: String, isSinger: Boolean) : Person(name) {}

Classes

open class Person constructor(name: String) {} class Singer(name: String, isSinger: Boolean) : Person(name) {}

Classes

open class Person constructor(name: String) { open fun walk() { println("walking as a normal person") }} class Singer(name: String, isSinger: Boolean) : Person(name) { override fun walk() { println("walking with some style") }}

Classes

interface Funk { var views: Long fun funk() { views++ println("ta tranquilo ta favorável") }}

class Singer(name: String, isSinger: Boolean, override var views: Long) : Funk

Interfaces

fun main(args: Array<String>) { val mc = Singer("MC Bin Laden", true) mc.funk() mc.walk()}

Interfaces

interface Moveable { fun move() { print("move") }} interface Walkable { fun move() { walk() } fun walk() { print("walk") }} class Human : Moveable, Walkable { override fun move() { super<Walkable>.move() }}

Interfaces - Resolução de conflitos

data class Singer(val name: String, val isSinger: Boolean, override var views: Long) : Funk

fun main(args: Array<String>) { val mc = Singer("MC Bin Laden", isSinger = true, views = 10000000) println(mc.toString()) println(mc.hashCode()) println(mc.copy(name = "Catra")) }

Singer(name=MC Bin Laden, isSinger=true, views=10000001) -117390027 Singer(name=Catra, isSinger=true, views=10000001)

Data class

class Panelist { val presentation : String by lazy { "Kotlin!!!!" } var numberOfQuestions : Int by Delegates.observable(0) { prop, old, new -> println("It was ${old} not it is ${new}") } }

Delegated properties

CRIANDO O APP

CRIANDO O APP | OBJETIVO

- Criar um app em Kotlin

- Consumir dados MOCK, simulando “News feed” do Reddit

CRIANDO O APP | PRIMEIRO PASSO

- Criar projeto com Activity básica

- Instalando Plugin: - Preferences - Plugins - Buscar por “Kotlin”

CRIANDO O APP | PLUGIN

CRIANDO O APP | CONFIGURANDO

- Abrir painel de ações - ctrl + shift + A - cmd + shift + A

O QUE MUDOU?

apply plugin: 'com.android.application'apply plugin: 'kotlin-android'android { … sourceSets { main.java.srcDirs += 'src/main/kotlin' }}dependencies {

… compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"}buildscript { ext.kotlin_version = '1.0.1-2' repositories { mavenCentral() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }}

build.gradle

JAVA -> KOTLIN

public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new OnClickListener(…) {

}); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return true; }

}

MainActivity.java

Java -> Kotlin | CONVERTENDO

- Convertendo código - Abrir painel ação - Buscar por “Convert Java…”

E A MÁGICA ACONTECE…

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar = findViewById(R.id.toolbar) as Toolbar? setSupportActionBar(toolbar) val fab = findViewById(R.id.fab) as FloatingActionButton? fab?.setOnClickListener({ view -> Snackbar.make(view, "Replace with your own

action", Snackbar.LENGTH_LONG).setAction("Action", null).show() }) } override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_main, menu) return true }

…}

MainActivity.kt

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)

{ super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)

MainActivity.kt

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)

{ super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)

MainActivity.kt

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)

{ super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)

MainActivity.kt

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)

{ super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)

MainActivity.kt

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)

{ super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)

MainActivity.kt

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar = findViewById(R.id.toolbar) as Toolbar? setSupportActionBar(toolbar) val fab = findViewById(R.id.fab) as FloatingActionButton? fab?.setOnClickListener({ view -> Snackbar.make(view, "Replace with your own

action", Snackbar.LENGTH_LONG).setAction("Action", null).show() }) } override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_main, menu) return true }

…}

MainActivity.kt

class MainActivity : AppCompatActivity() { … val toolbar = findViewById(R.id.toolbar) as Toolbar? setSupportActionBar(toolbar) val fab = findViewById(R.id.fab) as FloatingActionButton? fab?.setOnClickListener( { view -> Snackbar.make(view, "Replace with your own

action", Snackbar.LENGTH_LONG).setAction("Action", null).show() }) }

…}

MainActivity.kt

class MainActivity : AppCompatActivity() { … val toolbar = findViewById(R.id.toolbar) as Toolbar? setSupportActionBar(toolbar) val fab = findViewById(R.id.fab) as FloatingActionButton? fab?.setOnClickListener( { view -> Snackbar.make(view, "Replace with your own

action", Snackbar.LENGTH_LONG).setAction("Action", null).show() }) }

…}

MainActivity.kt

class MainActivity : AppCompatActivity() { … val toolbar = findViewById(R.id.toolbar) as Toolbar? setSupportActionBar(toolbar) val fab = findViewById(R.id.fab) as FloatingActionButton? fab?.setOnClickListener( { view -> Snackbar.make(view, "Replace with your own

action", Snackbar.LENGTH_LONG).setAction("Action", null).show() }) }

…}

MainActivity.kt

class MainActivity : AppCompatActivity() { … val toolbar = findViewById(R.id.toolbar) as Toolbar? setSupportActionBar(toolbar) val fab = findViewById(R.id.fab) as FloatingActionButton? fab?.setOnClickListener( { view -> Snackbar.make(view, "Replace with your own

action", Snackbar.LENGTH_LONG).setAction("Action", null).show() }) }

…}

MainActivity.kt

class MainActivity : AppCompatActivity() { … val toolbar = findViewById(R.id.toolbar) as Toolbar? setSupportActionBar(toolbar) val fab = findViewById(R.id.fab) as FloatingActionButton? fab?.setOnClickListener( { view -> Snackbar.make(view, "Replace with your own

action", Snackbar.LENGTH_LONG).setAction("Action", null).show() }) }

…}

MainActivity.kt

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar = findViewById(R.id.toolbar) as Toolbar? setSupportActionBar(toolbar) val fab = findViewById(R.id.fab) as FloatingActionButton? fab?.setOnClickListener({ view -> Snackbar.make(view, "Replace with your own

action", Snackbar.LENGTH_LONG).setAction("Action", null).show() }) } override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_main, menu) return true }

…}

MainActivity.kt

Criando o App | APP RODANDO

CRIANDO UM FRAGMENT

FRAGMENT | MUDANÇAS

- Adicionado Fragment Management - Removido Float Button - Removido Option Menu

FRAGMENT | LAYOUT

- Criando um layout Simples<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android=“….” android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/news_list" android:layout_width="wrap_content" android:layout_height="wrap_content"> </android.support.v7.widget.RecyclerView>

</RelativeLayout>

FRAGMENT | KOTLIN CODE

class NewsFragment : Fragment() {

private var newsList: RecyclerView? = null override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater?.inflate(R.layout.fragment_news, container, false) newsList = view?.findViewById(R.id.news_list) as RecyclerView? newsList?.setHasFixedSize(true) newsList?.layoutManager = LinearLayoutManager(context) return view }}

FRAGMENT | KOTLIN CODE

class NewsFragment : Fragment() {

private var newsList: RecyclerView? = null override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater?.inflate(R.layout.fragment_news, container, false) newsList = view?.findViewById(R.id.news_list) as RecyclerView? newsList?.setHasFixedSize(true) newsList?.layoutManager = LinearLayoutManager(context) return view }}

Como melhoraro código?

EXTENSION FUNCTIONS

EXTENSION FUNCTIONS | INFLATE

Como fazer o código abaixo ficar mais intuitivo?

… val view = inflater?.inflate(R.layout.fragment_news, container, false) …

… val view = container?.inflate(R.layout.fragment_news) …

EXTENSION FUNCTIONS | INFLATE

Como implementar?

// ExtensionsKt.ktfun ViewGroup.inflate(layoutId : Int) : View { return LayoutInflater.from(context).inflate(layoutId, this, false)}

EXTENSION FUNCTIONS | INFLATE

Reuso do código

// JavaExtensionsKt.inflate(container, R.layout.news_fragment);

// Kotlin container?.inflate(R.layout.fragment_news)

@file:JvmName("ExtensionsUtils")package com.arctouch.kotlin.commons.extensions…// Usar assim em Java:

ExtensionsUtils.inflate(container, R.layout.news_fragment);

EXTENSION FUNCTIONS | INFLATE

Parâmetros com valor padrão

fun ViewGroup.inflate(layoutId : Int, attachToRoot : Boolean = false) : View { return LayoutInflater.from(context).inflate(layoutId, this, attachToRoot)}// usar assim

val view = container?.inflate(R.layout.fragment_news) // default: false

// ou assimval view = container?.inflate(R.layout.fragment_news, true)

ANDROID EXTENSIONS

ANDROID EXTENSIONS | ADICIONANDO

apply plugin: 'com.android.application'apply plugin: 'kotlin-android'apply plugin: 'kotlin-android-extensions'

Adicionar no build.gradle

ANDROID EXTENSIONS | FUNCIONALIDADE

- Voltando ao Layout do Fragment…<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android=“….” android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/news_list" android:layout_width="wrap_content" android:layout_height="wrap_content"> </android.support.v7.widget.RecyclerView>

</RelativeLayout>

ANDROID EXTENSIONS | FUNCIONALIDADE

// importar no código

// import kotlinx.android.synthetic.<MODULO>.<VIEW>.*

import kotlinx.android.synthetic.main.fragment_news.*

// código antigonewsList = view?.findViewById(R.id.news_list) as RecyclerView? newsList?.setHasFixedSize(true)newsList?.layoutManager = LinearLayoutManager(context)// código novonews_list.setHasFixedSize(true)news_list.layoutManager = LinearLayoutManager(context)

LAZY PROPERTIES

LAZY PROPERTIES | INTRO

private val newsList by lazy { news_list}

override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) newsList.setHasFixedSize(true) // Executado de maneira Lazy newsList.layoutManager = LinearLayoutManager(context) }}

CRIANDO ADAPTER

CRIANDO ADAPTER | INICIO

- Usaremos padrão DelegateAdapter - A ideia é ter diferentes tipos de view

num mesmo adapter - Neste caso teremos algo parecido com:

CRIANDO ADAPTER | ESTRUTURA

- Cada ViewItem possui um ViewType

enum class ViewType { LOADING, NEWS}

interface ViewDelegate { fun getType() : ViewType}

CRIANDO ADAPTER | O ADAPTER

- Nosso NewsAdapter class NewsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val items: ArrayList<ViewItem> = ArrayList()

// outros métodos…}

CRIANDO ADAPTER | O ADAPTER

- Nosso NewsAdapter class NewsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val items: ArrayList<ViewItem> = ArrayList() private val delegateAdapters: HashMap<ViewType, ViewTypeDelegateAdapter> = HashMap()

// outros métodos…}

CRIANDO ADAPTER | O ADAPTER

- Nosso NewsAdapter class NewsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val items: ArrayList<ViewItem> = ArrayList() private val delegateAdapters: HashMap<ViewType, ViewTypeDelegateAdapter>

= HashMap() init { delegateAdapters.put(ViewType.LOADING, LoadingDelegateAdapter()) }

// outros métodos… }

CRIANDO ADAPTER | O ADAPTER

- Nosso NewsAdapter class NewsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { private val items: ArrayList<ViewItem> = ArrayList() private val delegateAdapters: HashMap<ViewType, ViewTypeDelegateAdapter> = HashMap() private val loadingItem = object : ViewItem { override fun getType() : ViewType = ViewType.LOADING } init { delegateAdapters.put(ViewType.LOADING, LoadingDelegateAdapter()) items.add(loadingItem) }

// outros métodos…}

DATA CLASSES

DATA CLASSES | MODEL

- Clássico Java model

public class RedditNewsItem { private String author; private String title; public RedditNewsItem(String author, String title) { this.author = author; this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; }}

DATA CLASSES | MODEL

- Kotlin data class

data class RedditNewsItem(var author: String, var title: String)

Java Model + equals/hashCode toString copy

DELEGATE ADAPTERS

DELEGATE ADAPTERS | ESTRUTURA

- Criando estrutura inicial

interface ViewTypeDelegateAdapter { fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewType)}

DELEGATE ADAPTERS | LOADING

LoadingDelegateAdapter.kt

class LoadingDelegateAdapter : ViewTypeDelegateAdapter { override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder = SpinnerViewHolder(parent) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewType) { } class SpinnerViewHolder(parent : ViewGroup) : RecyclerView.ViewHolder (parent.inflate(R.layout.news_item_loading)) { // infla layout em cima do pai }}

DELEGATE ADAPTERS | LOADING

LoadingDelegateAdapter.kt

class LoadingDelegateAdapter : ViewTypeDelegateAdapter { override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder = SpinnerViewHolder(parent) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewType) { } class SpinnerViewHolder(parent : ViewGroup) : RecyclerView.ViewHolder (parent.inflate(R.layout.news_item_loading)) { // infla layout em cima do pai }}

DELEGATE ADAPTERS | LOADING

LoadingDelegateAdapter.kt

class LoadingDelegateAdapter : ViewTypeDelegateAdapter { override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder = SpinnerViewHolder(parent) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewType) { } class SpinnerViewHolder(parent : ViewGroup) : RecyclerView.ViewHolder (parent.inflate(R.layout.news_item_loading)) { // infla layout em cima do pai }}

DELEGATE ADAPTERS | LOADING

LoadingDelegateAdapter.kt

class LoadingDelegateAdapter : ViewTypeDelegateAdapter { override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder = SpinnerViewHolder(parent) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewType) { } class SpinnerViewHolder(parent : ViewGroup) : RecyclerView.ViewHolder (parent.inflate(R.layout.news_item_loading)) { // infla layout em cima do pai }}

DELEGATE ADAPTERS | LOADING

LoadingDelegateAdapter.kt

class LoadingDelegateAdapter : ViewTypeDelegateAdapter { override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder = SpinnerViewHolder(parent) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewType) { } class SpinnerViewHolder(parent : ViewGroup) : RecyclerView.ViewHolder (parent.inflate(R.layout.news_item_loading)) { // infla layout dentro do container pai }}

DELEGATE ADAPTERS | NEWS

NewsDelegateAdapter.kt

class NewsAdapterDelegate : ViewTypeDelegateAdapter { override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder = NewsViewHolder(parent) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewItem) { holder as NewsViewHolder holder.bind(item as RedditNewsItem) } class NewsViewHolder(parent : ViewGroup) : RecyclerView.ViewHolder (parent.inflate(R.layout.news_item_loading)) { fun bind(item : RedditNewsItem) = with(itemView) { img_thumbnail.loadImgUrl(item.thumbnail) description.text = item.title author.text = item.author comments.text = "${item.commentsNumber} comments" } }}

DELEGATE ADAPTERS | NEWS

NewsDelegateAdapter.kt

class NewsAdapterDelegate : ViewTypeDelegateAdapter { override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder = NewsViewHolder(parent) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewItem) { holder as NewsViewHolder holder.bind(item as RedditNewsItem) } class NewsViewHolder(parent : ViewGroup) : RecyclerView.ViewHolder (parent.inflate(R.layout.news_item_loading)) { fun bind(item : RedditNewsItem) = with(itemView) { img_thumbnail.loadImgUrl(item.thumbnail) description.text = item.title author.text = item.author comments.text = "${item.commentsNumber} comments" } }}

DELEGATE ADAPTERS | NEWS

NewsDelegateAdapter.kt

class NewsAdapterDelegate : ViewTypeDelegateAdapter { override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder = NewsViewHolder(parent) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewItem) { holder as NewsViewHolder holder.bind(item as RedditNewsItem) } class NewsViewHolder(parent : ViewGroup) : RecyclerView.ViewHolder (parent.inflate(R.layout.news_item_loading)) { fun bind(item : RedditNewsItem) = with(itemView) { img_thumbnail.loadImgUrl(item.thumbnail) description.text = item.title author.text = item.author comments.text = "${item.commentsNumber} comments" } }}

Smart cast

DELEGATE ADAPTERS | NEWS

NewsDelegateAdapter.kt

class NewsAdapterDelegate : ViewTypeDelegateAdapter { override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder = NewsViewHolder(parent) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewItem) { holder as NewsViewHolder holder.bind(item as RedditNewsItem) } class NewsViewHolder(parent : ViewGroup) : RecyclerView.ViewHolder (parent.inflate(R.layout.news_item_loading)) { fun bind(item : RedditNewsItem) = with(itemView) { img_thumbnail.loadImgUrl(item.thumbnail) description.text = item.title author.text = item.author comments.text = "${item.commentsNumber} comments" } }}

DELEGATE ADAPTERS | NEWS

- Carregando imagem no bind - Extension functions

fun ImageView.loadImgUrl(imageUrl: String?) { if (imageUrl.isNullOrEmpty()) { Picasso.with(context).load(R.mipmap.ic_launcher).into(this) } else { Picasso.with(context).load(imageUrl).into(this) }}

ADICIONANDO ALGUNS DADOS MOCK

ADICIONANDO ALGUNS DADOS MOCKclass NewsFragment : Fragment() { // … override fun onActivityCreated(savedInstanceState: Bundle?) { // … val newsAdapter = NewsAdapter() newsList.adapter = newsAdapter if (savedInstanceState == null) { newsAdapter.addNews(mockData()) } } private fun mockData() : List<RedditNewsItem> { val news = mutableListOf<RedditNewsItem>() for (i in 1..10) { news.add(RedditNewsItem( "Author $i", "Title $i", "http://lorempixel.com/200/200/technics/$i", // thumbnail i // number of comments )) } return news }}

ADICIONANDO ALGUNS DADOS MOCK

class NewsFragment : Fragment() { // … override fun onActivityCreated(savedInstanceState: Bundle?) { // … val newsAdapter = NewsAdapter() newsList.adapter = newsAdapter if (savedInstanceState == null) { newsAdapter.addNews(mockData()) } }}

ADICIONANDO ALGUNS DADOS MOCK

class NewsFragment : Fragment() { // … override fun onActivityCreated(savedInstanceState: Bundle?) { // … val newsAdapter = NewsAdapter() newsList.adapter = newsAdapter if (savedInstanceState == null) { newsAdapter.addNews(mockData()) } }}

ADICIONANDO ALGUNS DADOS MOCKclass NewsFragment : Fragment() { // … override fun onActivityCreated(savedInstanceState: Bundle?) { // … val newsAdapter = NewsAdapter() newsList.adapter = newsAdapter if (savedInstanceState == null) { newsAdapter.addNews(mockData()) } } private fun mockData() : List<RedditNewsItem> { val news = mutableListOf<RedditNewsItem>() for (i in 1..10) { news.add(RedditNewsItem( "Author $i", "Title $i", "http://lorempixel.com/200/200/technics/$i", // thumbnail i // number of comments )) } return news }}

ADICIONANDO ALGUNS DADOS MOCK

class NewsFragment : Fragment() { // … private fun mockData() : List<RedditNewsItem> { val news = mutableListOf<RedditNewsItem>() for (i in 1..10) { news.add(RedditNewsItem( "Author $i", "Title $i", "http://lorempixel.com/200/200/technics/$i", // thumbnail i // number of comments )) } return news }}

ADICIONANDO ALGUNS DADOS MOCK

class NewsFragment : Fragment() { // … private fun mockData() : List<RedditNewsItem> { val news = mutableListOf<RedditNewsItem>() for (i in 1..10) { news.add(RedditNewsItem( "Author $i", "Title $i", "http://lorempixel.com/200/200/technics/$i", // thumbnail i // number of comments )) } return news }}

ADICIONANDO ALGUNS DADOS MOCK

class NewsFragment : Fragment() { // … private fun mockData() : List<RedditNewsItem> { val news = mutableListOf<RedditNewsItem>() for (i in 1..10) { news.add(RedditNewsItem( "Author $i", "Title $i", "http://lorempixel.com/200/200/technics/$i", // thumbnail i // number of comments )) } return news }}

ADICIONANDO ALGUNS DADOS MOCK

OUTRAS DICAS

OUTRAS DICAS | CLICK LISTENERS

myButton.setOnClickListener { navigateToDetail() }

OUTRAS DICAS | OPTION MENU

override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {

R.id.action_settings -> consume { navigateToSettings() }

R.id.nav_camera -> drawer.consume { navigateToCamera() }

R.id.nav_gallery -> drawer.consume { loadGallery() }

R.id.nav_slideshow -> drawer.consume { loadSlideshow() }

else -> super.onOptionsItemSelected(item)

}

OUTRAS DICAS | OPTION MENU

inline fun consume(f: () -> Unit): Boolean {

f()

return true

}

inline fun DrawerLayout.consume(f: () -> Unit): Boolean {

f()

closeDrawers()

return true

}

OUTRAS DICAS | LAMBDAS

view.postDelayed({ doWhatever() }, 200)

Thread().run {

// Running in a thread

}

OUTRAS DICAS | COLLECTIONS

return parsedContacts.filter { it.name != null && it.image != null }

.sortedBy { it.name }

.map { Contact(it.id, it.name!!, it.image!!) }

OUTRAS DICAS | ANKO

- DSL - Useful Helpers

OUTRAS DICAS | ANKO - DSL

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)

verticalLayout { padding = dip(30) editText { hint = "Name" textSize = 24f } editText { hint = "Password" textSize = 24f } button("Login") { textSize = 26f // usando tamanho em SP } } }

OUTRAS DICAS | ANKO

- DSL - Useful Helpers

OUTRAS DICAS | ANKO - Useful Helpers

- Fazer ligação makeCall(numeroTelefone)

- Abrir browser browse(url)- Compartilha texto share(text, [assunto])- Enviar email email(email, [assunto], [texto])- Abrir activity startActivity<SomeOtherActivity>("id" to 5)

OUTRAS DICAS | ANKO - Useful Helpers

async() {

// Faz algo em background

uiThread {

// Volta para main thread

}

}

LINKS

Kotlin Lang: - https://kotlinlang.org/

Kotlin Roadmap - http://go.arctouch.com/kotlin-roadmap

https://github.com/arctouch-luizsantana/tdc-kotlin

OBRIGADO.

Big Brains Wanted

Join our team! Go to arctouch.com/brjobs

Visit our booth to win an Apple Watch.