如何使用 JetPack Compose 在 LazyColumn 项目中单击导航到详细视图?

Posted

技术标签:

【中文标题】如何使用 JetPack Compose 在 LazyColumn 项目中单击导航到详细视图?【英文标题】:How to navigate to Detail View clicking in LazyColumn item with JetPack Compose? 【发布时间】:2021-11-09 21:16:22 【问题描述】:

我正在尝试使用 JetPack Compose 创建一个应用程序(第一次),但我遇到了一些导航问题。该应用程序有一个底部栏,其中包含 3 个导航到所选屏幕的项目。

这可行,问题是当我尝试访问其中一个屏幕中的 LazyColumn 的一项时。我想导航到显示所选项目数据的另一个屏幕(配置文件),但我找不到方法。无论我如何尝试,我总是得到这样的“@Composable 调用只能在 @Composable 函数的上下文中发生”。

有人能帮我解释一下怎么做吗?我想要的是了解如何以及为什么,而不仅仅是复制。

谢谢

MainActivity

class MainActivity : ComponentActivity() 

    @ExperimentalFoundationApi
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)


        setContent 
            val systemUiController = rememberSystemUiController()

            SideEffect 
                systemUiController.setStatusBarColor(color = PrimaryDark)
            
            AppTheme() 


                MainScreen()

            

        
    

    @ExperimentalFoundationApi
    @Composable
    fun MainScreen() 
        val navController = rememberNavController()
        val navigationItems = listOf(Obras, Talleres, Ajustes)
        Scaffold(bottomBar = 
            BottomNavigationBar(
                navController = navController,
                items = navigationItems
            )
        ) 

            NavigationHost(navController)
        
    
 

NavigationHost.kt

@ExperimentalFoundationApi
@Composable
fun NavigationHost(navController: NavHostController) 


    NavHost(navController = navController, startDestination = Obras.route) 
        composable(Obras.route) 
            Pantalla1(navigateToProfile =  authorId ->
                navController.navigate("$Profile.route/$authorId")
            )

        
        composable(Talleres.route) 
            Pantalla2()
        

        composable(Ajustes.route) 
            Pantalla3()
        

        composable(
            Profile.route + "/authorId",
            arguments = listOf(navArgument("authorId")  type = NavType.StringType )
        )  backStackEntry ->
            val authorId = backStackEntry.arguments!!.getString("authorId")!!
            Profile(authorId)
        

    


Pantalla1.kt

typealias AuthorId = String

@ExperimentalCoroutinesApi
@Composable
fun Pantalla1(navigateToProfile: (AuthorId) -> Unit) 


    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(
                paddingValues = PaddingValues(

                    bottom = 50.dp
                )
            ),
    ) 

        AutoresInfo(navigateToProfile)
    



@OptIn(ExperimentalFoundationApi::class)
@Composable
fun AutoresInfo(navigateToProfile: (AuthorId) -> Unit) 
    var autoresList by remember 
        mutableStateOf<List<Autor>?>(null)
    

    JetFirestore(path =  collection("autores") ,
        queryOnCollection =  orderBy("nombre", Query.Direction.ASCENDING) ,

        onRealtimeCollectionFetch =  value, exception ->
            autoresList = value.getListOfObjects()
        ) 
        autoresList?.let 
            val grouped = it.groupBy  it.nombre[0] 
            LazyColumn(

                modifier = Modifier.fillMaxSize()

            ) 


                grouped.forEach  (initial, autoresForInitial) ->
                    stickyHeader 
                        StickyHeaderAutores(initial = initial.toString())
                    

                    items(autoresForInitial, key =  autor -> autor.nombre )  autor ->
                        Surface(modifier = Modifier.clickable  navigateToProfile(autor.nombre) ) 
                            @OptIn(coil.annotation.ExperimentalCoilApi::class)
                            AutorCard(autor)
                        


                    
                


            
         ?: Column(
            modifier = Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) 
            CircularProgressIndicator(
                color = Color.Red,
                modifier = Modifier

                    .size(50.dp)

            )
        

    




【问题讨论】:

导航到个人资料屏幕的导航代码在哪里? 【参考方案1】:

第 1 步。为您的个人资料导航路线添加参数。查看 documentation 关于使用参数导航

composable(
    Profile.route + "/authorId",
    arguments = listOf(navArgument("authorId")  type = NavType.StringType )
)  backStackEntry ->
    val authorId = backStackEntry.arguments!!.getString("authorId")!!
    Profile(authorId)

第 2 步。您需要从 NavigationHost 传递 navigateToProfile 函数。如果不是String,您可以将AuthorId 替换为Int(或其他类型):

typealias AuthorId = String

@Composable
fun NavigationHost(navController: NavHostController)
    NavHost(navController = navController, startDestination = Obras.route) 
        composable(Obras.route) 
            Pantalla1(navigateToProfile =  authorId ->
                navController.navigate("$Profile.route/$authorId")
            )
        
        ... 


@Composable
fun Pantalla1(navigateToProfile: (AuthorId) -> Unit) 
    ...
        AutoresInfo(navigateToProfile)
    ...


@Composable
fun AutoresInfo(navigateToProfile: (AuthorId) -> Unit) 
    ...
    items(autoresForInitial, key =  autor -> autor.nombre )  autor ->
        Surface(modifier = Modifier.clickable 
            navigateToProfile(author.id)
        ) 
            AutorCard(autor)
        
    
    ...

第 3 步。在您的可组合配置文件中,您需要按 id 获取作者。不确定JetFirestore 是什么,您可能应该使用它。

@Composable
fun Profile(id: AuthorId) 
    JetFirestore(
        // fetch author by id
    )

【讨论】:

谢谢,我正在尝试这个,但没有用。我不知道为什么,但是当我单击一个项目时,它什么也不做。我更新了代码。 @Komito添加日志,检查clickable是否被调用,然后检查navController.navigate是否在NavigationHost内部被调用 我发现了问题。在 AutorCard 我有一个空的可点击。我删除了它,所以现在它可以正常工作了。非常感谢您的帮助。【参考方案2】:

创建一个包含如下路由的密封类

sealed class Screen(val route:String)
    object MyListScreen:Screen("my_list_screen")
    object MyDetailScreen:Screen("my_detail_screen")

使用 Row 创建 listItem 以接收列表和点击事件,如下所示

@Composable
fun MyListItem(
    yourList: YourList,
    onItemClick:(yourList) -> Unit
) 
    Row(
       modifier = Modifier
           .fillMaxWidth()
           .clickable 
               onItemClick(yourList)
           
    )
   //something here

在 LazyColoumn 的 listScreen 中,您可以执行此操作

 LazyColumn(modifier = Modifier.fillMaxSize())
            items (state.yourfreshlist)mylist->
               MyListItem(mylist = mylist, onItemClick = 
                 navController.navigate(Screen.MyDetailScreen.route+"/$mylist.something")
               
               )
            
    

你的宿主会是这样的

NavHost(
            navController = navController,
            startDestination = Screen.MyListScreen.route
        ) 
            composable(
                route = Screen.MyListScreen.route
            ) 
                MyListScreen(navController)
            
            composable(
                route = Screen.MyDetailScreen.route + "/anything here id for example"
            ) 
                MyDetailScreen()
            
        

更多信息可以浏览docs

【讨论】:

以上是关于如何使用 JetPack Compose 在 LazyColumn 项目中单击导航到详细视图?的主要内容,如果未能解决你的问题,请参考以下文章

Jetpack Compose ScrollableTabRow 如何调整最小宽度

如何在 Jetpack Compose 中使用小部件?

如何将 CameraView 与 Jetpack Compose 一起使用?

如何在 jetpack compose 中使用 MotionLayout、MotionScene、ConstraintSet?

如何在 Android Jetpack Compose 中使用字符串资源?

Jetpack Compose ScrollableTabRow 如何调整最小宽度