概要
既存のプロジェクトで開発しているとComposeUIを導入したくてもすでにxmlでレイアウトを組んでいるので導入が難しいと思います。
そんな時に便利なのがxmlとComposeUIを共存させる方法です。
とりあえずComposeUIをやってみたいというプロジェクトには簡単に導入できるのでおすすめです。
ListのところのみComposeUIで実装するイメージを例とします。 |
---|
準備
build.gradleに依存関係を追加するだけです。
その他Composeに必要な依存関係は下記のbuild.gradleを参照してください。
implementation 'androidx.activity:activity-compose:1.4.0'
Activityで実装する場合
レイアウトファイルのComposeUIを実装したいところに<androidx.compose.ui.platform.ComposeView
を追加します。
<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" tools:context=".MainActivity"> <TextView android:id="@+id/header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp" android:text="こんにちは" app:layout_constraintBottom_toTopOf="@+id/compose_view" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.compose.ui.platform.ComposeView android:id="@+id/compose_view" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/footer" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/header" /> <LinearLayout android:id="@+id/footer" android:layout_width="match_parent" android:layout_height="60dp" android:background="@color/purple_200" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/compose_view"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:textSize="26sp" android:gravity="center" android:text="Footer" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
続いてActivityでは、ComposeViewをレイアウトファイルから取得し、setContent(content: @Composable () -> Unit)
にComposeを実装していきます。これだけです。
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) title = "MainActivity" findViewById<ComposeView>(R.id.compose_view).apply { setContent { MainContent() } } } ・・・・
Fragmentで実装する場合
Fragmentの場合もActivityと同様でレイアウトファイルにComposeView
を追加し、
Fragment内でComposeView
を取得し同じように実装します。
class SampleFragment: Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_sample, container, false).apply { findViewById<ComposeView>(R.id.compose_view).apply { // lifecycleに沿ってComposeViewを自動で破棄します(LifecycleOwnerが不明の場合DisposeOnViewTreeLifecycleDestroyed) setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { MainContent() } } } return view } ・・・
ちなみにDataBindingを使う場合
class SampleFragment2 : Fragment() { private var _binding: FragmentSample2Binding? = null private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentSample2Binding.inflate(inflater, container, false) binding.composeView.apply { // lifecycleに沿ってComposeViewを自動で破棄します(LifecycleOwnerが不明の場合DisposeOnViewTreeLifecycleDestroyed) setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { MainContent() } } return binding.root } ・・・
全ソース公開
MainActivity
package com.example.sampleapplication import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Toast import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.unit.dp class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) title = "MainActivity" findViewById<ComposeView>(R.id.compose_view).apply { setContent { MainContent() } } } @Composable fun MainContent() { MaterialTheme { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { repeat(100) { i -> ListItem(i) { Toast.makeText( applicationContext, "Button$i Click!!", Toast.LENGTH_SHORT ).show() } } } } } @Composable fun ListItem(index: Int, onClickListener: () -> Unit) { Button( onClick = { onClickListener() }, modifier = Modifier.padding(10.dp) ) { Text(text = "サンプルボタン$index") } } }
SampleFragment
package com.example.sampleapplication import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.unit.dp import androidx.fragment.app.Fragment class SampleFragment: Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_sample, container, false).apply { findViewById<ComposeView>(R.id.compose_view).apply { // lifecycleに沿ってComposeViewを自動で破棄します(LifecycleOwnerが不明の場合DisposeOnViewTreeLifecycleDestroyed) setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { MainContent() } } } return view } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) requireActivity().title = "SampleFragment" } @Composable fun MainContent() { MaterialTheme { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { repeat(100) { i -> ListItem(i) { Toast.makeText( activity?.applicationContext, "Button$i Click!!", Toast.LENGTH_SHORT ).show() } } } } } @Composable fun ListItem(index: Int, onClickListener: () -> Unit) { Button( onClick = { onClickListener() }, modifier = Modifier.padding(10.dp) ) { Text(text = "サンプルボタン$index") } } }
SampleFragment2
package com.example.sampleapplication import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.unit.dp import androidx.fragment.app.Fragment import com.example.sampleapplication.databinding.FragmentSample2Binding class SampleFragment2 : Fragment() { private var _binding: FragmentSample2Binding? = null private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentSample2Binding.inflate(inflater, container, false) binding.composeView.apply { // lifecycleに沿ってComposeViewを自動で破棄します(LifecycleOwnerが不明の場合DisposeOnViewTreeLifecycleDestroyed) setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { MainContent() } } return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) requireActivity().title = "SampleFragment2" } override fun onDestroyView() { super.onDestroyView() _binding = null } @Composable fun MainContent() { MaterialTheme { Column(modifier = Modifier.verticalScroll(rememberScrollState())) { repeat(100) { i -> ListItem(i) { Toast.makeText( activity?.applicationContext, "Button$i Click!!", Toast.LENGTH_SHORT ).show() } } } } } @Composable fun ListItem(index: Int, onClickListener: () -> Unit) { Button( onClick = { onClickListener() }, modifier = Modifier.padding(10.dp) ) { Text(text = "サンプルボタン$index") } } }
activity_main
<?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" tools:context=".MainActivity"> <TextView android:id="@+id/header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp" android:text="こんにちは" app:layout_constraintBottom_toTopOf="@+id/compose_view" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.compose.ui.platform.ComposeView android:id="@+id/compose_view" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/footer" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/header" /> <LinearLayout android:id="@+id/footer" android:layout_width="match_parent" android:layout_height="60dp" android:background="@color/purple_200" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/compose_view"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:textSize="26sp" android:gravity="center" android:text="Footer" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
fragment_sample
<?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/header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp" android:text="こんにちは" app:layout_constraintBottom_toTopOf="@+id/compose_view" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.compose.ui.platform.ComposeView android:id="@+id/compose_view" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/footer" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/header" /> <LinearLayout android:id="@+id/footer" android:layout_width="match_parent" android:layout_height="60dp" android:background="@color/purple_200" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/compose_view"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="Footer" android:textSize="26sp" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
fragment_sample2
<?xml version="1.0" encoding="utf-8"?> <layout 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"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/header" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp" android:text="こんにちは" app:layout_constraintBottom_toTopOf="@+id/compose_view" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.compose.ui.platform.ComposeView android:id="@+id/compose_view" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toTopOf="@+id/footer" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/header" /> <LinearLayout android:id="@+id/footer" android:layout_width="match_parent" android:layout_height="60dp" android:background="@color/purple_200" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/compose_view"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:textSize="26sp" android:gravity="center" android:text="Footer" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
build.gradle
plugins { id 'com.android.application' id 'kotlin-android' } android { compileSdk 31 defaultConfig { applicationId "com.example.sampleapplication" minSdk 23 targetSdk 31 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } dataBinding { enabled = true } buildFeatures { compose true } composeOptions { kotlinCompilerExtensionVersion = "1.1.0-rc02" } kotlinOptions { jvmTarget = "1.8" } } dependencies { implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.4.0' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.2' implementation 'androidx.compose.ui:ui:1.0.5' implementation 'androidx.activity:activity-compose:1.4.0' implementation 'androidx.compose.material:material:1.0.5' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' }
スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。