如何在 Fragment 中使用 Compose?
How to use Compose inside Fragment?
文档描述了如何在 Activity 中创建 UI (Jetpack Compose https://developer.android.com/jetpack/compose)。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text("Hello world!")
}
}
}
但是我如何在片段中使用它呢?
找到了:
class LoginFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val fragmentView = inflater.inflate(R.layout.fragment_login, container, false)
(fragmentView as ViewGroup).setContent {
Hello("Jetpack Compose")
}
return fragmentView
}
@Composable
fun Hello(name: String) = MaterialTheme {
FlexColumn {
inflexible {
// Item height will be equal content height
TopAppBar( // App Bar with title
title = { Text("Jetpack Compose Sample") }
)
}
expanded(1F) {
// occupy whole empty space in the Column
Center {
// Center content
Text("Hello $name!") // Text label
}
}
}
}
}
ViewGroup
上的 setContent
现已弃用。
以下内容截至 Compose v1.0.0-alpha01.
是准确的
纯写UIFragment
:
class ComposeUIFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return ComposeView(requireContext()).apply {
setContent {
Text(text = "Hello world.")
}
}
}
}
对于混合组合 UI Fragment
- 添加 ComposeView
到 xml 布局,然后:
class ComposeUIFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_compose_ui, container, false).apply {
findViewById<ComposeView>(R.id.composeView).setContent {
Text(text = "Hello world.")
}
}
}
}
您不需要 Fragments with Compose。您可以导航到另一个屏幕而无需 Fragment 或 Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
NavHost(navController, startDestination = "welcome") {
composable("welcome") { WelcomeScreen(navController) }
composable("secondScreen") { SecondScreen() }
}
}
}
}
@Composable
fun WelcomeScreen(navController: NavController) {
Column {
Text(text = "Welcome!")
Button(onClick = { navController.navigate("secondScreen") }) {
Text(text = "Continue")
}
}
}
@Composable
fun SecondScreen() {
Text(text = "Second screen!")
}
使用 1.0.x
你可以:
- 在 xml-layout 中定义一个 ComposeView。
- 在您的布局中添加一个
androidx.compose.ui.platform.ComposeView
-xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
...>
<TextView ../>
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
- 然后使用XML ID获取
ComposeView
,设置一个Composition strategy并调用setContent()
:
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentExampleBinding.inflate(inflater, container, false)
val view = binding.root
view.composeView.apply {
// Dispose the Composition when viewLifecycleOwner is destroyed
setViewCompositionStrategy(
DisposeOnLifecycleDestroyed(viewLifecycleOwner)
)
setContent {
// In Compose world
MaterialTheme {
Text("Hello Compose!")
}
}
}
return view
}
/** ... */
}
- 直接在片段中包含一个 ComposeView
。
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
// Dispose the Composition when viewLifecycleOwner is destroyed
setViewCompositionStrategy(
DisposeOnLifecycleDestroyed(viewLifecycleOwner)
)
setContent {
MaterialTheme {
// In Compose world
Text("Hello Compose!")
}
}
}
}
}
在我看来,如果您想像这样以漂亮的方式将 Jetpack Compose 与片段一起使用
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
) = contentView {
Text("Hello world")
}
您可以为片段创建自己的扩展函数
fun Fragment.requireContentView(
compositionStrategy: ViewCompositionStrategy = DisposeOnDetachedFromWindow,
context: Context = requireContext(),
content: @Composable () -> Unit
): ComposeView {
val view = ComposeView(context)
view.setViewCompositionStrategy(compositionStrategy)
view.setContent(content)
return view
}
fun Fragment.contentView(
compositionStrategy: ViewCompositionStrategy = DisposeOnDetachedFromWindow,
context: Context? = getContext(),
content: @Composable () -> Unit
): ComposeView? {
context ?: return null
val view = ComposeView(context)
view.setViewCompositionStrategy(compositionStrategy)
view.setContent(content)
return view
}
我喜欢这种方法,因为它看起来类似于 Activity 的 setContent { }
扩展
您还可以定义另一个 CompositionStrategy
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
) = contentView(DisposeOnLifecycleDestroyed(viewLifecycleOwner)) {
Text("Hello world")
}
文档描述了如何在 Activity 中创建 UI (Jetpack Compose https://developer.android.com/jetpack/compose)。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text("Hello world!")
}
}
}
但是我如何在片段中使用它呢?
找到了:
class LoginFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val fragmentView = inflater.inflate(R.layout.fragment_login, container, false)
(fragmentView as ViewGroup).setContent {
Hello("Jetpack Compose")
}
return fragmentView
}
@Composable
fun Hello(name: String) = MaterialTheme {
FlexColumn {
inflexible {
// Item height will be equal content height
TopAppBar( // App Bar with title
title = { Text("Jetpack Compose Sample") }
)
}
expanded(1F) {
// occupy whole empty space in the Column
Center {
// Center content
Text("Hello $name!") // Text label
}
}
}
}
}
ViewGroup
上的 setContent
现已弃用。
以下内容截至 Compose v1.0.0-alpha01.
是准确的纯写UIFragment
:
class ComposeUIFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return ComposeView(requireContext()).apply {
setContent {
Text(text = "Hello world.")
}
}
}
}
对于混合组合 UI Fragment
- 添加 ComposeView
到 xml 布局,然后:
class ComposeUIFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_compose_ui, container, false).apply {
findViewById<ComposeView>(R.id.composeView).setContent {
Text(text = "Hello world.")
}
}
}
}
您不需要 Fragments with Compose。您可以导航到另一个屏幕而无需 Fragment 或 Activity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
NavHost(navController, startDestination = "welcome") {
composable("welcome") { WelcomeScreen(navController) }
composable("secondScreen") { SecondScreen() }
}
}
}
}
@Composable
fun WelcomeScreen(navController: NavController) {
Column {
Text(text = "Welcome!")
Button(onClick = { navController.navigate("secondScreen") }) {
Text(text = "Continue")
}
}
}
@Composable
fun SecondScreen() {
Text(text = "Second screen!")
}
使用 1.0.x
你可以:
- 在 xml-layout 中定义一个 ComposeView。
- 在您的布局中添加一个
androidx.compose.ui.platform.ComposeView
-xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
...>
<TextView ../>
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
- 然后使用XML ID获取
ComposeView
,设置一个Composition strategy并调用setContent()
:
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentExampleBinding.inflate(inflater, container, false)
val view = binding.root
view.composeView.apply {
// Dispose the Composition when viewLifecycleOwner is destroyed
setViewCompositionStrategy(
DisposeOnLifecycleDestroyed(viewLifecycleOwner)
)
setContent {
// In Compose world
MaterialTheme {
Text("Hello Compose!")
}
}
}
return view
}
/** ... */
}
- 直接在片段中包含一个 ComposeView
。
class ExampleFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
// Dispose the Composition when viewLifecycleOwner is destroyed
setViewCompositionStrategy(
DisposeOnLifecycleDestroyed(viewLifecycleOwner)
)
setContent {
MaterialTheme {
// In Compose world
Text("Hello Compose!")
}
}
}
}
}
在我看来,如果您想像这样以漂亮的方式将 Jetpack Compose 与片段一起使用
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
) = contentView {
Text("Hello world")
}
您可以为片段创建自己的扩展函数
fun Fragment.requireContentView(
compositionStrategy: ViewCompositionStrategy = DisposeOnDetachedFromWindow,
context: Context = requireContext(),
content: @Composable () -> Unit
): ComposeView {
val view = ComposeView(context)
view.setViewCompositionStrategy(compositionStrategy)
view.setContent(content)
return view
}
fun Fragment.contentView(
compositionStrategy: ViewCompositionStrategy = DisposeOnDetachedFromWindow,
context: Context? = getContext(),
content: @Composable () -> Unit
): ComposeView? {
context ?: return null
val view = ComposeView(context)
view.setViewCompositionStrategy(compositionStrategy)
view.setContent(content)
return view
}
我喜欢这种方法,因为它看起来类似于 Activity 的 setContent { }
扩展
您还可以定义另一个 CompositionStrategy
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
) = contentView(DisposeOnLifecycleDestroyed(viewLifecycleOwner)) {
Text("Hello world")
}