Jetpack Compose中的Navigation从入门到精通完全指南

news2024/12/27 12:50:46

Jetpack Compose中的Navigation从入门到精通完全指南

什么是Android导航

导航帮助您理解应用程序在不同组件间的移动方式。

Android JetPack Navigation可以帮助您以简单的方式实现高级导航。

导航组件由三个主要部分组成:

导航图(Navigation Graph): 这是一个资源,它将所有与导航相关的数据集中在一起。这包括应用程序中的所有位置(称为目标位置)以及用户在应用程序中可能采取的路径。它就像一本大书,里面包含了您在应用程序中可以访问的所有地方以及如何在它们之间移动的方法。可以将其视为地图和指南的结合。
NavHost: 这是一个独特的可组合项,您可以将其包含在布局中。它显示来自您的导航图的各个目标位置。NavHost将NavController与指定可组合项目标位置的导航图链接起来。当您在可组合项之间导航时,NavHost的内容将自动重新组合。导航图中的每个可组合项目标位置都与一个路由关联。
NavController: NavController是导航组件的中央API。它具有状态并跟踪组成应用程序屏幕的可组合项的后退栈以及每个屏幕的状态。

Jetpack Compose中的导航

导航组件为Jetpack Compose应用程序提供了支持。您可以在利用导航组件的基础设施和功能的同时,在可组合项之间进行导航。

要开始在Jetpack Compose中进行导航,您需要在项目的build.gradle文件中包含所需的依赖项:

implementation "androidx.navigation:navigation-compose:2.7.1"

Jetpack Compose中导航的基本概念。

NavController
NavController是导航组件的中央API。它具有状态并跟踪组成应用程序屏幕的可组合项的后退栈以及每个屏幕的状态。

您可以通过在可组合项中使用rememberNavController()方法来创建一个NavController:

val navController = rememberNavController()

您应该在可组合项层次结构中的一个位置创建NavController,并确保所有需要引用它的可组合项都能够访问它。这遵循状态提升的原则,并允许您使用NavController和通过currentBackStackEntryAsState()提供的状态作为更新屏幕外的可组合项的真实数据源。有关此功能的示例,请参阅与底部导航栏集成。

注意:如果您在片段中使用导航组件,您无需在Compose中定义新的导航图或使用NavHost可组合项。有关更多信息,请参阅互操作性部分。

NavHost
每个NavController必须与一个NavHost可组合项关联。NavHost将NavController与指定应该能够在其之间导航的可组合项目标位置的导航图链接起来。当您在可组合项之间导航时,NavHost的内容将自动重新组合。导航图中的每个可组合项目标位置都与一个路由相关联。

关键术语:路由是一个字符串,用于定义到达特定可组合项的路径。可以将其视为指向特定目的地的隐式深层链接。每个目的地应具有唯一的路由。

创建NavHost需要之前通过rememberNavController()创建的NavController以及您的导航图的起始目的地的路由。NavHost的创建使用了导航Kotlin DSL中的lambda语法来构建导航图。您可以使用composable()方法来添加导航结构。该方法要求您提供一个路由和应与目的地关联的可组合项:

NavHost(navController = navController, startDestination = "profile") {
    composable("profile") { Profile(/*...*/) }
    composable("friendslist") { FriendsList(/*...*/) }
    /*...*/
}

注意:导航组件要求您遵循导航原则并使用固定的起始目的地。您不应该对startDestination路由使用可组合项值。

示例:如何设置导航图、NavHostNavigationItem

步骤1:在一个文件中定义导航的屏幕名称和路由,例如AppNavigation.kt

enum class Screen {
    HOME,    
    LOGIN,
}
sealed class NavigationItem(val route: String) {
    object Home : NavigationItem(Screen.HOME.name)
    object Login : NavigationItem(Screen.LOGIN.name)
}

步骤2:定义NavHost,比如AppNavHost.kt

@Composable
fun AppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController,
    startDestination: String = NavigationItem.Splash.route,
    ... // other parameters
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable(NavigationItem.Splash.route) {
            SplashScreen(navController)
        }
        composable(NavigationItem.Login.route) {
            LoginScreen(navController)
        }
}

步骤3:在你的MainActivity.kt中调用AppNavHost

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            AutoPartsAppTheme {
               Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    AppNavHost(navController = rememberNavController())
                }
            }
        }
    }
}

导航参数:
导航Compose还支持在可组合项目标位置之间传递参数。为了实现这一点,您需要向路由添加参数占位符,类似于在使用基础导航库时向深层链接添加参数的方式:

使用情况:

  1. 无参数
  2. 使用简单参数,如预定义的数据类型(例如Int、String等)
  3. 使用复杂参数,如用户定义的数据类型
  4. 可选参数
  5. 返回结果的导航

无参数:

NavHost(navController = navController, startDestination = "profile") {
    composable("profile") { Profile(/*...*/) }
    composable("friendslist") { FriendsList(/*...*/) }
    /*...*/
}

使用简单参数:
默认情况下,所有参数都会被解析为字符串。composable()的arguments参数接受NamedNavArguments列表。您可以使用navArgument方法快速创建NamedNavArgument,并指定其确切类型:

NavHost(startDestination = "profile/{userId}") {
    ...
    composable("profile/{userId}") {...}
}

默认情况下,所有参数都会被解析为字符串。composable()的arguments参数接受NamedNavArguments列表。您可以使用navArgument方法快速创建NamedNavArgument,并指定其确切类型:

NavHost(startDestination = "profile/{userId}") {
    ...
    composable(
        "profile/{userId}",
        arguments = listOf(navArgument("userId"){
           type = NavType.StringType 
        })
    ) {...}
}

您应该从composable()函数的lambda中可用的NavBackStackEntry中提取参数。

composable("profile/{userId}") { backStackEntry ->
   val userId = backStackEntry.arguments?.getString("userId")
   // 在这里获取用户数据
   Profile(
      navController, 
      // 传递获取的用户数据,如UserInfo
   )
}

要将参数传递给目标位置,需要在进行导航调用时将其附加到路由中:

navController.navigate("profile/user1234")

有关支持的类型列表,请参阅在目的地之间传递数据。

使用复杂参数或用户定义的参数:
强烈建议在导航时不要传递复杂的数据对象,而是在执行导航操作时将最小必要的信息作为参数传递,例如唯一标识符或其他形式的ID:

// 仅在导航到新目标时传递用户ID作为参数
navController.navigate("profile/user1234")

复杂对象应存储为单一真相源(例如数据层)中的数据。导航到目的地后,您可以使用传递的ID从单一真相源中加载所需信息。要检索ViewModel中的参数,负责访问数据层的ViewModel,您可以使用ViewModel的SavedStateHandle

class UserViewModel(
    savedStateHandle: SavedStateHandle,
    private val userInfoRepository: UserInfoRepository
) : ViewModel() {

    private val userId: String = checkNotNull(savedStateHandle["userId"])

    // Fetch the relevant user information from the data layer,
    // ie. userInfoRepository, based on the passed userId argument
    private val userInfo: Flow<UserInfo> = userInfoRepository.getUserInfo(userId)

   --------------- OR -----------------
 
    // fetch data from network or database    
    private val _dataFlow =
            MutableStateFlow<UserInfo>(userInfoRepository.getUserInfo(userId))
    val dataFlow get() = _dataFlow.asStateFlow()
}

Composable函数

//Navhost 
composable("profile/{userId}") { backStackEntry ->
   val userId = backStackEntry.arguments?.getString("userId")
   // here you have to fetch user data 
   val userInfo by taskViewModel.dataFlow.collectAsState()   
   Profile(
      navController, 
      userInfo
   )
}

// Profile screen
@Composable
fun Profile(navController: NavController, userInfo:UserInfo){
    // do you work here
}

注意:将ViewModel放在可组合屏幕之外,因为预览将无法工作,并且最佳实践是避免可组合和ViewModel之间的耦合。

这种方法有助于防止在配置更改期间数据丢失以及更新或改变对象时出现任何不一致性。

有关为什么应避免将复杂数据作为参数传递的更详细解释,以及支持的参数类型列表,请参阅在目的地之间传递数据。

添加可选参数

Navigation Compose还支持可选导航参数。可选参数与必需参数有两点不同:

必须使用查询参数语法("?argName={argName}")进行包含
必须设置defaultValue,或者设置nullable = true(隐式地将默认值设置为null)
这意味着所有可选参数都必须显式地添加到composable()函数中作为列表:

composable(
    "profile?userId={userId}/{isMember}",
    arguments = listOf(
         navArgument("userId") {
            type = NavType.StringType
            defaultValue = "user1234"
           // OR 
            type = NavType.StringType
            nullable = true
         },
         navArgument("isNewTask") {
            type = NavType.BoolType
         }
     )
) { backStackEntry ->
    val userId = backStackEntry.arguments?.getString("userId")
    val isMember = backStackEntry.arguments?.getBoolean("isMember")?:false
    Profile(navController, userId, isMember)
}

现在,即使没有传递参数给目标,defaultValue = "user1234"也会被使用。

通过路由处理参数的结构意味着您的可组合部件完全独立于导航,并使它们更容易进行测试。

带有结果的返回导航

带有结果的返回导航是最常见的任务。例如,当您打开筛选对话框并选择筛选条件,然后带着所选的筛选条件返回导航到应用这些筛选条件到屏幕上。

有两个屏幕。1. FirstScreen(第一个屏幕)和2. SecondScreen(第二个屏幕)。我们需要从SecondScreen(第二个屏幕)获取数据到FirstScreen(第一个屏幕)。

NavHost.kt:设置导航图。

val navController = rememberNavController()
 NavHost(
     navController = navController,
     startDestination = "firstscreen"
 ) {
    composable("firstscreen") {
        FirstScreen(navController)
    }
    composable("secondscreen") {
        SecondScreen(navController)
    }
}
@Composable
fun FirstScreen(navController: NavController) {
    // Retrieve data from next screen
    val msg = 
        navController.currentBackStackEntry?.savedStateHandle?.get<String>("msg")
    Column(
        Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Button(onClick = { navController.navigate("secondscreen") }) {
            Text("Go to next screen")
        }
        Spacer(modifier = Modifier.height(8.dp))
        msg?.let {
            Text(it)
        }
    }
}

FirstScreen.kt: 利用NavController当前返回堆栈条目的savedStateHandle在从SecondScreen返回后检索数据。

@Composable
fun FirstScreen(navController: NavController) {
    // Retrieve data from next screen
    val msg = 
        navController.currentBackStackEntry?.savedStateHandle?.get<String>("msg")
    Column(
        Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Button(onClick = { navController.navigate("secondscreen") }) {
            Text("Go to next screen")
        }
        Spacer(modifier = Modifier.height(8.dp))
        msg?.let {
            Text(it)
        }
    }
}

SecondScreen.kt: 将数据放入NavController的前一个返回堆栈条目的savedStateHandle中。

@Composable
fun SecondScreen(navController: NavController) {
    var text by remember {
        mutableStateOf("")
    }
    Column(
        Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        TextField(
            value = text, onValueChange = { text = it },
            placeholder = {
                Text("Enter text", color = Color.Gray)
            }
        )
        Spacer(Modifier.height(8.dp))
        Button(onClick = {

           // Put data into savedStateHandle to retrive data on the previous screen
   
            navController.previousBackStackEntry?.savedStateHandle?.set("msg", text)
            navController.popBackStack()
        }) {
            Text(text = "Submit")
        }
    }
}

https://github.com/KaushalVasava/JetPackCompose_Basic/tree/navigate-back-with-result

Deep links

Navigation Compose支持将隐式深链接定义为composable()函数的一部分。它的deepLinks参数接受一个NavDeepLinks列表,可以使用navDeepLink方法快速创建:

val uri = "https://www.example.com"
composable(
    "profile?id={id}",
    deepLinks = listOf(navDeepLink { uriPattern = "$uri/{id}" })
) { backStackEntry ->
    Profile(navController, backStackEntry.arguments?.getString("id"))
}

这些深链接可以将特定的URL、操作或MIME类型与可组合关联起来。默认情况下,这些深链接不会向外部应用程序公开。要使这些深链接对外部可用,必须在应用程序的manifest.xml文件中添加适当的元素。为了启用上述深链接,您应该在清单文件的元素内添加以下内容:

<activity >
  <intent-filter>
    ...
    <data android:scheme="https" android:host="www.example.com" />
  </intent-filter>
</activity>

当深链接被其他应用触发时,导航会自动进入到该可组合。

这些相同的深链接也可以用于从可组合中构建具有适当深链接的PendingIntent:

val id = "exampleId"
val context = LocalContext.current
val deepLinkIntent = Intent(
    Intent.ACTION_VIEW,
    "https://www.example.com/$id".toUri(),
    context,
    MyActivity::class.java
)
val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
    addNextIntentWithParentStack(deepLinkIntent)
    getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}

然后,您可以像其他任何PendingIntent一样使用这个deepLinkPendingIntent来打开您的应用程序的深链接目标位置。

Nested Navigation


目标可以分组为嵌套图形,以模块化您应用程序界面中的特定流程。一个例子就是一个独立的登录流程。

嵌套图形将其目标分组,就像主图形一样,并且它需要一个指定的起始目标作为其关联路由,当访问该嵌套图形的路由时,您将进入该起始目标。

要将嵌套图形添加到NavHost中,可以使用导航扩展函数:

NavHost(navController, startDestination = "home") {
    ...
    // 通过其路由('login')导航到图形会自动导航到图形的起始目标 - 'username'
    // 因此封装了图形的内部路由逻辑
    navigation(startDestination = "username", route = "login") {
        composable("username") { ... }
        composable("password") { ... }
        composable("registration") { ... }
    }
    ...
}

强烈建议随着图形的增长,将导航图形拆分为多个方法。这还允许多个模块贡献自己的导航图形。

fun NavGraphBuilder.loginGraph(navController: NavController) {
    navigation(startDestination = "username", route = "login") {
        composable("username") { ... }
        composable("password") { ... }
        composable("registration") { ... }
    }
}

通过将该方法作为NavGraphBuilder的扩展方法,您可以与预构建的导航、composable和dialog扩展方法一起使用:

NavHost(navController, startDestination = "home") {
    ...
    loginGraph(navController)
    ...
}

示例:

val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
      composable("about") {}
      navigation(
         startDestination = "login",
        route = "auth"
      ) {
          composable("login") {
          val viewModel = it.sharedViewModel<SampleViewModel>(navController)

          Button(onClick = {
               navController.navigate("calendar") {
                  popUpTo("auth") {
                     inclusive = true
                  }
               }
           }) {
          }
         }
         composable("register") {
             val viewModel = it.sharedViewModel<SampleViewModel>(navController)
         } 
         composable("forgot_password") {
             val viewModel = it.sharedViewModel<SampleViewModel>(navController)
         }
      }
      navigation(
          startDestination = "calendar_overview",
          route = "calendar"
      ) {
           composable("calendar_overview") { }
           composable("calendar_entry") { }
      }
    }

NavBackStack扩展函数

@Composable
inline fun <reified T : ViewModel> NavBackStackEntry.sharedViewModel(navController: NavController): T {
    val navGraphRoute = destination.parent?.route ?: return viewModel()
    val parentEntry = remember(this) {
        navController.getBackStackEntry(navGraphRoute)
    }
    return viewModel(parentEntry)
}

与bottom nav bar的集成

通过在可组合层次结构的较高级别定义NavController,您可以将导航与其他组件(如底部导航组件)连接起来。这样做可以通过选择底部栏中的图标进行导航。

要使用BottomNavigationBottomNavigationItem组件,请将androidx.compose.material依赖项添加到Android应用程序中。

dependencies {
    implementation "androidx.compose.material:material:1.5.1"
}
android {
    buildFeatures {
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.3"
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

为了将底部导航栏中的项链接到导航图中的路由,建议定义一个sealed class(例如这里的Screen),其中包含目标的路由和String资源ID。

sealed class Screen(val route: String, @StringRes val resourceId: Int) {
    object Profile : Screen("profile", R.string.profile)
    object FriendsList : Screen("friendslist", R.string.friends_list)
}

然后将这些项放入可以供BottomNavigationItem使用的列表中:

val items = listOf(
   Screen.Profile,
   Screen.FriendsList,
)

BottomNavigation的可组合函数中,使用currentBackStackEntryAsState()函数获取当前的NavBackStackEntry。该条目可让您访问当前的NavDestination。然后,可以通过比较项的路由与当前目标及其父目标的路由(以处理使用嵌套导航时的情况)来确定每个BottomNavigationItem的选中状态,通过NavDestination层次结构。

项的路由还用于将onClick lambda连接到导航调用,以便点击该项时导航到该项。通过使用saveStaterestoreState标志,正确保存和恢复该项的状态和返回堆栈,以便在底部导航项之间切换时进行操作。

val navController = rememberNavController()
Scaffold(
  bottomBar = {
    BottomNavigation {
      val navBackStackEntry by navController.currentBackStackEntryAsState()
      val currentDestination = navBackStackEntry?.destination
      items.forEach { screen ->
        BottomNavigationItem(
          icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
          label = { Text(stringResource(screen.resourceId)) },
          selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
          onClick = {
            navController.navigate(screen.route) {
              // Pop up to the start destination of the graph to
              // avoid building up a large stack of destinations
              // on the back stack as users select items
              popUpTo(navController.graph.findStartDestination().id) {
                saveState = true
              }
              // Avoid multiple copies of the same destination when
              // reselecting the same item
              launchSingleTop = true
              // Restore state when reselecting a previously selected item
              restoreState = true
            }
          }
        )
      }
    }
  }
) { innerPadding ->
  NavHost(navController, startDestination = Screen.Profile.route, Modifier.padding(innerPadding)) {
    composable(Screen.Profile.route) { Profile(navController) }
    composable(Screen.FriendsList.route) { FriendsList(navController) }
  }
}

在这里,您可以利用NavController.currentBackStackEntryAsState()方法来提升navController状态,将其从NavHost函数中提取出来,并与BottomNavigation组件共享。这意味着BottomNavigation会自动具有最新的状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1040932.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Boot:控制器调用模板引擎渲染数据的基本过程

目录 基础知识注解&#xff1a; Controller方法&#xff1a;RequestMapping 基本过程添加 FreeMarker 依赖创建控制器方法创建 FTL 文件 基础知识 注解&#xff1a; Controller 控制器注解&#xff0c;表示这个类是一个控制器类&#xff0c;里面定义了一些处理客户端请求的方…

2023 “华为杯” 中国研究生数学建模竞赛(F题)深度剖析|数学建模完整代码+建模过程全解全析

F题代码思路 当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2021年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 让我们一起看看研赛的F题呀&#xff01;全文都已…

【OpenSSL】OpenSSL实现Base64

Base 64概述和应用场景 概述 Base64就是将二进制数据转换为字符串的一种算法。 应用场景 邮件编码xml或则json存储二进制内容网页传递数据URL数据库中以文本形式存放二进制数据可打印的比特币钱包地址base58Check(hash校验)网页上可以将图片直接使用Base64表达公私密钥的文…

Python 用列表实现模拟手机通讯录(简易版)

"""列表实现好友管理系统知识点&#xff1a;1、列表存储信息2、列表增删改查3、嵌套循环4、字符串分割和拼接&#xff08;重点&#xff09;5、列表索引"""# 暂存好友信息&#xff08;程序结束数据删除&#xff09; friend_info list()input_buf…

opencv形态学-膨胀

opencv形态学-膨胀 膨胀就是取每一个位置结构元领域内最大值作为该位置的输出灰度值&#xff1b; 膨胀是取邻域内最大值&#xff0c;那么显然膨胀后图像整体亮度会比原先要高&#xff0c;图像中亮的物体尺寸会变大&#xff0c;相反暗的尺寸会减小&#xff0c;甚至是消失 结构元…

流程图在线制作:5款专业流程图制作网站,无需下载,高效立现!

流程图&#xff0c;是特定的图形符号加上说明&#xff0c;表示算法的图&#xff0c;是一种可视化工具。近年来流程图逐渐在工作、教育、项目管理等诸多领域大放异彩&#xff0c;市面上也流行着许多流程图制作软件&#xff0c;考虑到许多软件下载流程繁琐、并且还有下载盗版的风…

pytorch学习笔记——BCE与CE

BCELoss的话只需要网络输出一个通道&#xff0c;CE Loss(Cross Entropy Loss)需要输出n_class个通道。 对于二分类任务可以使用CE Loss输出两个通道&#xff0c;也可以使用BCE Loss输出一个通道。 https://www.jianshu.com/p/5b01705368bb https://zhuanlan.zhihu.com/p/372628…

客户端负载均衡_负载均衡策略

以前的Ribbon有多种负载均衡策略 RandomRule - 随性而为 解释&#xff1a; 随机 RoundRobinRule - 按部就班 解释&#xff1a; 轮询 RetryRule - 卷土重来 解释&#xff1a; 先按照RoundRobinRule的策略获取服务&#xff0c;如果获取服务失败则在指定时间内会进行重试。 Weigh…

Learn Prompt- Midjourney案例:网页设计

快速开始​ 用 “ web design for...” 或 “ modern web design for..” 来快速开始你的提示。 web design for a generic SaaS startup --ar 3:2否定提示-no​ 使用--no告诉 Midjourney 你不想要什么。Midjourney 的默认风格倾向于现实和详细。但这可能不适用于所有品牌。…

HEC-RAS 1D/2D水动力与水环境模拟从小白到精通

专题一 水动力模型基础 1.水动力模型的本质 2.水动力模型的基本方程与适用范围 3.模型建模要点 4.注意事项与建模经验 专题二 恒定流模型(1D/2D) 1.恒定流及其适用范围 2.水面线分析及其数据要求 3.曼宁公式与恒定流&#xff0c;后处理 4.HEC-RA的水工建筑物&#xff…

ADC数模转化器

简介 • ADC &#xff08; Analog-Digital Converter &#xff09;模拟 - 数字转换器 • ADC 可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁 • 12 位逐次逼近型 ADC &#xff0c; 1us 转换时间 &#xff08;12位:分辨率…

vue里使用elementui的级联选择器el-cascader进行懒加载的怎么实现数据回显?

需要实现的懒加载回显效果 比如&#xff1a;后端返回数据 广东省/广州市/天河区 &#xff1a;440000000000/440100000000/440106000000&#xff0c;需要我们自动展开到天河区的下一级&#xff0c;效果如下 代码实现 我的实现思路就是拿到 440000000000/440100000000/44010600…

YOLOv5如何训练自己的数据集(生活垃圾数据集为例)

文章目录 前言1、数据标注说明2、定义自己模型文件3、训练模型参考文献 前言 本文主要介绍如何利用YOLOv5训练自己的数据集 1、数据标注说明 以生活垃圾数据集为例子 生活垃圾数据集&#xff08;YOLO版&#xff09;点击这里直接下载本文生活垃圾数据集 生活垃圾数据集组成&…

软件测试/测试开发丨利用人工智能ChatGPT自动生成PPT

点此获取更多相关资料 简介 PPT 已经渗透到我们的日常工作中&#xff0c;无论是工作汇报、商务报告、学术演讲、培训材料都常常要求编写一个正式的 PPT&#xff0c;协助完成一次汇报或一次演讲。PPT相比于传统文本的就是有布局、图片、动画效果等&#xff0c;可以给到观众更好…

VSCode 和 CLion

文章目录 一、VSCode1、文档2、插件3、智能编写4、VSCode 与 C&#xff08;1&#xff09;安装&#xff08;2&#xff09;调试&#xff08;a&#xff09;使用 CMake 进行跨平台编译与调试&#xff08;b&#xff09;launch.json&#xff08;c&#xff09;传参 &#xff08;3&…

mac有必要用清理软件吗

随着科技的不断进步&#xff0c;我们的计算机硬盘容量也在不断增长。即使是ARM架构的处理器&#xff0c;也可以通过高效的文件系统和技术来充分利用磁盘空间。然而&#xff0c;对于使用Mac OS系统的用户来说&#xff0c;仅仅使用一个盘来存储所有文件可能会导致一些残留文件的问…

深入了解 Docker 容器操作命令:掌握容器化管理的关键

Docker 已经成为现代应用程序开发和部署的行业标准。它借助容器化技术&#xff0c;提供了一种轻量、可移植和可扩展的方式来构建、发布和运行应用程序。然而&#xff0c;最近我在工作中发现&#xff0c;一些家人们对 Docker 容器的操作命令还不太熟悉。因此&#xff0c;本文旨在…

C++之std::function类模板定义函数对象应用总结(二百三十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

PWN基础:从源文件到可执行文件

目录 编译原理 GCC编译过程 Preprocess阶段 File命令 Compile阶段 Assemble阶段 Link阶段 高级语言编写的程序想在操作系统运行&#xff0c;需要被翻译为机器指令&#xff0c;在按照可执行目标文件格式打包并以二进制形式存储在文件中 编译原理 编译器作用&#xff1a;…

php实现分页功能跳转和ajax方式实现

实现效果 准备工作 创建数据表和导入测试数据 CREATE TABLE users ( id int(10) unsigned NOT NULL AUTO_INCREMENT, username varchar(30) DEFAULT NULL COMMENT 账号, email varchar(30) DEFAULT NULL COMMENT 密码, PRIMARY KEY (id) ) ENGINEMyISAM AUTO_INCREM…