だいぶ沼ったのでメモ。
動機
筆者はAndroid初心者で,Viewをコネコネしたことがない。 Composeで済ませられるなら全てComposeで済ませたい。
というわけで,InputMethodService
のViewをComposeで作成した。
問題
StackOverflowやGitHub code searchを頼りに,以下のようなコードを書いた。 が,動かなかった。(´・_・`)
package net.dyama.hogehoge
import android.inputmethodservice.InputMethodService
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.ViewTreeLifecycleOwner
@Composable
fun Keyboard() {
Box {
Text("hello!")
}
}
class HogeIME: InputMethodService(), LifecycleOwner { //
private val view by lazy {
ComposeView(this).apply {
setContent {
Keyboard()
}
}
}
override fun onCreate() { //
super.onCreate() //
ViewTreeLifecycleOwner.set(view, this) //
} //
// InputMethodService
override fun onCreateInputView() = view
// LifecycleOwner //
private val lifecycle = LifecycleRegistry(this) //
override fun getLifecycle() = lifecycle //
}
E/AndroidRuntime: FATAL EXCEPTION: main
Process: net.dyama.hogehoge, PID: xxxxx
java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from android.widget.LinearLayout{xxxxxxx... android:id/parentPanel}
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer(WindowRecomposer.android.kt:349)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer$default(WindowRecomposer.android.kt:324)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion$LifecycleAware$1.createRecomposer(WindowRecomposer.android.kt:168)
at androidx.compose.ui.platform.WindowRecomposerPolicy.createAndInstallWindowRecomposer$ui_release(WindowRecomposer.android.kt:224)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.getWindowRecomposer(WindowRecomposer.android.kt:299)
at androidx.compose.ui.platform.AbstractComposeView.resolveParentCompositionContext(ComposeView.android.kt:242)
at androidx.compose.ui.platform.AbstractComposeView.ensureCompositionCreated(ComposeView.android.kt:249)
at androidx.compose.ui.platform.AbstractComposeView.onAttachedToWindow(ComposeView.android.kt:281)
at android.view.View.dispatchAttachedToWindow(View.java:21290)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3491)
at android.view.ViewGroup.addViewInner(ViewGroup.java:5291)
at android.view.ViewGroup.addView(ViewGroup.java:5077)
at android.view.ViewGroup.addView(ViewGroup.java:5049)
at android.inputmethodservice.InputMethodService.setInputView(InputMethodService.java:2242)
at android.inputmethodservice.InputMethodService.updateInputViewShown(InputMethodService.java:2077)
at android.inputmethodservice.InputMethodService.prepareWindow(InputMethodService.java:2646)
at android.inputmethodservice.InputMethodService.showWindow(InputMethodService.java:2575)
at android.inputmethodservice.InputMethodService$InputMethodImpl.showSoftInput(InputMethodService.java:923)
at android.inputmethodservice.InputMethodService$InputMethodImpl.showSoftInputWithToken(InputMethodService.java:897)
at android.inputmethodservice.IInputMethodWrapper.executeMessage(IInputMethodWrapper.java:232)
at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:44)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
半泣きになりながらしばらくゴニョゴニョして,以下のことがわかった。
AbstractComposeView
(ConposeView
の継承元)はいろんなところでCompositionContext
というものを使っているAbstractComposeView
は,Viewのツリーを探し回ってもCompositionContext
が存在しないとき,Recomposer
(implements CompositionContext
)を,WindowRecomposerPolicy.createAndInstallWindowRecomposer(rootView)
によって生成するcreateAndInstallWindowRecomposer
がcreateLifecycleAwareWindowRecomposer
を呼び出し,rootView
のViewTreeLifecycleOwner
を探す → nullViewTreeLifecycleOwner.get(view.rootView)
→ 非null- なんで?
後日談。と言うか、今回のオチ。
今の最新のリリース版のライブラリを使っているのだが,ここら辺は絶賛改築中らしく,次のBetaリリースを導入するとコンパイルすら通らなくなってしまった。
というわけで,WindowRecomposer.android.kt
を参考に自分でRecomposerを生やした。
そして,エラー内容が変わった(!!!!)ので,その他の修正も加えた。
ライブラリのバージョン
version catalog (settings.gradle.kts
)
versionCatalogs {
create("libs") {
// versions
version("android-plugin", "7.4.1")
version("kotlin", "1.7.20")
version("compose", "1.3.2")
// libraries
library("androidx-core", "androidx.core:core-ktx:1.9.0")
library("androidx-lifecycle-runtime", "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
library("androidx-activity-compose", "androidx.activity:activity-compose:1.6.1")
library("androidx-compose-ui", "androidx.compose.ui", "ui").versionRef("compose")
library("androidx-compose-ui-tooling-preview", "androidx.compose.ui", "ui-tooling-preview").versionRef("compose")
library("androidx-compose-material3", "androidx.compose.material3:material3:1.0.1")
library("androidx-datastore", "androidx.datastore:datastore-preferences:1.0.0")
library("junit", "junit:junit:4.13.2")
library("androidx-test-ext-junit", "androidx.test.ext:junit:1.1.5")
library("androidx-test-espresso-core", "androidx.test.espresso:espresso-core:3.5.1")
library("androidx-compose-ui-test-junit4", "androidx.compose.ui", "ui-test-junit4").versionRef("compose")
library("androidx-compose-ui-tooling", "androidx.compose.ui", "ui-tooling").versionRef("compose")
library("androidx-compose-ui-test-manifest", "androidx.compose.ui", "ui-test-manifest").versionRef("compose")
// plugins
plugin("android-application", "com.android.application").versionRef("android.plugin")
plugin("android-library", "com.android.library").versionRef("android.plugin")
plugin("kotlin-android", "org.jetbrains.kotlin.android").versionRef("kotlin")
}
}
dependencies (app/build.gradle.kts
)
dependencies {
implementation(libs.androidx.core)
implementation(libs.androidx.lifecycle.runtime)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.datastore)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.androidx.test.espresso.core)
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
}
参考
問題が解決した後に本質情報を見つけた。