JFXPanel dispose后重新加载无法显示,Platform.runLater代码不执行
问题分析与解决方案
问题根源
首次打开正常但退出再打开后Platform.runLater代码不执行,核心原因有两点:
SwingPanel的factory默认仅在组件首次挂载时执行一次,页面退出后再次进入时,Compose可能复用旧的JFXPanel实例,未触发新的初始化逻辑。minimalViewer通过无依赖的remember保存,页面退出后状态未被清空,旧引用干扰新组件的创建流程。
修改后的代码
package ui.pdfpreview import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.awt.SwingPanel import com.geolo.desktop.common.utils.LogUtils import eu.patrickgeiger.fxpdf.util.PDF import eu.patrickgeiger.fxpdf.viewer.MinimalViewer import javafx.application.Platform import javafx.embed.swing.JFXPanel import javafx.scene.Scene import javafx.scene.layout.StackPane import java.io.File @Composable fun PdfPreview( state: PdfPreviewState, modifier: Modifier = Modifier, onCreated: (MinimalViewer) -> Unit = {}, onDispose: (MinimalViewer) -> Unit = {}, ) { // 让状态依赖文件路径,页面重建或文件变更时重置状态 var minimalViewer by remember(state.filePath) { mutableStateOf<MinimalViewer?>(null) } var jfxPanel by remember(state.filePath) { mutableStateOf<JFXPanel?>(null) } val currentOnDispose by rememberUpdatedState(onDispose) minimalViewer?.let { viewer -> DisposableEffect(viewer) { onDispose { LogUtils.i("linlian pdfpreview onDispose minimalViewer=$viewer ") viewer.pdf.closeDocument() currentOnDispose(viewer) // 清空引用并清理旧场景 minimalViewer = null jfxPanel?.scene = null } } } SwingPanel( factory = { JFXPanel().also { panel -> jfxPanel = panel Platform.runLater { val rootViewer = MinimalViewer.MinimalViewerBuilder() .setPDF(PDF(File(state.filePath))) .build() minimalViewer = rootViewer val root = StackPane().apply { children.add(rootViewer) } val scene = Scene(root) onCreated.invoke(rootViewer) panel.scene = scene } } }, modifier = modifier, // 绑定key,确保文件变化或页面重建时重新创建Swing组件 key = state.filePath ) { jfxPanel -> LogUtils.i("linlian pdfpreview update minimalViewer=$minimalViewer ") LogUtils.i("linlian pdfpreview update jfxpannel=$jfxPanel ") } } class PdfPreviewState( val filePath: String )
关键修改点说明
- 状态依赖绑定:给
remember的minimalViewer和jfxPanel添加state.filePath作为依赖,确保页面重新进入或文件路径变化时,状态自动重置为null,触发全新初始化流程。 - SwingPanel强制重建:给
SwingPanel添加key = state.filePath,当key变化时,Compose会销毁旧的Swing组件并重新调用factory创建新实例,避免复用旧的JFXPanel。 - 资源彻底清理:在
DisposableEffect的销毁逻辑中,不仅关闭PDF文档,还清空minimalViewer引用,并将JFXPanel的scene设为null,彻底释放旧资源,避免干扰新组件。
额外注意事项
确保JavaFX运行时在应用启动时已初始化,可在应用入口添加:
fun main() { // 提前初始化JavaFX线程环境 JFXPanel() // 启动Compose应用 application { // 你的应用代码 } }
内容的提问来源于stack exchange,提问作者lynn8570
相关产品推荐
相关产品推荐

