「新しいプロジェクト、ComposeとXMLどっちで始めるべき?」
Android開発者なら一度は悩む問題です。
Googleは2021年にJetpack Composeを正式リリースし、「これからのAndroid UI開発の標準」と位置づけています。しかし、XMLも依然として広く使われています。
この記事では、両者を様々な観点から比較し、どちらを選ぶべきかの判断材料を提供します。
結論から言うと
| 状況 | おすすめ |
|---|---|
| 新規プロジェクト | Compose |
| 既存プロジェクトの保守 | XML(そのまま) |
| 既存プロジェクトに新機能追加 | Compose(段階的導入) |
| 学習目的 | 両方(Compose優先) |
| チームにCompose経験者がいない | XML(短期)/ Compose(中長期) |
では、詳しく見ていきましょう。
Jetpack Composeとは
Composeは、Kotlinで宣言的にUIを記述するモダンなUIツールキットです。
Composeの基本コード
@Composable
fun Greeting(name: String) {
Text(
text = "Hello, $name!",
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(16.dp)
)
}
@Composable
fun UserCard(user: User) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
AsyncImage(
model = user.avatarUrl,
contentDescription = null,
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
)
Spacer(modifier = Modifier.width(16.dp))
Column {
Text(user.name, fontWeight = FontWeight.Bold)
Text(user.email, color = Color.Gray)
}
}
}
}
特徴
- 宣言的UI: 「どう見えるか」を記述
- Kotlinオンリー: XMLファイル不要
- 状態管理: UIと状態が自動で同期
- プレビュー: Android Studio上でリアルタイムプレビュー
XML(View System)とは
従来のAndroid UI開発方式。XMLでレイアウトを定義し、Kotlin/Javaでロジックを書きます。
XMLの基本コード
<!-- activity_main.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/greeting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp" />
</LinearLayout>
// MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.greeting.text = "Hello, World!"
}
}
特徴
- 命令的UI: 「どう変更するか」を記述
- XMLとKotlin分離: レイアウトとロジックが別ファイル
- ViewBinding/DataBinding: 型安全なView参照
- 豊富な実績: 10年以上の歴史
徹底比較
1. コード量
同じUIを実装した場合のコード量を比較します。
例:ユーザーリスト画面
Compose
@Composable
fun UserListScreen(users: List<User>) {
LazyColumn {
items(users) { user ->
UserItem(user)
}
}
}
@Composable
fun UserItem(user: User) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(user.name)
Spacer(Modifier.weight(1f))
Text(user.email)
}
}
→ 約20行
XML + Kotlin
<!-- fragment_user_list.xml -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- item_user.xml -->
<LinearLayout ...>
<TextView android:id="@+id/name" ... />
<TextView android:id="@+id/email" ... />
</LinearLayout>
// UserAdapter.kt
class UserAdapter : ListAdapter<User, UserViewHolder>(...) {
override fun onCreateViewHolder(...): UserViewHolder { ... }
override fun onBindViewHolder(...) { ... }
}
// UserViewHolder.kt
class UserViewHolder(binding: ItemUserBinding) : RecyclerView.ViewHolder(...) {
fun bind(user: User) { ... }
}
// UserListFragment.kt
class UserListFragment : Fragment() {
override fun onViewCreated(...) {
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(context)
}
}
→ 約60〜80行(複数ファイル)
結論: Composeの方が圧倒的に少ないコード量
2. 学習コスト
| 観点 | Compose | XML |
|---|---|---|
| Kotlin知識 | 必須(上級) | 基本でOK |
| 新しい概念 | 多い | 少ない |
| 学習リソース | 増加中 | 豊富 |
| 既存知識の活用 | React等の経験が活きる | Android経験が活きる |
Composeで学ぶべき新概念
- Composable関数
- 状態管理(remember、State)
- 再コンポジション
- Modifier
- Side Effects
結論: 短期的にはXMLが楽、長期的にはCompose
3. パフォーマンス
| 観点 | Compose | XML |
|---|---|---|
| 初期表示 | やや遅い | 速い |
| 更新処理 | 速い | 場合による |
| メモリ使用量 | 同等〜やや多い | 基準 |
| アニメーション | 最適化されている | 実装次第 |
Composeのパフォーマンス改善ポイント
// ❌ 非効率:毎回再コンポーズ
@Composable
fun UserList(users: List<User>) {
users.forEach { user ->
UserItem(user)
}
}
// ✅ 効率的:LazyColumnで必要な分だけ
@Composable
fun UserList(users: List<User>) {
LazyColumn {
items(users, key = { it.id }) { user ->
UserItem(user)
}
}
}
結論: 適切に実装すれば両者とも十分なパフォーマンス
4. 状態管理
Compose
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Count: $count")
}
}
UIと状態が自動で同期。countが変わると自動で再描画。
XML
class CounterFragment : Fragment() {
private var count = 0
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.button.setOnClickListener {
count++
binding.button.text = "Count: $count" // 手動で更新
}
}
}
状態変更のたびに手動でViewを更新する必要がある。
結論: Composeの方が状態管理がシンプル
5. テスト
Compose
@Test
fun counterTest() {
composeTestRule.setContent {
Counter()
}
composeTestRule.onNodeWithText("Count: 0").assertExists()
composeTestRule.onNodeWithText("Count: 0").performClick()
composeTestRule.onNodeWithText("Count: 1").assertExists()
}
XML
@Test
fun counterTest() {
val scenario = launchFragmentInContainer<CounterFragment>()
onView(withId(R.id.button)).check(matches(withText("Count: 0")))
onView(withId(R.id.button)).perform(click())
onView(withId(R.id.button)).check(matches(withText("Count: 1")))
}
結論: どちらもテスト可能だが、Composeの方がセマンティック
6. ツールサポート
| 機能 | Compose | XML |
|---|---|---|
| プレビュー | @Preview | Layout Editor |
| ホットリロード | Live Edit | Apply Changes |
| コード補完 | ◎ | ◎ |
| リファクタリング | ◎ | ◎ |
| デバッグツール | Layout Inspector | Layout Inspector |
Composeのプレビュー
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
Greeting("Android")
}
@Preview(name = "Dark Mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun GreetingDarkPreview() {
Greeting("Android")
}
結論: ほぼ同等、好みの問題
7. ライブラリ・エコシステム
| カテゴリ | Compose | XML |
|---|---|---|
| UI コンポーネント | Material 3対応 | Material Design |
| 画像読み込み | Coil推奨 | Glide, Picasso |
| ナビゲーション | Navigation Compose | Navigation Component |
| DI | Hilt対応 | Hilt対応 |
| サードパーティ | 増加中 | 豊富 |
結論: XMLの方がまだ選択肢が多いが、Composeも急速に充実
8. 既存プロジェクトとの互換性
Composeは既存のXMLプロジェクトに段階的に導入できます。
XMLの中にComposeを埋め込む
<androidx.compose.ui.platform.ComposeView
android:id="@+id/composeView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
binding.composeView.setContent {
MaterialTheme {
NewFeatureScreen()
}
}
Composeの中にXMLを埋め込む
@Composable
fun LegacyViewInCompose() {
AndroidView(
factory = { context ->
LegacyCustomView(context)
},
update = { view ->
view.updateData(data)
}
)
}
結論: 共存可能、段階的移行ができる
比較表まとめ
| 観点 | Compose | XML | 勝者 |
|---|---|---|---|
| コード量 | 少ない | 多い | Compose |
| 学習コスト(短期) | 高い | 低い | XML |
| 学習コスト(長期) | 投資価値あり | 将来性△ | Compose |
| パフォーマンス | ◎ | ◎ | 引き分け |
| 状態管理 | シンプル | 複雑 | Compose |
| テスト | 書きやすい | 書きやすい | 引き分け |
| ツールサポート | ◎ | ◎ | 引き分け |
| ライブラリ | 増加中 | 豊富 | XML |
| 既存資産活用 | 段階的移行可 | そのまま使える | XML |
| 将来性 | ◎ | △ | Compose |
どちらを選ぶべきか
Composeを選ぶべき場合
✅ 新規プロジェクトを始める
✅ モダンな開発体験を求めている
✅ 宣言的UIに慣れている(React, Flutter, SwiftUI等)
✅ チームがKotlinに習熟している
✅ 長期的なプロジェクト
✅ UI/UXに力を入れたいアプリ
XMLを選ぶべき場合
✅ 既存の大規模プロジェクトを保守する
✅ チームにCompose経験者がいない(短期プロジェクト)
✅ 特定のライブラリがCompose未対応
✅ 古いAndroidバージョンのサポートが必須(API 21未満)
✅ 学習時間が取れない緊急案件
段階的移行がおすすめの場合
✅ 既存プロジェクトに新機能を追加する
✅ チームにComposeを学ばせたい
✅ 時間をかけてモダン化したい
私のおすすめ
2026年現在、新規プロジェクトならComposeを強くおすすめします。
理由:
- Googleの公式推奨: 今後のAndroid開発の標準
- 開発効率: 慣れれば圧倒的に速い
- コード品質: 宣言的UIでバグが減る
- 将来性: 新機能はCompose優先で提供される
ただし、学習コストはあります。最初の1〜2ヶ月は生産性が落ちることを覚悟してください。でも、その投資は必ず返ってきます。
学習リソース
Compose
| リソース | 特徴 |
|---|---|
| 公式チュートリアル | 基本を学ぶ |
| Compose Pathway | 体系的な学習 |
| Now in Android | 実践的なサンプル |
XML
| リソース | 特徴 |
|---|---|
| 公式ガイド | 基本を学ぶ |
| Android Basics | 初心者向け |
まとめ
| 状況 | 選択 |
|---|---|
| 新規プロジェクト | Compose |
| 既存プロジェクト保守 | XML |
| 新機能追加 | Compose(段階的導入) |
| 学習 | 両方(Compose優先) |
迷ったらComposeを選んでください。
Android開発の未来はComposeにあります。早く始めれば、それだけ早くメリットを享受できます。
この記事が、技術選定の参考になれば幸いです。

