Jetpack Compose:システム提供のCompositionLocal

投稿日:  更新日:

CompositionLocalはシステムにより提供されているものがあります。

プログラミングに有益で利用頻度の高いものが用意されています。例えば、LocalContextやLocalConfigurationなどです。

※環境:Android Studio Giraffe | 2022.3.1 Patch 1
    Kotlin 1.8.10
    Compose Compiler 1.4.3

スポンサーリンク

CompositionLocalのリスト

システム提供のCompositionLocalは、パッケージandroidx.compose.ui.platform以下に定義されています。

Android Studioのエディターを用いれば、補完機能(Ctrl+SPACE)がリストを表示してくれるので便利です。

システム提供のCompositionLocalリスト
※エディタで「androidx.compose.ui.platform.Local」と打ち込んだ場合

また、androidx.compose.ui.platformのドキュメントに、簡単な説明が掲載されています。

スポンサーリンク

状態の保持

状態の保持はMainActivity#setContent()を実行した際に自動的に行われます。ですので、ユーザは何もすることなく、システム提供のCompositionLocalが利用可能です。

class MainActivity : ComponentActivity() {
    @SuppressLint("CoroutineCreationDuringComposition", "UnrememberedMutableState")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent { // ← システム提供のCompositionLocalの状態を保持
            androidx.compose.ui.platform.LocalContext
            MyApplicationTheme {
                // アプリのコンテンツ
            }
        }
    }
}

内部ではProvideAndroidCompositionLocals関数が実行されています。

@Composable
@OptIn(ExperimentalComposeUiApi::class)
internal fun ProvideAndroidCompositionLocals(
    owner: AndroidComposeView,
    content: @Composable () -> Unit
) {
    val view = owner
    val context = view.context
    var configuration by remember {
        mutableStateOf(
            context.resources.configuration,
            neverEqualPolicy()
        )
    }

    ...

    val imageVectorCache = obtainImageVectorCache(context, configuration)
    CompositionLocalProvider(
        LocalConfiguration provides configuration,
        LocalContext provides context,
        LocalLifecycleOwner provides viewTreeOwners.lifecycleOwner,
        LocalSavedStateRegistryOwner provides viewTreeOwners.savedStateRegistryOwner,
        LocalSaveableStateRegistry provides saveableStateRegistry,
        LocalView provides owner.view,
        LocalImageVectorCache provides imageVectorCache
    ) {
        ProvideCommonCompositionLocals(
            owner = owner,
            uriHandler = uriHandler,
            content = content
        )
    }
}
@OptIn(ExperimentalTextApi::class)
@ExperimentalComposeUiApi
@Composable
internal fun ProvideCommonCompositionLocals(
    owner: Owner,
    uriHandler: UriHandler,
    content: @Composable () -> Unit
) {
    CompositionLocalProvider(
        LocalAccessibilityManager provides owner.accessibilityManager,
        LocalAutofill provides owner.autofill,
        LocalAutofillTree provides owner.autofillTree,
        LocalClipboardManager provides owner.clipboardManager,
        LocalDensity provides owner.density,
        LocalFocusManager provides owner.focusOwner,
        @Suppress("DEPRECATION") LocalFontLoader
            providesDefault @Suppress("DEPRECATION") owner.fontLoader,
        LocalFontFamilyResolver providesDefault owner.fontFamilyResolver,
        LocalHapticFeedback provides owner.hapticFeedBack,
        LocalInputModeManager provides owner.inputModeManager,
        LocalLayoutDirection provides owner.layoutDirection,
        LocalTextInputService provides owner.textInputService,
        LocalPlatformTextInputPluginRegistry provides owner.platformTextInputPluginRegistry,
        LocalTextToolbar provides owner.textToolbar,
        LocalUriHandler provides uriHandler,
        LocalViewConfiguration provides owner.viewConfiguration,
        LocalWindowInfo provides owner.windowInfo,
        LocalPointerIconService provides owner.pointerIconService,
        content = content
    )
}

※状態の保持については「Jetpack Compose:UIツリーにローカルな変数の確保(CompositionLocal)」を参照

スポンサーリンク

StaticとDynamic

キーの定義をみると、LocalConfigurationのみDynamic側であり、その他はStatic側である事に注意してください。

ユーザー定義のCompositionLocalと再Composeの範囲に違いが出ます。

    :
	:
/**
 * The Android [Configuration]. The [Configuration] is useful for determining how to organize the
 * UI.
 */
val LocalConfiguration = compositionLocalOf<Configuration>(
    neverEqualPolicy()
) {
    noLocalProvidedFor("LocalConfiguration")
}

/**
 * Provides a [Context] that can be used by Android applications.
 */
val LocalContext = staticCompositionLocalOf<Context> {
    noLocalProvidedFor("LocalContext")
}
    :
	:

※Dynamic側とStatic側の違いは「Jetpack Compose:staticCompositionLocalOfの違い」を参照

スポンサーリンク

例:LocalContext

サンプルはStringリソースから文字列を取得して、地域に適した言語で表示する例です。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">LocalContext Sample</string>
    <string name="greeting">こんにちは アンドロイド!</string>
</resources>
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting()
                }
            }
        }
    }
}

@Composable
fun Greeting(modifier: Modifier = Modifier) {
    val _text = LocalContext.current.resources.getString(R.string.greeting)
    Text(
        text = _text,
        modifier = modifier
    )
}

LocalContextのサンプル

※注意:端末のLanguagesを「日本語(日本)」にしてください。

スポンサーリンク

例:LocalConfiguration

Configurationは端末の構成の変更を管理するシステムです。

構成の変更とは、例えば端末の回転(Orientation:Landscope⇔Portrait)などです。

通常、構成の変更が行われると、Activityの再作成が行われます。つまり、旧構成のActivityインスタンスは閉じられ、新構成のActivityインスタンスが作成されます。

ただし、ユーザによりActivtyの再作成の抑止(AndoridManifest.xmkへ追記)が可能です。

サンプルは端末の回転が行わた時にActivityの再作成を抑止して、Orientationが変わったことによる画面の表示切替をプログラムに委ねる例です。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <application ...>
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.MyApplication">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    ElementA()
                }
            }
        }
    }
}

@Composable
fun ElementA() {
    ElementB()
}
@Composable
fun ElementB() {
    ElementC()
}
@Composable
fun ElementC() {
    val _text = when(LocalConfiguration.current.orientation) {
        Configuration.ORIENTATION_LANDSCAPE -> "Landscape !"
        Configuration.ORIENTATION_PORTRAIT -> "Portrait !"
        else -> "Unknown !"
    }
    Text(text = _text)
}

端末の回転

LocalConfigurationの再Compose

※注意:端末のAuto-rotateをOnにしてください。

スポンサーリンク

関連記事:

プロジェクトのビルドで「Something went wrong while checking for version compatibility between the Compose Compiler and the Kotlin Compiler.」とメッセージを吐き、エラーになる場合があります。 既存のプロジェクトを新しくリリースされたAndroid Studioでビルドした場合に頻発します。 先日、「Giraffe|2022.3.1」がリリース(2023.07)されて、早速、ビルドをしたら発生しました。 その対処方法を説明します。 ※環境:Android Studio Giraffe | 2022.3.1 ...
表示の変わらないUI要素(Composable関数)に対して行われる再Composeは無駄な処理です。 ですので、極力排除したいところですが、表示が変わらないため、画面上からの判断が難しくなっています。 このようなとき、Layout Inspectorを利用すると、無駄な再Composeを見つけ出すことが出来ます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1 ...
Jetpack ComposeはAndroidシステムの新たなUIフレームワークです。従来のViewシステムと、アプリ画面の描画の仕組みが異なります。 このJetpack Composeによるアプリ画面の描画について、仕組みの大枠をまとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
Jetpack Composeは描画処理の軽量化(消費リソース量の削減)をするために、表示の変更されたUI要素のみを再Composeし、表示の変わらないUI要素をスキップします。これにより、高い表示パフォーマンスを維持しています。 しかし、スキップが正常に行われないとしても、アプリの画面に現れて来ません。なぜなら、同じ表示を無駄に繰り返すことになるからです。 アプリは動くけれど動作が鈍いならば、真っ先に疑うポイントです。不要な再Composeが行われている可能性が考えられます。 これは気付かないうちに蓄積し易い不具合です。ですので、再Composeとスキップについて理解し、予防に努めることをお勧めします。 今回は「再Composeとスキップ」について、まとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
サンプルアプリを作成して、描画処理(再Compose)の周期を観測してみました。 その結果を紹介します。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
mutableStateOfはComposable関数ではありません。 ですので、Composable関数内にある必要はなく、どこでも記述できます。 Activityから表示の更新を発行する方法として使えそうです。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
状態の保持(remenber)はアプリの画面(UI)を制御・管理するために必要な動作です。 再Composeのスケジューリング(mutableStateOf)に並び、Jetpack Composeの重要な技術の一つです。 今回は「再Composeを超えて状態の保持」について、まとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
再Composeのスケジューリング(mutableStateOf)はアプリのパフォーマンスに直結する動作です。 状態の保持(remember)に並び、Jetpack Composeの重要な技術の一つです。 今回は「再Composeのスケジューリング」について、まとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
CompositionLocalはUIツリーで発生する状態のバケツリレーを解決してくれます。 また、広く共有したい状態の定義にも適しています。例えば、「ツリーのある階層以下に対して」などと言った場合です。 CompositionLocalについて、まとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
CompositionLocalはUIツリーにローカルな変数を確保します。 その変数の参照キーはcompositionLocalOf関数(以降、Dynamic側と呼ぶ)によって返されるCompositionLocalインスタンスです。 このインスタンスを返す方法に、もう一つ、staticCompositionLocalOf関数(以降、Static側と呼ぶ)があります。 この両者の違いをまとめます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
「Android Studio Giraffe」の作成するプロジェクトは、Jetpack Composeの利用が推奨されます。 今後、Viewシステムに代わり、Jetpack Composeが主流になるようです。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
「Android Studio Giraffe」の作成するプロジェクトは、Jetpack Composeの利用が推奨されます。 そして、作成されたプロジェクトは、Material Designeに準拠したテーマが指定されます。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
Android StudioにおけるJetpack Composeプロジェクトは、エディタ上でUIのプレビューが行えます。 Kotlinで記述した画面構成(UIツリー)が視覚的に確認できるので、とても便利です。 さらに、色々な表示条件の設定が行えるので、使いこなせば更に利便性が向上します。 ※この記事の執筆中にドキュメント「コンポーザブルのプレビューで UI をプレビューする」を見つけました。記事はこのドキュメントと重複する部分が多いです。ドキュメントも参考にして下さい。 ※環境:Android Studio Giraffe | 2022.3.1 Patch 3     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
Jetpack Composeが提供する既存のUI要素(Compose UI)は、必ずModifierを引数に持ちます。 このModifierの役割はUI要素へ装飾や機能拡張を追加することですが、裏でアプリ画面の描画処理と密接に関連しており、UI要素よりもシステム側に近い存在です。 理解せずに誤った使い方をすれば、装飾や機能拡張の域を脱してUI要素が表示されないこともあり、思ったようなアプリ画面は望めません。 Modifierについて、まとめます。 ※環境:Android Studio Hedgehog | 2023.1.1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
スポンサーリンク