- Ruby / Rails以外の開発一般
- Ruby / Rails関連
Rails: Hotwire Nativeで作るネイティブモバイルアプリ: Android編(3)ネイティブ画面(翻訳)
Rails: Hotwire Nativeで作るネイティブモバイルアプリ: Android編(3)ネイティブ画面(翻訳)
前回の記事では、パス構成(path configuration)を設定するために必要な手順について説明しました。パス構成は、Hotwire Native iOSとHotwire Native Androidの両方で重要な概念です。これにより、特定のコンテンツをWeb画面、モーダル、またはネイティブ画面として表示できます。重要なのは、これをサーバーから更新することで、アプリストアでデプロイする場合よりも楽に変更をデプロイできることです。
今回は、Hotwire Androidアプリでネイティブ画面をレンダリングします。本記事の執筆時点では、Hotwire Native Android版は移行中の段階にあります。
第1に、Android Viewsを使う方法があります。これは、独自のロジックでマークアップするXMLファイルです。
第2に、SwiftUIに似たJetpack Composeを使う方法があります。
私がAndroidに携わったのは短い期間ですが、これまで出会ったのはほとんどがAndroid Viewsでした。Jetpack Composeがもっと普及するにはもう少し時間がかかりそうです。
私の予測の話はおいといて、本題に入りましょう。
訳注
Railsアプリも以下のiOS編(3)と同様に設定しておく必要があります。
Rails: Hotwire Nativeで作るネイティブモバイルアプリ: iOS編(3)ブリッジコンポーネント(翻訳)
🔗 Webフラグメントを設定する
ネイティブ画面を作成する前に、Hotwire Androidから継承するWebフラグメントを作成する必要があります。これをネイティブコンポーネントの基盤とします。
mainディレクトリでfeatures
という新しいパッケージを作成し、次の2つのフラグメントを作成します。
- 1:
WebFragment
を追加します。
// app/src/main/java/com/example/hotwireexample/WebFragment.kt
package com.example.hotwireexample
import dev.hotwire.navigation.destinations.HotwireDestinationDeepLink
import dev.hotwire.navigation.fragments.HotwireWebFragment
@HotwireDestinationDeepLink(uri = "hotwire://fragment/web")
class WebFragment : HotwireWebFragment() {}
- 2:
WebBottomSheetFragment
を追加します。
// app/src/main/java/com/example/hotwireexample/WebBottomSheetFragment.kt
package com.example.hotwireexample
import dev.hotwire.navigation.destinations.HotwireDestinationDeepLink
import dev.hotwire.navigation.fragments.HotwireWebBottomSheetFragment
@HotwireDestinationDeepLink(uri = "hotwire://fragment/web/modal/sheet")
class WebBottomSheetFragment : HotwireWebBottomSheetFragment() {}
次に、これらのフラグメントをHotwireApplication
に登録する必要があります。defaultFragmentDestination
を設定していることにご注目ください。
// app/src/main/java/com/example/hotwireexample/HotwireApplication.kt
package com.example.hotwireexample.main
import android.app.Application
import dev.hotwire.core.config.Hotwire
import dev.hotwire.core.turbo.config.PathConfiguration
import dev.hotwire.navigation.config.defaultFragmentDestination
import dev.hotwire.navigation.config.registerFragmentDestinations
import com.example.hotwireexample.*
class HotwireApplication: Application() {
override fun onCreate() {
super.onCreate()
configureApp()
}
private fun configureApp() {
Hotwire.loadPathConfiguration(
context = this,
location = PathConfiguration.Location(
assetFilePath = "json/path-configuration.json"
)
)
Hotwire.defaultFragmentDestination = WebFragment::class
Hotwire.registerFragmentDestinations(
WebFragment::class,
WebBottomSheetFragment::class,
)
}
}
この時点でアプリを実行して、すべて順調に動いていることを確認します。
🔗 新規フラグメントをAndroid Viewsで作成する場合
ネイティブ画面をレンダリングするには、以下の3つの作業が必要です。
- パス構成を調整する
- ネイティブフラグメントを作成する
- フラグメントのdestinationを登録する
最も面倒なのはステップ2です。🤣
最初にpath-configuration.json
に以下の新しいルールを追加しましょう。
{
...
{
"patterns": [
"/native"
],
"properties": {
"context": "modal",
"uri": "hotwire://fragment/hello_world",
"pull_to_refresh_enabled": false
}
}
]
}
次に、他のフラグメントと一緒に存在する新しいフラグメントを作成しましょう。
このフラグメントをHelloWorldFragment
と呼ぶことにします。
// app/src/main/java/com/example/hotwireexample/HelloWorldFragment.kt
package com.example.hotwireexample
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import dev.hotwire.navigation.destinations.HotwireDestinationDeepLink
import dev.hotwire.navigation.fragments.HotwireFragment
import com.example.hotwireexample.main.*
@HotwireDestinationDeepLink(uri = "hotwire://fragment/hello_world")
class HelloWorldFragment: HotwireFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.hello_world, container, false)
}
}
次に、hello_world
というレイアウトを以下の内容で作成する必要があります。これはres/layout/
ディレクトリに配置します。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text_view_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
これで、画面中央にTextView
が表示され、そこに"hello world"が表示されます。この"@string/hello_world"
に対応する文字列リソースをアプリで作成しておく必要があるでしょう。
訳注
これを行うには、res/values/strings.xml
に以下を追加する必要があります。
<resources>
<string name="app_name">Hotwire Example</string>
+ <string name="hello_world">Hello World</string>
</resources>
これで、アプリを実行して/native
に移動すると、真ん中に「Hello World」がレンダリングされるはずです。
Android Viewsは非常に人気がありますが、Jetpack Composeも素晴らしい技術なので、次はJetpack Composeを使う方法を考えてみましょう。
🔗 新規フラグメントをJetpack Composeで作成する場合
🔗 Jetpack Composeコンパイラをインストールする
最初に必要なのはJetpack Composeライブラリです。Android Studioのセットアップ時にJetpack Composeを選んでいなかったので、インストールが必要です。
最初に、Compose Compiler Gradle Pluginを設定します。
libs.versions.toml
ファイルで、以下のようにKotlinのバージョンを変更し、Compose Compilerをプラグインに追加します。
[versions]
...
-kotlin = "1.9.24"
+kotlin = "2.0.0"
...
[plugins]
...
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
次に、プロジェクト直下のbuild.gradle.kts
ファイルに以下を追加します。
plugins {
...
alias(libs.plugins.compose.compiler) apply false
}
次に、app/
ディレクトリの下にあるbuild.gradle.kts
ファイルに以下を追加します。
plugins {
...
alias(libs.plugins.compose.compiler)
}
app/
ディレクトリの下にあるbuild.gradle.kts
ファイルで行う最後の手順として、以下のbuildFeatures
を追加します。
android {
...
buildFeatures {
compose = true
}
}
ふう。すべてが同期・ビルド可能であることを確認してからJetpack Compose ライブラリの追加に進みましょう。
🔗 Jetpack Composeをインストールする
アプリ直下のbuild.gradle.kts
ファイルの依存関係に以下を追加します。
dependencies {
implementation(libs.hotwire.core)
val composeBom = platform("androidx.compose:compose-bom:2024.12.01")
implementation(composeBom)
androidTestImplementation(composeBom)
// Material Design 3
implementation("androidx.compose.material3:material3")
// Android Studio Preview support
implementation("androidx.compose.ui:ui-tooling-preview")
debugImplementation("androidx.compose.ui:ui-tooling")
// UI Tests
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}
Android Studioで"Sync Now"が表示されたら必ずクリックしてください。

🔗 Jetpack Compose Componentを初めてビルドする
Jetpack Composeがインストールされたので(これは面倒な作業です)、Jetpack Composeでネイティブ画面を構築する準備が整いました。
HelloWorldFragment
の内容を以下で置き換えてJetpack Composeが使われるようにしましょう。
// app/src/main/java/com/example/hotwireexample/HelloWorldFragment.k
package com.example.hotwireexample
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.ComposeView
import dev.hotwire.navigation.destinations.HotwireDestinationDeepLink
import dev.hotwire.navigation.fragments.HotwireFragment
@HotwireDestinationDeepLink(uri = "hotwire://fragment/hello_world")
class HelloWorldFragment: HotwireFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
Hello()
}
} }
@Composable
fun Hello() {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Hello World from Jetpack compose")
}
}
}
これで、アプリをビルドして実行し、/native
のネイティブ画面に移動すると、すべてAndroid Viewsと同じように動くことがわかります。
🔗 次回
Androidでネイティブ画面を構築し、そこへ移動するさまざまな方法を見てきました。Jetpack ComposeとAndroid Viewsのどちらを使うかはお好み次第です。
ネイティブ画面は、顧客と仕事をしていると最も求められる機能の1つです。ただし、ネイティブ機能にアクセスするには、よりよい方法がある場合もあります。
Javascriptブリッジでデバイスと通信できることをご存知ですか?次回の記事ではこれについて解説します。
それまで、Happy hacking!
概要
元サイトの許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。
従来Turbo NativeとStradaと呼ばれていたものは、現在はHotwire Nativeに統合されました。
参考: Hotwire Native: Hotwire Native is a web-first framework for building native mobile apps.