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 hello



fun 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.ArrayList

println(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 ?: "someone@isgoingtoreadit.com"

if (message != null) {

mailer.sendMessage(email, message)

}

}
Tipos Null
fun sendEmailToClient(client: Client?, message: String?, mailer: Mailer) {

val email = client?.personalInfo?.email ?: "someone@isgoingtoreadit.com"

if (message != null) {

mailer.sendMessage(email, message)

}

}
Null Safe Calls
fun sendEmailToClient(client: Client?, message: String?, mailer: Mailer) {

val email = client?.personalInfo?.email ?: "someone@isgoingtoreadit.com"

if (message != null) {

mailer.sendMessage(email, message)

}

}
Elvis
fun sendEmailToClient(client: Client?, message: String?, mailer: Mailer) {

val email = client?.personalInfo?.email ?: "someone@isgoingtoreadit.com"

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 Expr

class Num(val value: Int) : Expr

class 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 melhorar

o 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.kt



fun ViewGroup.inflate(layoutId : Int) : View {

return LayoutInflater.from(context).inflate(layoutId, this, false)

}
EXTENSION FUNCTIONS | INFLATE
Reuso do código
// Java

ExtensionsKt.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 assim

val 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 antigo

newsList = view?.findViewById(R.id.news_list) as RecyclerView?

newsList?.setHasFixedSize(true)

newsList?.layoutManager = LinearLayoutManager(context)



// código novo

news_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 MOCK
class 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 MOCK
class 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.

Criando app Android utilizando Kotlin

  • 1.
    CRIANDO APP ANDROIDUTILIZANDO KOTLIN Luiz Santana / André Cardoso
  • 2.
    QUAL É OPROBLEMA?
  • 3.
    - Java 8no 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
  • 4.
    - 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
  • 5.
  • 6.
    - JetBrains - JVM -Concisa, Segura, Versátil, Interoperável
  • 7.
    package hello
 
 fun main(args:Array<String>) {
 println("Hello TDC!")
 }
  • 8.
    fun welcome(name: String,event: String): String {
 return "Hello ${name}, welcome to ${event}!"
 } Functions
  • 9.
    fun welcome(name: String,event: String) = "Hello ${name}, welcome to ${event}!" Functions
  • 10.
    fun main(args: Array<String>){
 val greeting = welcome("André", "TDC")
 println(greeting)
 } Functions
  • 11.
    fun main(args: Array<String>){
 val greeting = welcome("André", "TDC")
 println(greeting)
 
 greeting = welcome("Luiz", "TDC")
 println(greeting)
 } Variáveis
  • 12.
    fun main(args: Array<String>){
 var greeting = welcome("André", "TDC")
 println(greeting)
 
 greeting = welcome("Luiz", "TDC")
 println(greeting)
 } Variáveis
  • 13.
    fun welcome(name: String,event: String = "TDC")
 = "Hello ${name}, welcome to ${event}!"
 
 Parâmetros Default
  • 14.
    fun main(args: Array<String>){
 val greeting = welcome("André")
 println(greeting)
 } Parâmetros Default
  • 15.
    fun main(args: Array<String>){
 val greeting = welcome(name = "Luiz", event = "I/O")
 println(greeting)
 } Parâmetros Default
  • 16.
    fun welcome(name: String= "Developer", event: String = "TDC")
 = "Hello ${name}, welcome to ${event}!"
 
 Parâmetros Default
  • 17.
    fun main(args: Array<String>){
 val greeting = welcome(event = “Android Meetup")
 println(greeting)
 } Parâmetros Default
  • 18.
    fun main(args: Array<String>){
 val greeting = welcome()
 println(greeting)
 } Parâmetros Default
  • 19.
    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
  • 20.
    val famousPeople =arrayListOf<String>("Einstein", "Newton", "MC Biel")
 
 val forSure = famousPeople is java.util.ArrayList
 println(forSure) Collections
  • 21.
    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
  • 22.
    val pairs =famousPeople.map { Pair(it, it == "MC Bin Laden") } Equality
  • 23.
    pairs.forEach {
 val (name,singer) = it
 val verb = if (singer) "is" else "is not"
 println("${name} ${verb} a singer")
 } Destructors e if expressions
  • 24.
    fun String.isSinger() =this == "MC Bin Laden" Extension Functions
  • 25.
    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
  • 26.
    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
  • 27.
    db.transaction {
 db.delete("people", "is_singer= ?", "is_singer" to true)
 } Extension Functions
  • 28.
    fun sendEmailToClient(client: Client?,message: String?, mailer: Mailer) {
 val email = client?.personalInfo?.email ?: "someone@isgoingtoreadit.com"
 if (message != null) {
 mailer.sendMessage(email, message)
 }
 } Tipos Null
  • 29.
    fun sendEmailToClient(client: Client?,message: String?, mailer: Mailer) {
 val email = client?.personalInfo?.email ?: "someone@isgoingtoreadit.com"
 if (message != null) {
 mailer.sendMessage(email, message)
 }
 } Null Safe Calls
  • 30.
    fun sendEmailToClient(client: Client?,message: String?, mailer: Mailer) {
 val email = client?.personalInfo?.email ?: "someone@isgoingtoreadit.com"
 if (message != null) {
 mailer.sendMessage(email, message)
 }
 } Elvis
  • 31.
    fun sendEmailToClient(client: Client?,message: String?, mailer: Mailer) {
 val email = client?.personalInfo?.email ?: "someone@isgoingtoreadit.com"
 if (message != null) {
 mailer.sendMessage(email, message)
 }
 } Smart Cast
  • 32.
    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 Expr
 class Num(val value: Int) : Expr
 class Sum(val left: Expr, val right: Expr) : Expr Smart Cast
  • 33.
    class Person constructor(name:String) {
 }
 
 class Singer(name: String, isSinger: Boolean) : Person(name) {
 } Classes
  • 34.
    open class Personconstructor(name: String) {
 }
 
 class Singer(name: String, isSinger: Boolean) : Person(name) {
 } Classes
  • 35.
    open class Personconstructor(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
  • 36.
    interface Funk {
 varviews: Long
 
 fun funk() {
 views++
 println("ta tranquilo ta favorável")
 }
 } class Singer(name: String, isSinger: Boolean, override var views: Long) : Funk Interfaces
  • 37.
    fun main(args: Array<String>){
 val mc = Singer("MC Bin Laden", true)
 mc.funk()
 mc.walk()
 } Interfaces
  • 38.
    interface Moveable {
 funmove() { 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
  • 39.
    data class Singer(valname: 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
  • 40.
    class Panelist {
 valpresentation : String by lazy {
 "Kotlin!!!!"
 }
 
 var numberOfQuestions : Int by Delegates.observable(0) {
 prop, old, new ->
 println("It was ${old} not it is ${new}")
 }
 } Delegated properties
  • 42.
  • 43.
    CRIANDO O APP| OBJETIVO - Criar um app em Kotlin - Consumir dados MOCK, simulando “News feed” do Reddit
  • 44.
    CRIANDO O APP| PRIMEIRO PASSO - Criar projeto com Activity básica
  • 45.
    - Instalando Plugin: -Preferences - Plugins - Buscar por “Kotlin” CRIANDO O APP | PLUGIN
  • 46.
    CRIANDO O APP| CONFIGURANDO - Abrir painel de ações - ctrl + shift + A - cmd + shift + A
  • 47.
  • 48.
    apply plugin: 'com.android.application'
 applyplugin: '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
  • 49.
  • 50.
    public class MainActivityextends 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
  • 51.
    Java -> Kotlin| CONVERTENDO - Convertendo código - Abrir painel ação - Buscar por “Convert Java…”
  • 52.
    E A MÁGICAACONTECE…
  • 53.
    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
  • 54.
    class MainActivity :AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_main) …
 MainActivity.kt
  • 55.
    class MainActivity :AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_main) …
 MainActivity.kt
  • 56.
    class MainActivity :AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_main) …
 MainActivity.kt
  • 57.
    class MainActivity :AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_main) …
 MainActivity.kt
  • 58.
    class MainActivity :AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_main) …
 MainActivity.kt
  • 59.
    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
  • 60.
    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
  • 61.
    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
  • 62.
    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
  • 63.
    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
  • 64.
    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
  • 65.
    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
  • 66.
    Criando o App| APP RODANDO
  • 67.
  • 68.
    FRAGMENT | MUDANÇAS -Adicionado Fragment Management - Removido Float Button - Removido Option Menu
  • 69.
    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>
  • 70.
    FRAGMENT | KOTLINCODE 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
 }
 }
  • 71.
    FRAGMENT | KOTLINCODE 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 melhorar
 o código?
  • 72.
  • 73.
    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) 
 …
  • 74.
    EXTENSION FUNCTIONS |INFLATE Como implementar? // ExtensionsKt.kt
 
 fun ViewGroup.inflate(layoutId : Int) : View {
 return LayoutInflater.from(context).inflate(layoutId, this, false)
 }
  • 75.
    EXTENSION FUNCTIONS |INFLATE Reuso do código // Java
 ExtensionsKt.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);
  • 76.
    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 assim
 val view = container?.inflate(R.layout.fragment_news, true)
  • 77.
  • 78.
    ANDROID EXTENSIONS |ADICIONANDO apply plugin: 'com.android.application'
 apply plugin: 'kotlin-android'
 apply plugin: 'kotlin-android-extensions' Adicionar no build.gradle
  • 79.
    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>
  • 80.
    ANDROID EXTENSIONS |FUNCIONALIDADE // importar no código // import kotlinx.android.synthetic.<MODULO>.<VIEW>.* import kotlinx.android.synthetic.main.fragment_news.* // código antigo
 newsList = view?.findViewById(R.id.news_list) as RecyclerView?
 newsList?.setHasFixedSize(true)
 newsList?.layoutManager = LinearLayoutManager(context)
 
 // código novo
 news_list.setHasFixedSize(true)
 news_list.layoutManager = LinearLayoutManager(context)
  • 81.
  • 82.
    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)
 }
 }
  • 83.
  • 84.
    CRIANDO ADAPTER |INICIO - Usaremos padrão DelegateAdapter - A ideia é ter diferentes tipos de view num mesmo adapter - Neste caso teremos algo parecido com:
  • 85.
    CRIANDO ADAPTER |ESTRUTURA - Cada ViewItem possui um ViewType enum class ViewType {
 LOADING,
 NEWS
 } interface ViewDelegate {
 fun getType() : ViewType
 }
  • 86.
    CRIANDO ADAPTER |O ADAPTER - Nosso NewsAdapter 
 class NewsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
 
 private val items: ArrayList<ViewItem> = ArrayList() // outros métodos…
 }
  • 87.
    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…
 }
  • 88.
    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…
 }
  • 89.
    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…
 }
  • 90.
  • 91.
    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;
 }
 }
  • 92.
    DATA CLASSES |MODEL - Kotlin data class data class RedditNewsItem(var author: String, var title: String) 
 Java Model + 
 equals/hashCode 
 toString
 copy

  • 93.
  • 94.
    DELEGATE ADAPTERS |ESTRUTURA - Criando estrutura inicial interface ViewTypeDelegateAdapter {
 fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder
 fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: ViewType)
 }
  • 95.
    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
 }
 }
  • 96.
    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
 }
 }
  • 97.
    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
 }
 }
  • 98.
    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
 }
 }
  • 99.
    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
 }
 }
  • 100.
    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"
 }
 }
 }
  • 101.
    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"
 }
 }
 }
  • 102.
    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
  • 103.
    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"
 }
 }
 }
  • 104.
    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)
 }
 }
  • 105.
  • 106.
    ADICIONANDO ALGUNS DADOSMOCK class 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
 }
 }
  • 107.
    ADICIONANDO ALGUNS DADOSMOCK class NewsFragment : Fragment() {
 // …
 override fun onActivityCreated(savedInstanceState: Bundle?) {
 // …
 val newsAdapter = NewsAdapter()
 newsList.adapter = newsAdapter
 
 if (savedInstanceState == null) {
 newsAdapter.addNews(mockData())
 }
 }
 }
  • 108.
    ADICIONANDO ALGUNS DADOSMOCK class NewsFragment : Fragment() {
 // …
 override fun onActivityCreated(savedInstanceState: Bundle?) {
 // …
 val newsAdapter = NewsAdapter()
 newsList.adapter = newsAdapter
 
 if (savedInstanceState == null) {
 newsAdapter.addNews(mockData())
 }
 }
 }
  • 109.
    ADICIONANDO ALGUNS DADOSMOCK class 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
 }
 }
  • 110.
    ADICIONANDO ALGUNS DADOSMOCK 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
 }
 }
  • 111.
    ADICIONANDO ALGUNS DADOSMOCK 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
 }
 }
  • 112.
    ADICIONANDO ALGUNS DADOSMOCK 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
 }
 }
  • 113.
  • 114.
  • 115.
    OUTRAS DICAS |CLICK LISTENERS myButton.setOnClickListener { navigateToDetail() }
  • 116.
    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) }
  • 117.
    OUTRAS DICAS |OPTION MENU inline fun consume(f: () -> Unit): Boolean { f() return true } inline fun DrawerLayout.consume(f: () -> Unit): Boolean { f() closeDrawers() return true }
  • 118.
    OUTRAS DICAS |LAMBDAS view.postDelayed({ doWhatever() }, 200)
 
 
 Thread().run { // Running in a thread }
  • 119.
    OUTRAS DICAS |COLLECTIONS return parsedContacts.filter { it.name != null && it.image != null } .sortedBy { it.name } .map { Contact(it.id, it.name!!, it.image!!) }
  • 120.
    OUTRAS DICAS |ANKO - DSL - Useful Helpers
  • 121.
    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 } } }
  • 122.
    OUTRAS DICAS |ANKO - DSL - Useful Helpers
  • 123.
    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)
  • 124.
    OUTRAS DICAS |ANKO - Useful Helpers async() { // Faz algo em background uiThread { // Volta para main thread } }
  • 125.
    LINKS Kotlin Lang: - https://kotlinlang.org/ KotlinRoadmap - http://go.arctouch.com/kotlin-roadmap
  • 126.
  • 127.
    OBRIGADO. Big Brains Wanted Joinour team! Go to arctouch.com/brjobs Visit our booth to win an Apple Watch.