如何在 Fragment 中使用 Compose?

Posted

技术标签:

【中文标题】如何在 Fragment 中使用 Compose?【英文标题】:How to use Compose inside Fragment? 【发布时间】:2020-04-09 14:37:24 【问题描述】:

文档描述了如何在 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!")
    

但是我如何在片段中使用它呢?

【问题讨论】:

请编辑您的问题以提供一个可重复的最小示例。到目前为止,您的问题还很模糊,很难看出您在做什么以及问题出在哪里。 如果我不确定我是否得到你,请在你的活动中使用框架布局,片段有自己的生命周期和你可以使用的 xml 文件,这里是官方文档链接developer.android.com/guide/components/fragments 好的,我给你一个赞,现在享受代码..我已经发布了代码.. 【参考方案1】:

ViewGroup 上的setContent 现在已弃用。

从 Compose v1.0.0-alpha01 开始,以下是准确的。

对于纯撰写 UI Fragment

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.")
            
        
    

【讨论】:

setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed) 此外,在执行此操作时应审查组合策略。 developer.android.com/jetpack/compose/interop/… 如何获取Fragment中的NavController?【参考方案2】:

Compose 不需要 Fragments。您无需 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!")


【讨论】:

虽然这是真的,但它不会使将片段用作主干的用例无效,这可能是迁移代码库的常见场景。所以这并不能回答问题。 虽然这是可能的,但在具有多个屏幕的应用中是不可行的。 @JasonCrosby 我不明白你为什么不在有很多屏幕的应用程序中使用它。是的,NavHost 会增长,但您可以轻松地将其提取到单独的文件中,这样您的主要活动将保持精简。 将所有的撰写代码放在一个活动中最终会导致数千行的活动。也许更多。 是的。这就是为什么在现实世界中,您实际上不应该像我的示例中那样编写代码。每个屏幕(如WelcomeScreenSecondScreen)都应该在自己的文件中,而不是在 MainActivity.kt 中。同样适用于导航。【参考方案3】:

找到了:

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
            
        
    
 

【讨论】:

可以分享一下fragment_login.xml的内容吗? 已弃用(在 alpha12 上测试)【参考方案4】:

使用1.0.x,您可以:

- 在 xml-layout 中定义 ComposeView。

在您的 layout-xml 文件中添加 androidx.compose.ui.platform.ComposeView
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        android:orientation="vertical"
         ...>
    
        <TextView ../>
    
        <androidx.compose.ui.platform.ComposeView
            android:id="@+id/compose_view"
            android:layout_
            android:layout_ />
    
    </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!")
                    
                
            
        
    

【讨论】:

谢谢,setViewCompositionStrategy 解决了我的问题【参考方案5】:

如果你想以这样漂亮的方式使用带有片段的 Jetpack Compose,我想

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
) = contentView 
    Text("Hello world")

您可以为 Fragments 创建自己的扩展函数

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")

【讨论】:

以上是关于如何在 Fragment 中使用 Compose?的主要内容,如果未能解决你的问题,请参考以下文章

jetpack compose 接收返回参数

Jetpack Compose 中的动态加载插件化技术探索

如何在 Fragment l 中使用 WebView? [关闭]

如何在Fragment中使用PreferenceFragment

如何在Fragment中使用PreferenceFragment

如何在 Fragment 中使用 SharedPreferences 保存数据