Compose에서 뷰 사용

Compose UI에 Android 뷰 계층 구조를 포함할 수 있습니다. 이 접근 방식은 AdView와 같이 Compose에서 아직 사용할 수 없는 UI 요소를 사용하려는 경우에 특히 유용합니다. 이 접근 방식을 사용하면 직접 디자인한 맞춤 뷰를 재사용할 수도 있습니다.

뷰 요소 또는 계층 구조를 포함하려면 AndroidView 있습니다. AndroidView에는 View를 반환하는 람다가 전달됩니다. 또한 AndroidView는 뷰가 확장될 때 호출되는 update 콜백도 제공합니다. AndroidView는 콜백 내에서 읽은 State가 변경될 때마다 재구성됩니다. 여타 기본 제공 컴포저블과 마찬가지로 AndroidView는 상위 컴포저블에서 위치를 설정하는 등의 목적으로 사용할 수 있는 Modifier 매개변수를 취합니다.

@Composable
fun CustomView() {
    var selectedItem by remember { mutableStateOf(0) }

    // Adds view to Compose
    AndroidView(
        modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
        factory = { context ->
            // Creates view
            MyView(context).apply {
                // Sets up listeners for View -> Compose communication
                setOnClickListener {
                    selectedItem = 1
                }
            }
        },
        update = { view ->
            // View's been inflated or state read in this block has been updated
            // Add logic here if necessary

            // As selectedItem is read here, AndroidView will recompose
            // whenever the state changes
            // Example of Compose -> View communication
            view.selectedItem = selectedItem
        }
    )
}

@Composable
fun ContentExample() {
    Column(Modifier.fillMaxSize()) {
        Text("Look at this CustomView!")
        CustomView()
    }
}

뷰 결합이 있는 AndroidView

XML 레이아웃을 삽입하려면 androidx.compose.ui:ui-viewbinding 라이브러리에서 제공하는 AndroidViewBinding API를 사용합니다. 그렇게 하려면 프로젝트에서 뷰 결합을 사용 설정해야 합니다.

@Composable
fun AndroidViewBindingExample() {
    AndroidViewBinding(ExampleLayoutBinding::inflate) {
        exampleView.setBackgroundColor(Color.GRAY)
    }
}

지연 목록의 AndroidView

지연 목록 (LazyColumn, LazyRow,AndroidView Pager 등)의 경우 AndroidView 사용을 고려해 보세요. 버전 1.4.0-rc01에 도입된 오버로드 이 오버로드를 사용하면 Compose에서 포함된 컴포지션이 있는 그대로 재사용되는 경우 기본 View 인스턴스 Lazy 목록의 경우에 해당합니다.

AndroidView의 이 오버로드는 매개변수 2개를 추가합니다.

  • onReset: View가 재사용됨 뷰 재사용을 사용 설정하려면 null이 아니어야 합니다.
  • onRelease (선택사항): View가 컴포지션을 종료하고 다시 사용되지 않습니다.

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun AndroidViewInLazyList() {
    LazyColumn {
        items(100) { index ->
            AndroidView(
                modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
                factory = { context ->
                    MyView(context)
                },
                update = { view ->
                    view.selectedItem = index
                },
                onReset = { view ->
                    view.clear()
                }
            )
        }
    }
}

Compose의 프래그먼트

AndroidViewBinding 컴포저블을 사용하여 Compose에 Fragment를 추가합니다. AndroidViewBinding에는 컴포저블이 컴포지션을 종료할 때 프래그먼트를 삭제하는 등 프래그먼트별 처리 기능이 있습니다.

이렇게 하려면 FragmentContainerView가 포함된 XML을 Fragment의 홀더로 확장하면 됩니다.

예를 들어 my_fragment_layout.xml을 정의한 경우 android:name XML 속성을 Fragment의 클래스 이름으로 대체하면서 다음과 같은 코드를 사용할 수 있습니다.

<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="com.example.compose.snippets.interop.MyFragment" />

다음과 같이 Compose에서 이 프래그먼트를 확장합니다.

@Composable
fun FragmentInComposeExample() {
    AndroidViewBinding(MyFragmentLayoutBinding::inflate) {
        val myFragment = fragmentContainerView.getFragment<MyFragment>()
        // ...
    }
}

동일한 레이아웃에서 여러 프래그먼트를 사용해야 하는 경우 각 FragmentContainerView에 고유 ID를 정의했는지 확인하세요.

Compose에서 Android 프레임워크 호출

Compose는 Android 프레임워크 클래스 내에서 작동합니다. 예를 들어 Activity 또는 Fragment와 같은 Android 뷰 클래스에서 호스팅되며 Context 또는 시스템 리소스, Service, BroadcastReceiver 등의 Android 프레임워크 클래스를 사용할 수 있습니다.

시스템 리소스에 관한 자세한 내용은 Compose의 리소스를 참고하세요.

컴포지션 로컬

CompositionLocal 클래스를 사용하면 구성 가능한 함수를 통해 암시적으로 데이터를 전달할 수 있습니다. 일반적으로 이 클래스는 UI 트리의 특정 노드의 값과 함께 제공됩니다. 이 값은 구성 가능한 함수에서 CompositionLocal을 매개변수로 선언하지 않아도 구성 가능한 하위 요소에 사용할 수 있습니다.

CompositionLocal은 Compose에서 Context, Configuration 또는 View 같은 Android 프레임워크 유형의 값을 전파하는 데 사용됩니다. 이러한 프레임워크에서 Compose 코드가 해당 LocalContext, LocalConfiguration 또는 LocalView와 함께 호스팅됩니다. IDE에서 자동 완성 기능을 사용할 때 검색 가능성을 높이기 위해 CompositionLocal 클래스에 Local 접두사가 지정됩니다.

current 속성을 사용하여 CompositionLocal의 현재 값에 액세스합니다. 예를 들어 아래 코드는 Toast.makeToast 메서드에 LocalContext.current를 제공하여 토스트 메시지를 표시합니다.

@Composable
fun ToastGreetingButton(greeting: String) {
    val context = LocalContext.current
    Button(onClick = {
        Toast.makeText(context, greeting, Toast.LENGTH_SHORT).show()
    }) {
        Text("Greet")
    }
}

더욱 자세한 예시는 이 문서 끝에 있는 우수사례: BroadcastReceivers 섹션을 참고하세요.

기타 상호작용

필요한 상호작용에 관해 정의된 유틸리티가 없다면 일반적인 Compose 가이드라인인 데이터는 아래로 흐르고 이벤트는 위로 흐름을 따르는 것이 좋습니다(Compose 이해에서 더 자세히 논의). 예를 들어 다음 컴포저블은 다른 활동을 시작합니다.

class OtherInteractionsActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // get data from savedInstanceState
        setContent {
            MaterialTheme {
                ExampleComposable(data, onButtonClick = {
                    startActivity(Intent(this, MyActivity::class.java))
                })
            }
        }
    }
}

@Composable
fun ExampleComposable(data: DataExample, onButtonClick: () -> Unit) {
    Button(onClick = onButtonClick) {
        Text(data.title)
    }
}

우수사례: Broadcast receiver

Compose에 이전하거나 구현하고자 하는 기능과 관련해 보다 실질적인 예를 보여주고 CompositionLocal부수 효과를 보여주기 위해 구성 가능한 함수에서 BroadcastReceiver를 등록해야 한다고 가정하겠습니다.

이 방법에서는 LocalContext를 이용해 현재 컨텍스트와 rememberUpdatedStateDisposableEffect 부수 효과를 사용합니다.

@Composable
fun SystemBroadcastReceiver(
    systemAction: String,
    onSystemEvent: (intent: Intent?) -> Unit
) {
    // Grab the current context in this part of the UI tree
    val context = LocalContext.current

    // Safely use the latest onSystemEvent lambda passed to the function
    val currentOnSystemEvent by rememberUpdatedState(onSystemEvent)

    // If either context or systemAction changes, unregister and register again
    DisposableEffect(context, systemAction) {
        val intentFilter = IntentFilter(systemAction)
        val broadcast = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                currentOnSystemEvent(intent)
            }
        }

        context.registerReceiver(broadcast, intentFilter)

        // When the effect leaves the Composition, remove the callback
        onDispose {
            context.unregisterReceiver(broadcast)
        }
    }
}

@Composable
fun HomeScreen() {

    SystemBroadcastReceiver(Intent.ACTION_BATTERY_CHANGED) { batteryStatus ->
        val isCharging = /* Get from batteryStatus ... */ true
        /* Do something if the device is charging */
    }

    /* Rest of the HomeScreen */
}

다음 단계

뷰에서 Compose를 사용할 때 또는 그 반대의 경우에 상호 운용성 API를 알아봤습니다. 자세한 내용은 기타 고려사항 페이지를 참고하세요.