（自分用メモ）現場で、Android開発を始めたので、直近で学んだことをただひたすらまとめる。

## kotlinで、外部APIを、repository → viewModel → Activityの流れで呼び出す。

### Repositoryの作成
外部APIへのアクセスを担当するrepositoryを作成。repositoryはデータの取得や保存、変換などの責務を持つ。外部APIとの通信を行い、必要なデータを取得する。

```kotlin
class MyRepository {
    suspend fun fetchData(): ApiResponse {
        // 外部APIからデータを取得する処理を実装
    }
}
```

### ViewModelの作成
データの処理とUIの状態管理を担当するviewModelを作成する。viewModelはrepositoryからデータを取得し、必要な加工や変換を行う。また、viewModelはActivityとの間でデータや状態のやり取りを行う。

```kotlin
class MyViewModel(private val repository: MyRepository) : ViewModel() {
    private val _data = MutableLiveData<Data>()
    val data: LiveData<Data> = _data

    fun fetchData() {
        viewModelScope.launch {
            val response = repository.fetchData()
            if (response.isSuccessful) {
                val data = response.body() // レスポンスデータを取得
                _data.value = data // UIにデータを通知
            } else {
                // エラーハンドリングなど
            }
        }
    }
}
```

### Activityの作成
ユーザーインターフェースを担当するActivityを作成。ActivityはviewModelと連携し、データや状態の更新を受け取ってUIを更新する。また、ユーザーの操作やイベントをviewModelに伝える。

```kotlin
class MyActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ViewModelの初期化
        viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
        // データの変更を監視し、UIを更新
        viewModel.data.observe(this, Observer { data ->
            // UIの更新処理
        })

        // データの取得を開始
        viewModel.fetchData()
    }
}
```

`repository`が外部APIとの通信を担当し、`viewModel`がデータの取得と加工を行い、`Activity`がUIの表示とユーザーの操作を扱う。

### APIを呼び出すときの、dataクラスについて

APIを呼び出す際には、データの受け取りや送信に使用するデータクラスを定義することが一般的。データクラスは、APIのレスポンスやリクエストのボディに対応するデータ構造を表現する。

データクラスは、Kotlinの`data class`キーワードを使用して定義される。

```kotlin
data class User(
    val id: String,
    val name: String,
    val email: String
)
```

このデータクラスは、`id`、`name`、`email`というプロパティを持つ。データクラスは自動的に以下の機能を提供する。

* プロパティのゲッターとイコール（`equals()`）メソッド
* プロパティのハッシュコード（`hashCode()`）メソッド
* プロパティの文字列表現（`toString()`）メソッド
* プロパティのコピー（`copy()`）メソッド

```json
{
  "id": "123",
  "name": "John",
  "email": "john@example.com"
}
```

このレスポンスデータを受け取るためのデータクラスを定義すると以下のようになる。

```kotlin
data class User(
    val id: String,
    val name: String,
    val email: String
)
```

そして、APIのレスポンスを受け取る際には、レスポンスデータをデータクラスのインスタンスにマッピングする。

```kotlin
val response = // APIレスポンスを受け取る処理
val user = response.body()?.let { User(it.id, it.name, it.email) }
```

APIのレスポンスをUserデータクラスのインスタンスに変換。レスポンスの`id`、`name`、`email`というフィールドをデータクラスのプロパティに対応付ける。

データクラスを使用することで、APIのデータを型安全に扱うことができる。また、データクラスの便利な機能を利用して、比較や表示などの処理を簡潔に記述することができる。

## dataクラスで定義する　@parcelableはどの様な時に使うのか

`@Parcelize`アノテーションは、Kotlinのデータクラスを`Parcelable`として宣言するために使用される。`Parcelable`は、Androidフレームワークでオブジェクトの状態をシリアライズ（直列化）およびデシリアライズ（非直列化）するためのインターフェース。

1. 簡潔な実装
`@Parcelize`をデータクラスに適用することで、`Parcelable`インターフェースの実装が自動的に生成される

2. パフォーマンスの向上
`Parcelable`はJavaの`Serializable`よりも高速であり、データのシリアライズとデシリアライズにかかるオーバーヘッドが少ないため、パフォーマンスが向上する。

`@Parcelize`は、以下のようなケースで使用される。

* `Intent`を使用してオブジェクトを他の`Activity`に渡す必要がある場合。
* データを`Bundle`に格納してフラグメント間でやり取りする場合。
* `ViewModel`など、`Parcelable`が要求されるクラスとの統合が必要な場合。

ただし、`@Parcelize`を使用するためには、以下の条件が必要。

* データクラスは直列化可能なプロパティ（基本データ型や他の`Parcelable`オブジェクトなど）のみを持つ必要がある。
* データクラスはトップレベルであるか、ネストしたクラスである必要がある。

`Parcelable`が必要な場合やパフォーマンスの向上が求められる場合には、`@Parcelize`を使用すると便利。ただし、シリアライズやデシリアライズの必要がない場合やデータが複雑でない場合には、独自のシリアライゼーション手法を使用することもできる。

```kotlin
@JsonClass(generateAdapter = true)
@Parcelize
data class TestData(
        val testItemsCount: TestItemsCount,
        val testItemsStamp: TestItemsStamp
) : Parcelable

@JsonClass(generateAdapter = true)
@Parcelize
data class TestItemsCount(
        val count_this_month: Int,
        val count_total: Int,
        val updated_at: String?
) : Parcelable

@JsonClass(generateAdapter = true)
@Parcelize
data class TestItemsStamp(
        val title: String,
        val detail: String,
        val test_flag: Boolean,
        val stamps: List<Stamps>
) : Parcelable

@JsonClass(generateAdapter = true)
@Parcelize
data class Stamps(
        val number: Int,
        val is_stamp: Boolean,
        val date: String?
) : Parcelable
```

## 実際に叩かれているエンドポイントを確認する

```kotlin
suspend fun fetchTest(id: Int): TestData = withContext(dispatchersProvider.io) {
    Log.d("API", "Requesting fetchTest with ID: $id")
    val response = testApi.fetchTest(id).await()
    Log.d("API", "Response: $response")
    response
}
```

* ログ出力を確認するためには、アプリをビルドして実行し、対象の画面に遷移する必要がある。ログは実行時に出力されるため、実際のアプリの動作中にログを確認することができる。
* アプリを実行する際には、Logcatを使用する。
* アプリが実行されている間は、ログメッセージがリアルタイムに表示されるので、APIリクエストの前後のログやレスポンスデータを確認することができる。

## 関数の中で、関数を定義する

関数内で定義された関数をローカル関数（Local Function）と呼ぶ。

```kotlin
fun outerFunction() {
    println("Outer Function")

    fun innerFunction() {
        println("Inner Function")
    }

    innerFunction() // ローカル関数の呼び出し
}

fun main() {
    outerFunction()
}
```

`outerFunction`内で`innerFunction`というローカル関数を定義。`outerFunction`を呼び出すと、"Outer Function"が表示され、その後に`innerFunction`が呼び出され、"Inner Function"が表示される。

ローカル関数は、外部の関数内でのみアクセス可能であり、外部の関数のローカル変数やパラメータにアクセスすることができる。また、ローカル関数は外部から直接アクセスできないため、カプセル化やコードの整理に役立つ。

注意点として、ローカル関数は定義された位置よりも後で使用することはでき無い。つまり、ローカル関数が定義される前に呼び出そうとするとコンパイルエラーになる。

Kotlinでは、ローカル関数は同じスコープ内の他の関数からは直接呼び出すことができ無い。ローカル関数は、定義された関数内でのみアクセス可能。

## nullだった場合に、textViewをisVisible = falseにする。

`?.let`ブロックを使用してnull安全な操作を行う。

```kotlin
val data: DataClass? = // データクラスのインスタンスを取得するなど

data?.propertyName?.let { value ->
    textView.text = value
    textView.isVisible = true
} ?: run {
    textView.isVisible = false
}
```

`data?.propertyName`が`null`でない場合は、`let`ブロック内の処理が実行される。`textView.text`に値をセットし、`textView.isVisible`を`true`に設定する。

`data?.propertyName`が`null`の場合は、`?.let`ブロックがスキップされ、`run`ブロックが実行される。`run`ブロック内の処理では、`textView.isVisible`を`false`に設定して`textView`を非表示にする。

このようにすることで、`null`の場合に`textView`を非表示にすることができる。



## onResume内で、現在のviewが非表示かどうかの判定をする

### `View`の可視性を直接チェックする方法
`onResume`内で現在の`View`の可視性を直接チェックすることができる。
これには、`View`の`isVisible`プロパティを使用する。

```kotlin
override fun onResume() {
    super.onResume()

    if (view.isVisible) {
        // Viewが表示されている場合の処理
    } else {
        // Viewが非表示の場合の処理
    }
}
```

この方法は、`View`自体の可視性をチェックするため、親の`View`や`Fragment`の可視性には依存しない

### `View`の可視性を親の可視性に基づいてチェックする方法 
もし、`View`が他の親の`View`内に含まれている場合、その親の可視性に基づいて現在の`View`の可視性を判定することもできる。これには、`View`の`isShown`メソッドを使用する。

```kotlin
override fun onResume() {
    super.onResume()

    if (view.isShown) {
        // Viewが表示されている場合の処理
    } else {
        // Viewが非表示の場合の処理
    }
}
```

この方法は、`View`が他の親の`View`内に含まれている場合にのみ有効。また、`View`自体の可視性だけでなく、親の可視性にも依存するため、親の可視性が変更された場合にも適切に反映される。

注意点として、これらの方法は`View`の可視性をチェックするだけであり、`View`が非表示の場合の原因（例: `setVisibility(View.GONE)`による非表示設定）については判定しない。

## visibilityと、isVisibleの使い分け



### `visibility`プロパティ
`visibility`プロパティは、Viewの表示状態を設定および取得するためのプロパティ。
`visibility`プロパティには、次の3つの値を指定できる。
* `View.VISIBLE`:  Viewを表示。
* `View.INVISIBLE`:  Viewを非表示にするが、領域を確保する。
* `View.GONE`: Viewを非表示にし、領域も確保しない。

`visibility`プロパティは、Int`型であり、設定時には上記の値を指定。直接設定することができ、可視性を細かく制御できる。

### `isVisible`プロパティ

* `View`が表示されているかどうかを判定するためのプロパティ。
* `View`の可視性を基にしてtrueまたはfalseを返す。
* `Boolean`型であり、判定のみに使用する。
* 読み取り専用であるため、直接設定することはでき無い。

基本的には、`View`の可視性を設定する場合は`visibility`プロパティを使用し、
`View`の可視性を判定する場合は`isVisible`プロパティを使用する。

ただし、特定のケースでは両方を組み合わせて使用することもある。
例えば、可視性を設定する際に他の`View`の状態に応じて条件分岐を行い、可視性を判定する場合など。

また、注意点として、`visibility`プロパティの変更は実際にUIに反映されるまでに時間がかかる場合があるため、UIの更新が完了するまで待つ必要がある。
それに対して、`isVisible`プロパティは即座に現在の可視性を判定する。



## あるボタンを押した際、指定のviewが画面の一番上に来る様に、画面をスクロールさせる。



XMLレイアウトファイルで、`ScrollView`内にスクロール可能なコンテンツを配置。

```xml
<ScrollView
    android:id="@+id/scrollView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/containerLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <!-- スクロール可能なコンテンツ -->

    </LinearLayout>
</ScrollView>
```

Kotlinのアクティビティファイル（またはフラグメントファイル）で、ボタンのクリックイベントを処理するためのコードを追加。

```kotlin
val scrollView = findViewById<ScrollView>(R.id.scrollView)
val containerLayout = findViewById<LinearLayout>(R.id.containerLayout)
val targetView = findViewById<View>(R.id.targetView)  // スクロールさせたいViewのID
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
    scrollView.post {
        val targetTop = targetView.top
        scrollView.smoothScrollTo(0, targetTop)
    }
}
```

上記のコードは、ボタンをクリックしたときに`ScrollView`をスクロールさせ、指定した`View（targetView）`が画面の一番上に表示されるようしている。`targetView`は、スクロールさせたい`View`の`ID`に置き換える。

`scrollView.post`メソッドは、`ScrollView`がレイアウト計算を完了してからスクロール処理を行うために使用される。
`scrollView.smoothScrollTo`メソッドは、指定した座標にスクロールするためのメソッド。

これにより、ボタンを押すと指定した`View`までスクロールし、その`View`が画面の一番上に表示される。



## adapterにデータを渡す際、 private var data: List<String> = emptyList()の様に、emptyList()を使うのは何故か

初期状態でデータが空のリストであることを示すため。

`emptyList()`は、Kotlin標準ライブラリで提供される関数であり、要素がない空のリストを生成する。
これにより、アダプターが初期化されたときにデータがない状態であることを明示的に示すことができる。

### Null安全性
`emptyList()`を使用することで、`data`プロパティは非`null`であり、常に有効なリストオブジェクトを参照していることが保証される。
これにより、`null`チェックやヌルポインタエラーを回避することができる。

### 初期状態の一貫性 
初期状態で空のリストを使用することで、アダプターがデータなしで初期化されたことが明確になる。
これにより、アプリケーションの他の部分でデータの有無をチェックする必要がある場合でも、一貫性を保つことができる。

### データの追加や変更
`emptyList()`を使用すると、後からデータを追加または変更することが容易になる。
`data`プロパティは`var` で宣言されているため、後で新しいリストを代入することができる。

```kotlin
adapter.data = listOf("Item 1", "Item 2", "Item 3") // データを追加
```

## 文字列2023-04-01を、04月01日にフォーマットする。


```kotlin
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

val dateString = "2023-04-01"
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val date = dateFormat.parse(dateString)

println(date) // 出力: Sun Apr 01 00:00:00 JST 2023
```
まず日付文字列「2023-04-01」を S`impleDateFormat`オブジェクトを使って `Date`オブジェクトに変換する。
`SimpleDateFormat`のパターン文字列には「yyyy-MM-dd」を指定し、与えられた日付文字列と一致するようにする。

変換後、`date`には`Date`オブジェクトが格納されている。

表示方法や利用方法は、Date オブジェクトのプロパティやメソッドに従って行うことができる。



### 文字列2023-04-01を、2023/04/01にフォーマットする。

```kotlin
val dateString = "2022-06-01"
val formattedDate = dateString.replace("-", "/")
```

`replace`メソッドを使用して、ハイフン(`-`)をスラッシュ(`/`)に置換。これにより、"2022-06-01" が "2022/06/01" に変換される。


## 数字が四桁になった場合、100の位から、,を入れる。


```kotlin
val number = 1234
val formattedNumber = String.format("%,d", number)
```

`String.format`メソッドを使用して、指定したフォーマットに従って数値を文字列に変換。
`%,d`というフォーマット指定子を使用することで、3桁ごとにカンマを挿入する。
例1234 は "1,234" という形式でフォーマットされる。