KotlinでAndroid

Androidの勉強をしたいけどJavaが全然頭に入ってこないのでKotlinを使うことにしました。自分用の覚書です。

# Kotlin

## log

```kotlin
print("hogeohoge")
println("HOGEHOGE")
//=> hogehogeHOGEHOGE
```

改行するまで出力されません。最初、print()でログに表示されないので困ってました。

## 配列

listとかarrayとか。この辺、Java分かってないとダメっぽい。あとで調べる。

```kotlin
val list = listOf(0, 1, 2)
println(list.javaClass)
//=> class java.util.Arrays$ArrayList

val mutableList = mutableListOf(0, 1, 2)
println(mutableList.javaClass)
//=> class java.util.ArrayList

val array = arrayOf(0, 1, 2)
println(array.javaClass)
//=> class [Ljava.lang.Integer;
```


最後のカンマはエラーになる。個人的にこれは痛い。

```kotolin
val list = listOf(0, 1, 2, 3, 4,)
```

### ~~immutable~~ read-only

```kotlin
val list = listOf(0, 1, 2, 3, 4)
//list[2] = 0 //エラー
println(list)
//=> [0, 1, 2, 3, 4]
```

`var`にしても中身は~~immutable~~ read-only。

```kotlin
var list = listOf(0, 1, 2, 3, 4)
//list[2] = 0 //エラー
```

なぜimmutableではないかと言うと、
[読み取り専用とイミュータブルの違い](https://qiita.com/opengl-8080/items/36351dca891b6d9c9687#%E8%AA%AD%E3%81%BF%E5%8F%96%E3%82%8A%E5%B0%82%E7%94%A8%E3%81%A8%E3%82%A4%E3%83%9F%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%96%E3%83%AB%E3%81%AE%E9%81%95%E3%81%84)
‥ということらしい。

ようするに、

```kotlin:kotlin
val list = listOf(0, 1, 2, 3, 4)
println(list)
//=> [0, 1, 2, 3, 4]

//MutableListにコピー(Shallow copy)して書き換えると
val mutableList = list as MutableList
mutableList[2] = 999

//listが書き換えられてる!!
println(list)
//=> [0, 1, 999, 3, 4]
```

ということ。（たぶん）

SwiftはDeep copyなので上記のようなことにはならない。

```swift:swift
let immutableArray = [0, 1, 2, 3, 4]
//immutableArray[2] = 999 //エラー
print(immutableArray)
//=> [0, 1, 2, 3, 4]

var mutableArray = immutableArray
mutableArray[2] = 999
print(mutableArray)
//=> [0, 1, 999, 3, 4] //こっちは当然書き換えられる
print(immutableArray)
//=> [0, 1, 2, 3, 4]   //元の方は書き換わっていない
```

### mutable

```kotlin
val list = mutableListOf(0, 1, 2, 3, 4)
list[2] = 0
println(list)
//=> [0, 1, 0, 3, 4]
```

### mapとか

```kotlin
val list = listOf(0, 3, 2, 4, 1)
println(list.map {it * 2})
//=> [0, 6, 4, 8, 2]
println(list.sorted())
//=> [0, 1, 2, 3, 4]
```

### withIndex()

```kotlin
val ordinals = listOf<String>("first", "second", "third")
for ((index, value) in ordinals.withIndex()) {
    println("$index: $value")
}
//=> 0: first
//=> 1: second
//=> 2: third
```

## 辞書

```kotlin
val hashMap = hashMapOf("key" to "value")
println(hashMap["key"])
//=> value
println(hashMap.javaClass)
//=> class java.util.HashMap
```

## lateinit

iOSのStoryboardの定番。

```swift:swift
class Hoge: UIViewController {
    @IBOutlet weak var button: UIButton!
}
```

こうやる。

```kotlin:kotlin
class Hoge : AppCompatActivity() {
    lateinit var button : Button
    override fun onCreate(savedInstanceState: Bundle?) {
    :
    button = findViewById(R.id.button) as Button
    }
}
```

# Android

## View取得

```xml:activity_main.xml
android:id="@+id/text_view"
```

```kotlin
val textView = findViewById(R.id.text_view) as TextView
println(textView.text)
//=> Hello World!
```

[Kotlin Android Extensions](https://kotlinlang.org/docs/tutorials/android-plugin.html)を使うと`id`で直接アクセス出来るようですが、あまりそういうのに依存するのもアレかと思うので使わないことにします。

```kotlin
println(text_view.text) //いきなり使える(らしい)
//=> Hello World!
```

## リソース取得

### String

```xml:strings.xml
<resources>
    <string name="string_hello">Hello Kotlin!</string>
</resources>
```

```Kotlin:.kt
println(getString(R.string.string_hello))
//=> Hello Kotlin!
```

### Color

API23未満がえらい面倒だったのでメモ。

```xml:colors.xml
<resources>
    <color name="red">#ff0000</color>
</resources>
```

```kotlin:.kt
ContextCompat.getColor(this, R.color.red)
```

API23以上は`getColor(R.color.red)`で取れる。（たぶん）


## Button

### SAM (Single Abstract Method)

```kotlin
val button = findViewById(R.id.button) as Button
button.text = "push me"
button.setTextColor(BLUE)
//button.textColor = BLUE //これはダメだった
button.setOnClickListener { view ->
    val button = view as Button
    button.text = "thanks!"
}
```

### SAM+引数省略

```kotlin
button.setOnClickListener {
    (it as Button).text = "thanks!"
}
```

### 関数化‥じゃなくて変数化?

```kotlin
val listener: (View) -> Unit = {
    (it as Button).text = "thanks!"
}
button.setOnClickListener(listener)
```

この辺はあとでちゃんと調べる。

## 遷移

### 普通に遷移
```kotlin:MainActivity.kt
//val intent = Intent(this@MainActivity, javaClass<SecondActivity>()) //エラー。古い書き方?
val intent = Intent(this@MainActivity, SecondActivity::class.java)
startActivity(intent)
```

`this@MainActivity`はクラスを明示せず`this`だけでOK。

```kotlin:SecondActivity
finish()
```

戻る。

### すげ替え

```kotlin
setContentView(R.layout.activity_second)
```

## ListView

超基本的な部分だけ。
更新はadapterにお願いする。

```kotlin
val weekdays = mutableListOf(
    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
)
val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, weekdays)
val listView = findViewById(R.id.list_view) as ListView
listView.adapter = adapter

//更新
weekdays[1] = "HOGEHOGE"
adapter.notifyDataSetChanged()

//これでもいける（けどキャストが‥）
(listView.adapter as ArrayAdapter<*>).notifyDataSetChanged()
```

参考：[[Android] ListView と ArrayAdapter 簡単なテキストリストの表示](https://akira-watson.com/android/listview_1.html)

## Preference

### 取得

```kotlin
val pref = getSharedPreferences("Hoge", MODE_PRIVATE)
```

基本形。nameとmodeを指定して取得。modeの`MODE_WORLD_READABLE`や`MODE_WORLD_WRITEABLE`はdeprecatedになっている。

```kotlin
val pref = getPreferences(MODE_PRIVATE)
```

アクティビティのクラス名で取得。例えば、nameに"MainActivity"を指定するのと同じこと。

```kotlin
val pref = PreferenceManager.getDefaultSharedPreferences(this)
```

パッケージ名で取得出来る。modeは`MODE_PRIVATE`固定。`MODE_WORLD_READABLE`や`MODE_WORLD_WRITEABLE`が無くなっているので、これだけでいいんじゃないかと思う。

### 書き込み

```kotlin
pref.edit().putString("key", "hoge").commit()
```

Editorを通して書き込む。型は、int、long、float、boolean、String。
`commit()`は同期、`apply()`は非同期で書き込む。

### 読み込み

```kotlin
println(pref1.getString("key", "default"));
//=> hoge
println(pref1.getString("undefinedKey", "default"));
//=> default
```

第2引数でキーが未定義の場合の値を指定する。

### xml

```xml:com.example.testpreference.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="key">hoge</string>
</map>
```


参考：[SharedPreferencesに出力したデータの保存場所](http://qiita.com/ochim/items/dc4d77d478e87c1449ad)

## 時間差処理

とりあえずメインスレッド用。UI触りたいときに。

```kotlin:.kt
Handler(mainLooper).postDelayed({
    //処理
}, 3000) //3000ms後
```