You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Compose for Desktop中JList项内ComposePanel持续重绘问题排查

问题

在Compose/Swing互操作项目中,给SwingPanel添加了JList,JList使用渲染器渲染ComposePanel,但启动后JList里的ComposePanel内容持续重绘,控制台不断输出"recompose"。

简化后的核心代码如下:

主Compose代码

val fakeData = mutableListOf<String>().apply {
    repeat(5) {
        add("test $it")
    }
}

@Composable
@Preview
fun App() {
    MaterialTheme {
        Box(
            modifier = Modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ) {
            Column(
                modifier = Modifier.padding(top = 80.dp, bottom = 20.dp)
            ) {
                Text("Recompose Test")
                Spacer(modifier = Modifier.height(20.dp))

                SwingPanel(
                    background = Color.Red,
                    modifier = Modifier.size(500.dp),
                    factory = {
                        JPanel().apply {
                            layout = BoxLayout(this, BoxLayout.Y_AXIS)
                            alignmentY = Component.TOP_ALIGNMENT

                            val composeJList = JList(fakeData.toTypedArray()).apply {
                                cellRenderer = ComposeRenderer()
                            }

                            val scrollPane = JScrollPane(composeJList)
                            add(scrollPane)

                            isVisible = true
                        }
                    }
                )
            }
        }
    }
}

fun main() = application {
    Window(onCloseRequest = ::exitApplication) {
        App()
    }
}

ListCellRenderer代码

class ComposeRenderer : JPanel(), ListCellRenderer<String> {
    private val composePanel = ComposePanel()

    init {
        layout = BoxLayout(this, BoxLayout.Y_AXIS)
        add(composePanel)
    }

    override fun getListCellRendererComponent(
        list: JList<out String>,
        text: String,
        index: Int,
        isSelected: Boolean,
        cellHasFocus: Boolean
    ): Component {
        print("recompose")
        composePanel.setContent {
            Text(text)
        }
        return this
    }
}
原因分析

这是代码逻辑问题,不是Bug。核心问题在于getListCellRendererComponent的实现:

  • 每次Swing需要渲染列表项时,都会调用该方法
  • 方法内每次都执行composePanel.setContent更新Compose内容,而Compose内容更新会触发Swing组件的重绘
  • 重绘操作又会再次触发getListCellRendererComponent调用,形成无限循环,导致控制台持续输出"recompose"
解决方案

通过缓存当前渲染状态,只在内容或状态发生变化时才更新ComposePanel的内容,打破循环。

修改后的ComposeRenderer代码:

class ComposeRenderer : JPanel(), ListCellRenderer<String> {
    private val composePanel = ComposePanel()
    private var currentText: String? = null
    private var currentIsSelected: Boolean = false
    private var currentHasFocus: Boolean = false

    init {
        layout = BoxLayout(this, BoxLayout.Y_AXIS)
        add(composePanel)
    }

    override fun getListCellRendererComponent(
        list: JList<out String>,
        text: String,
        index: Int,
        isSelected: Boolean,
        cellHasFocus: Boolean
    ): Component {
        // 仅当渲染状态变化时更新Compose内容
        if (text != currentText || isSelected != currentIsSelected || cellHasFocus != currentHasFocus) {
            currentText = text
            currentIsSelected = isSelected
            currentHasFocus = cellHasFocus
            print("recompose")
            composePanel.setContent {
                Text(
                    text,
                    color = if (isSelected) Color.White else Color.Black,
                    modifier = Modifier.background(if (isSelected) Color.Blue else Color.Transparent)
                )
            }
        }
        // 同步Swing原生选中样式(可选,保证和原生JList视觉一致)
        background = if (isSelected) list.selectionBackground else list.background
        foreground = if (isSelected) list.selectionForeground else list.foreground
        return this
    }
}

另外需要注意:JList的渲染器是复用实例,一个渲染器会被用来绘制多个列表项,因此必须确保每次调用时正确更新状态,同时避免无意义的Compose内容更新。

内容的提问来源于stack exchange,提问作者Eynnzerr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 05:26:16