2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2023-06-10

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

Repositoryの作成

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

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

ViewModelの作成

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

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に伝える。

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キーワードを使用して定義される。

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

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

  • プロパティのゲッターとイコール(equals())メソッド
  • プロパティのハッシュコード(hashCode())メソッド
  • プロパティの文字列表現(toString())メソッド
  • プロパティのコピー(copy())メソッド
{
  "id": "123",
  "name": "John",
  "email": "john@example.com"
}

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

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

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

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

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

データクラスを使用することで、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を使用すると便利。ただし、シリアライズやデシリアライズの必要がない場合やデータが複雑でない場合には、独自のシリアライゼーション手法を使用することもできる。

@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

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

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)と呼ぶ。

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安全な操作を行う。

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

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

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

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

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

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

Viewの可視性を直接チェックする方法

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

override fun onResume() {
    super.onResume()

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

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

Viewの可視性を親の可視性に基づいてチェックする方法

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

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内にスクロール可能なコンテンツを配置。

<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のアクティビティファイル(またはフラグメントファイル)で、ボタンのクリックイベントを処理するためのコードを追加。

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は、スクロールさせたいViewIDに置き換える。

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

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

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

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

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

Null安全性

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

初期状態の一貫性

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

データの追加や変更

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

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

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

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」を SimpleDateFormatオブジェクトを使って Dateオブジェクトに変換する。
SimpleDateFormatのパターン文字列には「yyyy-MM-dd」を指定し、与えられた日付文字列と一致するようにする。

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

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

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

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

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

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

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

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

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?