2020年11月12日木曜日

複数の nullable な mutable 変数の null チェック

nullable な mutable 変数 a と b があって、両方 non-null のとき何かしたいという場合、

これだとコンパイルエラーになる var a: String? = "Hello" var b: String? = "Android" fun main() { if (a != null && b != null) { println(a.length + b.length) // compile error! } } let だと入れ子になるからいまいち var a: String? = "Hello" var b: String? = "Android" fun main() { a?.let { a -> b?.let { b -> println(a.length + b.length) } } } ローカルの変数に入れれば OK var a: String? = "Hello" var b: String? = "Android" fun main() { val a = a val b = b if (a != null && b != null) { println(a.length + b.length) // ok } } vararg を使って、渡した値が全部 non-null だったら block を実行するというメソッドを用意すると、let っぽい感じで入れ子にせずに書ける var a: String? = "Hello" var b: String? = "Android" fun main() { doIfAllNotNull(a, b) { (a, b) -> println(a.length + b.length) } } private inline fun <T> doIfAllNotNull( vararg value: T?, block: (values: List<T>) -> Unit ) { val nonNullValues = value.filterNotNull() if (nonNullValues.size == value.size) { block(nonNullValues) } }

2020年11月11日水曜日

Kotlin メモ : vetoable

vetoable

変更を拒否(veto)するかどうかのコールバックを指定できる property delegate を返す。

fun main() { var value: Int by Delegates.vetoable(0) { property, oldValue, newValue -> newValue > 0 } println(value) // 0 value = 10 println(value) // 10 value = -1 println(value) // 10 }

2020年11月10日火曜日

ConstraintSet を使って Activity を再生成せずにレイアウトを切り替える

自分で縦横レイアウトを切り替えるので Manifest に設定を追加 <activity android:name=".MainActivity" android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"> ... </activity> 縦画面用のレイアウトと、横画面用のレイアウトを用意する。
例としてここでは縦画面では上部に 16:9 で配置し、横画面では全画面に配置している。

縦画面用
res/layout/activity_main_port.xml <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/constraintLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <include android:id="@+id/container" layout="@layout/activity_main" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintDimensionRatio="16:9" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> 横画面用
res/layout/activity_main_land.xml <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/constraintLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <include android:id="@+id/container" layout="@layout/activity_main" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> activity_main_port.xml と activity_main_land.xml からそれぞれ ConstraintSet を構成する。
onConfigurationChanged() で画面の向きに応じて対応する ConstraintSet を ConstraintLayout に applyTo() する。 class MainActivity : AppCompatActivity() { private val constraintSetPort = ConstraintSet() private val constraintSetLand = ConstraintSet() private lateinit var constraintLayout: ConstraintLayout override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val layoutId = when (resources.configuration.orientation) { Configuration.ORIENTATION_LANDSCAPE -> R.layout.activity_main_land else -> R.layout.activity_main_port } setContentView(layoutId) constraintLayout = findViewById(R.id.constraintLayout) constraintSetPort.clone(this, R.layout.activity_main_port) constraintSetLand.clone(this, R.layout.activity_main_land) } override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) val constraintSet = when (newConfig.orientation) { Configuration.ORIENTATION_LANDSCAPE -> constraintSetLand else -> constraintSetPort } constraintSet.applyTo(constraintLayout) } }