我刚开始接触Compose和声明式编程,我正在努力理解它。为了学习,在阅读教程和观看课程之后,现在我正在创建我的第一个应用程序。
我正在创建一个使用compose multiplatform的compose桌面应用程序,它可以给予你从电脑中选择一个文件夹,并显示该文件夹中的所有文件。我正在启动一个JFileChooser
来选择一个文件夹。当它被选中时,状态变量被改变并且Box
被填充以表示该文件夹内的文件名的文本。这些名称是通过使用JFileChooser
返回的路径的函数获得的。
这个应用程序有两个奇怪的行为。首先是因为这个屏幕有一个TextField
,如果我在里面写东西,充满文本的Box
似乎被重新绘制,再次调用搜索文件的函数(这些文件可能会导致应用程序变慢)。
第二个奇怪的行为是,如果我再次打开JFileChooser
来更改文件夹,它会正确地重绘Box
,并获得该文件夹的文件名,但如果我选择之前选择的同一个文件夹,则不会重绘Box
,如果该文件夹中的文件被更改,则会出现问题。
有人能帮助我理解Compose
的这些问题吗?我认为这两个问题都与声明性组合逻辑有关,但不明白哪里出了问题。谢谢。
这是显示JFileChooser的按钮
var listRomsState by remember { mutableStateOf(false) }
Button(onClick = {
folderChosenPath = folderChooser()
if (folderChosenPath != "")
listRomsState = true
}) {
Text(text = "List roms")
}
这是显示JFileChooser的函数
fun folderChooser(): String {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
val f = JFileChooser()
f.fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
val result: Int = f.showSaveDialog(null)
if (result == JFileChooser.APPROVE_OPTION) {
return f.selectedFile.path
} else {
return ""
}
}
显示文件选择器的按钮下方是文件名列表:
if (listRomsState) {
RomsList(File(folderChosenPath))
}
下面是RomsList函数:
@Composable
fun RomsList(folder: File) {
Box (
modifier = Modifier.fillMaxSize().border(1.dp, Color.LightGray)
) {
LazyColumn(
Modifier.fillMaxSize().padding(top = 5.dp, end = 8.dp)
){
var romsList = getRomsFromFolder(folder)
items(romsList.size) {
Box (
modifier = Modifier.padding(5.dp, 0.dp, 5.dp, 0.dp).fillMaxWidth(),
contentAlignment = Alignment.CenterStart
) {
Row (horizontalArrangement = Arrangement.spacedBy(5.dp)){
Text(text = "" + (it+1), modifier = Modifier.weight(0.6f).background(color = Color(0, 0, 0, 20)))
Text(text = romsList[it].title, modifier = Modifier.weight(9.4f).background(color = Color(0, 0, 0, 20)))
}
}
Spacer(modifier = Modifier.height(5.dp))
}
}
}
}
这个函数可以递归地获取文件夹中的所有文件名:
fun getRomsFromFolder(curDir: File? = File(".")): MutableList<Rom> {
var romsList = mutableListOf<Rom>()
val filesList = curDir?.listFiles()
filesList?.let {
for (f in filesList) {
if (f.isDirectory) romsList.addAll(getRomsFromFolder(f))
if (f.isFile) {
romsList.add(Rom(f.name))
}
}
}
return romsList
}
3条答案
按热度按时间crcmnpdw1#
我创建了一个非常简单的可组合对象,它与基于我们讨论的组合结构完全相同。
请考虑以下代码:
当您运行此命令时,以及当您在TextField中键入任何内容时,当您在文本字段中键入内容时,这两个可组合程序都将重新组合并生成此日志输出
从这个article中,我运行了一个命令,发现
java.io.File
不是一个稳定的类型。现在我们知道
File
不是@Stable
类型,并且我们无法控制它的API,我将它 Package 在一个自定义数据类中,如下所示,并修改了调用站点因此,使用
FileWrapper
修改上面的所有代码。生成以下日志输出。
运行gradle命令后,报告如下:
RomsList
现在是skippable
,带有一个稳定的参数,因此当其父级可组合重新组合时,将跳过RomsList
。对于您的第二个问题,您是否介意尝试将
mutableList
替换为mutableStateList()
?(这将创建SnapshotStateList
的示例),这样,对列表的任何更改都将保证对读取它的可组合对象的更新7lrncoxx2#
你需要习惯的重要机制是重组。我不知道它在ComposeMultiplatform中是如何工作的,但在android中重组依赖于状态的变化。当可组合函数包含某种状态时,它会自动监听它的变化,并在突变时进行重组。
在重新组合期间,重新组合的可组合对象的UI元素将使用新值再次绘制,以表示实际状态。
所以,解释你的奇怪行为:
这个应用程序有两个奇怪的行为。首先是因为屏幕上有一个文本字段,如果我在里面写东西,充满文本的框似乎会被重新绘制,再次调用搜索文件的函数(这些文件可能会导致应用程序变慢)。
发生这种情况是因为你改变了状态--文本字段的文本值。所以重组发生了。解决方案是移动所有的逻辑,这确实需要被再次调用以分离可组合的。解释是现在here
第二个奇怪的行为是,如果我再次打开JFileChooser来更改文件夹,它会正确地重画Box,并获得该文件夹的文件名,但如果我选择先前选择的同一个文件夹,则不会重画Box,如果该文件夹中的文件被更改,则会出现问题。
当需要重新组合但没有发生时就是这种情况。发生这种情况是因为可组合的
RomsList
不包含文件夹状态,因此不会在文件夹更改时自动重新组合。您可能不应该将
folder
作为一个简单的参数传递,而应该将其作为一个状态来记忆。然而,由于文件夹是从另一个函数进入可组合对象的,因此解决方案之一是在调用函数中创建这样的状态,并将该函数标记为
@Composable
。6jjcrrmo3#
最后我在Steyrix,z.y和其他一些人的帮助下发现了一些东西。
首先,正如这里所提到的,https://developer.android.com/jetpack/compose/mental-model#recomposition可组合函数只有在其参数被更改时才执行其代码,因此这就是问题2的原因。
另外,这两个问题的主要问题是,我执行的逻辑在错误的位置检索文件夹中的所有文件。它是在一个可组合函数中执行的,这是一个问题,因为每次重新组合时都会执行它。因此,在收到文件选择器的结果后,将逻辑移到onclick解决了这两个问题。
而且,现在我明白的事情多了。
谢谢你们!