https://developer.android.com/develop/ui/compose/mental-model?hl=ja
Jetpack Compose
Jetpack Composeとは、Androidアプリの画面を作るための最新ツールです。これを使うと、アプリの見た目を作るのがすごく簡単になります。どう簡単になるかというと、画面のデザインを「宣言的」に書くことで、プログラムが自動で画面を更新してくれるんです。
宣言型プログラミングって何?
今までのAndroidアプリでは、ユーザーが何か操作をするたびに、プログラマーが手動で画面を更新する必要がありました。例えば、「このボタンを押したら、この文字を表示する」という風に、一つ一つ指示を出していました。
でも、これって結構面倒で、間違えやすいんです。データが色んな場所に表示されていると、どこを更新したらいいかわからなくなったり、2つの更新がかち合ってしまって変な画面になったり…。
Jetpack Composeを使用する前
Androidの従来のUI開発では、XMLでUIのレイアウトを定義し、ActivityやFragmentのコード内でUIコンポーネントに対する操作を行っていました。
XMLレイアウト (activity_main.xml):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ボタン" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/button"
android:text="ここに文字が表示されます"
android:visibility="gone"/>
</RelativeLayout>
Activity (MainActivity.java):
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
TextView textView = findViewById(R.id.textView);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textView.setVisibility(View.VISIBLE);
textView.setText("ボタンが押されました!");
}
});
}
}
Jetpack Composeを使用した後
Compose UI (MainActivity.kt):
このComposeの例では、UIの状態を変更するために状態変数textVisibleを使っています。ボタンがクリックされると、この状態が更新され、UIが自動的に再描画されます。これにより、明示的にUIコンポーネントを探して更新する必要がなくなり、コードがよりシンプルで読みやすくなります。
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var textVisible by remember { mutableStateOf(false) }
Column {
Button(onClick = { textVisible = !textVisible }) {
Text("ボタン")
}
if (textVisible) {
Text("ボタンが押されました!")
}
}
}
}
}
宣言型UIで何が変わる?
宣言型UIでは、画面のどんな部分がどんな状態であるべきかを「宣言」します。あとはComposが勝手に、その指示に従って画面を更新してくれます。これにより、手動で画面をいじる必要がなくなり、エラーが減ってプログラムがシンプルになります。
でも、画面を毎回全部作り直すって大変じゃない?
確かに、画面をゼロから作り直すのは、時間もパワーもバッテリーも使います。でも大丈夫!Composは賢いので、本当に更新する必要がある部分だけを選んで、それだけを再描画します。これにより、パフォーマンスも保ちつつ、開発がラクになるんです。
というわけで、Jetpack Composeは、アプリの見た目を簡単に、エラー少なく、効率的に作れるすごいツールなんです!
宣言型パラダイム シフト
Androidアプリの世界では、画面上で見えるモノ(ボタンやテキストなど)は「ウィジェット」と呼ばれる小さな部品で作られています。今までのやり方では、これらのウィジェットを組み合わせて画面を作るためには、一種の設計図であるXMLファイルを使って、どんなウィジェットがどこにあるかを指定していました。そして、プログラムが動いている間、プログラマーが手動でウィジェットをいじって、画面の見た目を変えていました。
昔のやり方の問題点は?
ウィジェットは、自分自身がどんな状態か(例えば、ボタンが押されたかどうか)を覚えている必要がありました。それに、プログラマーがウィジェットを直接いじるためには、そのウィジェットを探してくる必要があり、これがなかなか面倒だったんです。
たとえ
```bash あなたが部屋の中にいて、部屋の中にはたくさんのボタンがあるとします。それぞれのボタンは、ライトをつけたり、音楽を流したりする役割があります。でも、ある特定のボタンを押したいとき、そのボタンがどこにあるかを毎回探さなければなりません。見つけたら、そのボタンが今、どんな状態にあるか(つまり、ライトがついているか、音楽が流れているか)を確認して、必要に応じて操作します。これが、従来のウィジェットを使った画面更新の方法です。つまり、「探して、確認して、操作する」というステップを踏むわけです。 このやり方の問題点は、特に大きな部屋(たくさんの機能があるアプリ)では、そのボタンを探すのが本当に大変だということです。また、たくさんのボタンがあると、どのボタンがどの状態にあるのかを覚えておくのも一苦労です。 Jetpack Composeの登場で、このやり方が変わります。部屋の中にあるボタンを直接探す代わりに、あなたが何をしたいかをただ「宣言」するだけです。「この部屋でライトをつけて」と言えば、自動的にライトがつきます。ボタンを探したり、現在の状態を確認したりする必要がなくなるわけです。これが「宣言型」のアプローチで、画面の更新がもっとシンプルに、効率的に行えるようになります。 ```Composeは何が違うの?
Jetpack Composeという新しいツールを使うと、このやり方がガラリと変わります。Composeでは、ウィジェットを直接いじるのではなく、ウィジェットがどうあるべきかを「宣言」するだけです。画面上で何かが変わる必要がある時は、Composeが自動で画面を更新してくれます。つまり、プログラマーは「このボタンが押されたら、この文字を表示する」という結果だけを指定すればいいので、ずっとシンプルになります。
どうやって動くの?
- アプリのロジックが、画面に表示するデータを持っています。
- このデータは、Composeによって画面の「レシピ」となるコードに渡されます。
- ユーザーが画面で何か操作をする(例えばボタンを押す)と、そのイベントはアプリのロジックに伝えられ、データが更新されます。
- データが変わると、Composeが自動で画面を最新の状態に更新します。このプロセスを「再コンポーズ」と呼びます。
図で理解する
- 図2:アプリのロジックがデータを持っていて、それをComposeのレシピに渡す流れ。
- 図3:ユーザーが操作すると、イベントが発生してアプリのロジックがデータを更新、Composeが画面を再描画する流れ。
動的コンテンツ
Jetpack Composeを使うと、画面の見た目をプログラミングのコードで直接記述できます。これができるのは、ComposeがKotlinというプログラミング言語で書かれているからです。これにより、動的なコンテンツを非常に柔軟に扱うことが可能になります。
柔軟性
Jetpack Composeの素晴らしい点は、これだけではありません。プログラムの条件(ifステートメント)を使って、特定の条件下で特定のUI要素を表示したり隠したりすることができます。また、ループ(例えばforループ)を使ってリストの各要素に対して操作を行ったり、他の関数を呼び出してコードを再利用したりすることが可能です。
なぜこれが良いのか?
このような柔軟性と機能の豊富さが、Jetpack Composeを使う大きな利点の一つです。XMLのように静的なレイアウトファイルを使う代わりに、動的でリッチなユーザーインターフェイスをプログラムのコードで直接記述できるので、開発者はより創造的で効率的にアプリのUIをデザインできるようになります。
再コンポーズ
Jetpack Composeでの「再コンポーズ」というのは、ちょっとした魔法みたいなものです。画面の一部分が変わる必要がある時、Composeはその部分だけを賢く更新します。これは、アプリをもっと早く、バッテリーを長持ちさせる秘密の技です。
例:クリックカウンターボタン
あなたがボタンをクリックするたびに、画面に「このボタンは○回クリックされました」と表示させたいとしましょう。
@Composable
fun ClickCounter(clicks: Int, onClick: () -> Unit) {
Button(onClick = onClick) {
Text("I've been clicked $clicks times")
}
}
このコードは、ボタンがクリックされるたびに、ボタンの上にあるテキストを更新します。Composeは、クリックの数が変わるたびに、このテキストだけを再描画します。
再コンポーズって何がすごいの?
- 賢い更新:Composeは変わった部分だけを更新します。画面の他の部分はそのままで、必要なところだけをピンポイントで直します。
- 高速&バッテリー長持ち:全てを一から作り直す代わりに、変更があった部分だけを直すので、速くてバッテリーにも優しいです。
注意点
- 副作用に注意:コンポーズ可能な関数は何回も実行されることがあるので、外部のデータを変えるような操作(副作用)には注意が必要です。例えば、データベースを更新するようなことをこの関数の中でやると、予想外のことが起こるかもしれません。
- アニメーション中は特に注意:アニメーションがスムーズに動くように、コンポーズ可能な関数は早く動く必要があります。重たい処理はバックグラウンドでやりましょう
Compose を使用するときに注意すべき点
コンポーズ可能な関数は任意の順序で実行できる
Composeでは、画面のコンポーネントを作る関数は、必ずしも書かれた順番通りに動くわけではありません。これは、Composeが画面の更新を効率的に行うために、関数を最適な順序で実行するからです。つまり、画面の一部を描画するために、関数Aが先に、関数Bが後に実行されるとは限らないのです。
コンポーズ可能な関数は並行して実行できる
Composeは、画面を更新するための関数を同時に(並行して)実行することができます。これにより、画面の更新が速くなり、アプリがスムーズに動作します。ただし、この特性のために、関数内で外部のデータを変更するような「副作用」があると問題が起こることがあります。
再コンポーズは可能な限りスキップする
画面の小さな部分だけが変更された場合、Composeは必要最低限の更新だけを行います。つまり、変更があった部分に関連する関数だけが再び実行され、他の部分はそのままにされます。これにより、アプリのパフォーマンスが向上します。
再コンポーズは厳密なものではない
画面を更新するために関数が再び実行される(再コンポーズされる)とき、その処理は中断されることがあります。たとえば、更新中に関数が再び実行されるべき新しいデータが来た場合です。このため、関数の中で外部の状態に依存することは避けるべきです。
コンポーズ可能な関数は何度も実行されることがある
アニメーションなどのために、コンポーズ可能な関数が何度も繰り返し実行されることがあります。そのため、関数が重い処理を含むと、アプリの動作が遅くなったり、不自然になったりすることがあります。重い処理は、別の場所で行い、結果だけを関数に渡すようにしましょう。