Edited at

KotlinでAndroid

More than 1 year has passed since last update.

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


Kotlin


log

print("hogeohoge")

println("HOGEHOGE")
//=> hogehogeHOGEHOGE

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


配列

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

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;

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

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


immutable read-only

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

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

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

var list = listOf(0, 1, 2, 3, 4)

//list[2] = 0 //エラー

なぜimmutableではないかと言うと、

読み取り専用とイミュータブルの違い

‥ということらしい。

ようするに、


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

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

val list = mutableListOf(0, 1, 2, 3, 4)

list[2] = 0
println(list)
//=> [0, 1, 0, 3, 4]


mapとか

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

val ordinals = listOf<String>("first", "second", "third")

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


辞書

val hashMap = hashMapOf("key" to "value")

println(hashMap["key"])
//=> value
println(hashMap.javaClass)
//=> class java.util.HashMap


lateinit

iOSのStoryboardの定番。


swift

class Hoge: UIViewController {

@IBOutlet weak var button: UIButton!
}

こうやる。


kotlin

class Hoge : AppCompatActivity() {

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


Android


View取得


activity_main.xml

android:id="@+id/text_view"


val textView = findViewById(R.id.text_view) as TextView

println(textView.text)
//=> Hello World!

Kotlin Android Extensionsを使うとidで直接アクセス出来るようですが、あまりそういうのに依存するのもアレかと思うので使わないことにします。

println(text_view.text) //いきなり使える(らしい)

//=> Hello World!


リソース取得


String


strings.xml

<resources>

<string name="string_hello">Hello Kotlin!</string>
</resources>


.kt

println(getString(R.string.string_hello))

//=> Hello Kotlin!


Color

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


colors.xml

<resources>

<color name="red">#ff0000</color>
</resources>


.kt

ContextCompat.getColor(this, R.color.red)


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


Button


SAM (Single Abstract Method)

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+引数省略

button.setOnClickListener {

(it as Button).text = "thanks!"
}


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

val listener: (View) -> Unit = {

(it as Button).text = "thanks!"
}
button.setOnClickListener(listener)

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


遷移


普通に遷移


MainActivity.kt

//val intent = Intent(this@MainActivity, javaClass<SecondActivity>()) //エラー。古い書き方?

val intent = Intent(this@MainActivity, SecondActivity::class.java)
startActivity(intent)

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


SecondActivity

finish()


戻る。


すげ替え

setContentView(R.layout.activity_second)


ListView

超基本的な部分だけ。

更新はadapterにお願いする。

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 簡単なテキストリストの表示


Preference


取得

val pref = getSharedPreferences("Hoge", MODE_PRIVATE)

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

val pref = getPreferences(MODE_PRIVATE)

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

val pref = PreferenceManager.getDefaultSharedPreferences(this)

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


書き込み

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

Editorを通して書き込む。型は、int、long、float、boolean、String。

commit()は同期、apply()は非同期で書き込む。


読み込み

println(pref1.getString("key", "default"));

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

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


xml


com.example.testpreference.xml

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>

<map>
<string name="key">hoge</string>
</map>

参考:SharedPreferencesに出力したデータの保存場所


時間差処理

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


.kt

Handler(mainLooper).postDelayed({

//処理
}, 3000) //3000ms後