ページ

ラベル kotlin の投稿を表示しています。 すべての投稿を表示
ラベル kotlin の投稿を表示しています。 すべての投稿を表示

2018年3月17日土曜日

RxKotlinとRxBindingでフォームの入力チェック


RxBinding

AndroidのUIコンポーネントをRx~でよろしく触れるようにしたもの

:computer:環境構築


今回はKotlinを使うのでRxBindingもKotlin用をインストール
app/build.gradle
    // RxKotlin
    compile "io.reactivex.rxjava2:rxkotlin:$rxkotlin_version"

    // RxBinding
    compile "com.jakewharton.rxbinding2:rxbinding-kotlin:$rxbinding_version"

:pencil: 実装


簡単なサンプル

よくある以下のようなフォーム画面を作成してみる。
  • 入力フィールドに正しい値が設定されている時のみSubmitボタンが押せるようにする
レイアウトはこんな感じ
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/profileToolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="@color/colorPrimary"
        app:theme="@style/ToolbarTheme"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="5dp"
        android:orientation="vertical">

        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            app:errorEnabled="true"
            app:hintEnabled="true"
            app:hintAnimationEnabled="true">

            <EditText
                android:id="@+id/profileName"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:maxLines="1"
                android:maxLength="20"
                android:inputType="text"
                android:hint="@string/hint_name" />

        </android.support.design.widget.TextInputLayout>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/hint_area"/>

        <Spinner
            android:id="@+id/spinnerArea"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dp"
            android:entries="@array/areas" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:paddingStart="50dp"
            android:paddingEnd="50dp"
            android:gravity="center">

            <Button
                android:id="@+id/profileSubmit"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/round_shape_disabled"
                android:enabled="false"
                android:text="@string/save"
                android:textColor="@android:color/white"/>

        </LinearLayout>

    </LinearLayout>
</LinearLayout>
image.png (43.9 kB)
名前とエリアを選択するフォーム画面
名前やエリアが未選択の場合Submitボタンが押せない状態にする
kotlin
        val nameChanges = profileName.textChanges()
        val areaChanges = spinnerArea.itemSelections()

        disposable += Observables.combineLatest(nameChanges, areaChanges)
                    {name, area -> name.isNotEmpty() && area != AdapterView.INVALID_POSITION}
                    .subscribe { isValid ->
                        profileSubmit.isEnabled = isValid
                        profileSubmit.setBackgroundResource(
                                if (isValid)
                                    R.drawable.round_shape_enabled
                                else
                                    R.drawable.round_shape_disabled)
                    }
名前とエリアのObservableをcombineLatestで結合し、
両方の値チェックでOKだった場合のみボタンを有効化しています。
name.isNotEmpty() && area != AdapterView.INVALID_POSITION
-> 名前が空でなく、spinnerの選択位置が空でない

オペレータ

  • distinctUntilChanged
    連続して重複したデータを通知しない
    kotlin
        disposable += Observable.fromArray(1, 2, 2, 3, 4, 5, 5, 6)
                .distinctUntilChanged()
                .subscribe(::println) // 1,2,3,4,5,6
    
  • combineLatest
    どちらか1つの送信される時、指定された関数によって、各Observableから送信される最新のアイテムを結合し、この関数の評価に基づいたアイテムを送信する
    kotlin
            val o1 = PublishSubject.create<Int>()
            val o2 = PublishSubject.create<String>()
    
            disposable += Observables.combineLatest(
                                    o1, o2
                                    ) { n, s -> "${n}/${s}"}
                                    .subscribe(::println)
            o1.onNext(1)
            o2.onNext("a")
            o1.onNext(2)
            o1.onNext(3)
            o2.onNext("b")
            // 1/a
            // 2/a
            // 3/a
            // 3/b
    

:bomb: バッドノウハウ


自分だけかもしれませんが、Observables.combineLatestObservable.combineLatest
(Observable(s)の違い)と書くとエラーになります。。

2017年12月22日金曜日

kotlin入門2


前回の続き

:computer:環境構築


今回はこちらの環境を使ってみる

:pencil: 実装


  • 数値型
種類ビット幅
Double浮動小数点数64
Float浮動小数点数32
Long整数64
Int整数32
Short整数16
Byte整数8
  • その他の基本型
種類
Boolean真偽値
Char文字
String文字列

色んなパターンの実装

  • レンジ
    kotlin
        val range = 0..10
        if (5 in range) {
            print("contains!")
        } // contains!
        range.toList() // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
  • when
    kotlin
        val x = 2
        val hoge = when(x) {
            99 -> "string ${x}"
            in 1..5 -> "in 1..5"
            else -> "else"
        }
        print(hoge) // in 1..5
    
  • ヒアドキュメント
    kotlin
        val text = """
          |Title
          | aaaaaa
          | bbbbbb
          |   ccccccc
        """.trimMargin()
        print(text)
    // Title
    //  aaaaaa
    //  bbbbbb
    //   ccccccc
    
  • toString()のオーバーライド
    kotlin
    class Sample(val a: Int, val b: Int) {
        override fun toString(): String = "${a} ${b}"
    }
    
    fun main(args: Array<String>) {
        val ref = Sample(1, 0)
        println(ref) // 1 0
    }
    
  • イニシャライザでパラメータチェック
    kotlin
    class Sample(val number: Int) {
        init {
            require(number != 0, {"number must not be 0"})
        }
    }
    
    fun main(args: Array<String>) {
        val ref = Sample(0)
        println(ref)
    }
    // Exception in thread "main" java.lang.IllegalArgumentException: number must not be 0
    

2017年12月17日日曜日

kotlin入門1

kotlin
絶滅しないようにkotlinの勉強を進めていこうかと思いますw
今回はkotlin自体の基本的なところを見てみます。

様々なプラットフォーム

  • Kotlin for Server Side
  • Kotlin for Android
  • Kotlin for JavaScript
    • KotlinをJavaScriptにコンパイルする
  • Kotlin/Native
    • Kotlinのコンパイラ LLVMを使っている様子
知らない間に色々ある。。。

:pencil: 基本実装


変数

kotlin
fun main(args: Array<String>) {
    val a: Int = 1  // Int型として変数宣言かつ値代入
    val b = 2   // `Int` 型を推論
    val c: Int  // 値代入しない場合は型は必須
    c = 3       // 後で値を設定
    println("a = $a, b = $b, c = $c") // a = 1, b = 2, c = 3
}

文字列テンプレート

kotlin
fun main(args: Array<String>) {
    var a = 1
    // 文字列中に`$a`で値を展開できる
    val s1 = "a is $a" 

    a = 2
    // 展開時に式も書ける
    val s2 = "${s1.replace("is", "was")}, but now is $a"
    println(s2) // a was 1, but now is 2
}

メソッドの書き方

  • 2つのInt型の引数をとって1つのInt型の戻り値を返す
    kotlin
    fun sum(a: Int, b: Int): Int {
        return a + b
    }
    
  • 上のはこんな感じでも書ける
    kotlin
    fun sum(a: Int, b: Int) = a + b
    
  • 戻り値が無い場合
    kotlin
    fun printSum(a: Int, b: Int): Unit {
        println("sum of $a and $b is ${a + b}")
    }
    
  • Unitは省略できる
    kotlin
    fun printSum(a: Int, b: Int) {
        println("sum of $a and $b is ${a + b}")
    }
    
  • if-expressions.
    kotlin
    fun maxOf(a: Int, b: Int) = if (a > b) a else b
    
    fun main(args: Array<String>) {
        println("max of 0 and 42 is ${maxOf(0, 42)}") // max of 0 and 42 is 42
    }
    
  • nullが返るかもしれない場合 Int?みたいにかく
    kotlin
    fun parseInt(str: String): Int? {
        return str.toIntOrNull()
    }
    

is オブジェクトがキャスト可能か調べる

kotlin
fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // `obj` is automatically cast to `String` in this branch
        return obj.length
    }

    // `obj` is still of type `Any` outside of the type-checked branch
    return null
}

for ループ

kotlin
val items = listOf("apple", "banana", "kiwi")
for (item in items) {
    println(item)
}
// or
val items = listOf("apple", "banana", "kiwi")
for (index in items.indices) {
    println("item at $index is ${items[index]}")
}

when - switchみたいなもの

kotlin
fun describe(obj: Any): String =
when (obj) {
    1          -> "One"
    "Hello"    -> "Greeting"
    is Long    -> "Long"
    !is String -> "Not a string"
    else       -> "Unknown"
}
fun main(args: Array<String>) {
    println(describe(1)) //One
    println(describe("Hello")) // Greeting
    println(describe(1000L)) // Long
    println(describe(2)) // Not a string
    println(describe("other")) // Unknown
}

range

kotlin
for (x in 1..5) {
    print(x) // 12345
}

collection

kotlin
fun main(args: Array<String>) {
    val items = setOf("apple", "banana", "kiwi")
    when {
        "orange" in items -> println("juicy")
        "apple" in items -> println("apple is fine too")
    }
} // apple is fine too
リスト内に特定の文字が含まれているかチェックできる!
kotlin
fun main(args: Array<String>) {
    val fruits = listOf("banana", "avocado", "apple", "kiwi")
    fruits
    .filter { it.startsWith("a") }
    .sortedBy { it }
    .map { it.toUpperCase() }
    .forEach { println(it) }
} 
// APPLE
// AVOCADO

クラス

kotlin
fun main(args: Array<String>) {
    val rectangle = Rectangle(5.0, 2.0) //no 'new' keyword required
    val triangle = Triangle(3.0, 4.0, 5.0)
    println("Area of rectangle is ${rectangle.calculateArea()}, its perimeter is ${rectangle.perimeter}")
    println("Area of triangle is ${triangle.calculateArea()}, its perimeter is ${triangle.perimeter}")
}

abstract class Shape(val sides: List<Double>) {
    val perimeter: Double get() = sides.sum() // getterの`perimeter`プロパティ
    abstract fun calculateArea(): Double
}

interface RectangleProperties {
    val isSquare: Boolean
}

// Primary なコンストラクタは`height`と`length`の引数を受け取る
class Rectangle(
    var height: Double,
    var length: Double
) : Shape(listOf(height, length, height, length)), RectangleProperties {
    override val isSquare: Boolean get() = length == height
    override fun calculateArea(): Double = height * length
}

class Triangle(
    var sideA: Double,
    var sideB: Double,
    var sideC: Double
) : Shape(listOf(sideA, sideB, sideC)) {
    override fun calculateArea(): Double {
        val s = perimeter / 2
        return Math.sqrt(s * (s - sideA) * (s - sideB) * (s - sideC))
    }
}
// Area of rectangle is 10.0, its perimeter is 14.0
// Area of triangle is 6.0, its perimeter is 12.0