使用kotlin和tornadofx在listview上无限滚动

使用kotlin和tornadofx在listview上无限滚动,listview,javafx,kotlin,tornadofx,Listview,Javafx,Kotlin,Tornadofx,我想实现listview的无止境滚动 该应用程序从服务器部分接收数据,目前只显示最后50行。 我使用的是TornadFX,代码如下: MainController.kt: private fun setAllServerHistoryList() { if (curSelectedLogId != -1) { serverHistoryList.clear() runAsync { serverAPI

我想实现listview的无止境滚动

该应用程序从服务器部分接收数据,目前只显示最后50行。 我使用的是
TornadFX
,代码如下:

MainController.kt

private fun setAllServerHistoryList() {
        if (curSelectedLogId != -1) {
            serverHistoryList.clear()
            runAsync {
                serverAPI.getServerHistory(curSelectedLogId)
            } ui {
                serverHistoryList.setAll(it)
            }
        }
    }
private val historyListView = listview(controller.serverHistoryList) {
        selectionModel = NoSelectionModel()
        cellFormat{...}
}
MainView.kt

private fun setAllServerHistoryList() {
        if (curSelectedLogId != -1) {
            serverHistoryList.clear()
            runAsync {
                serverAPI.getServerHistory(curSelectedLogId)
            } ui {
                serverHistoryList.setAll(it)
            }
        }
    }
private val historyListView = listview(controller.serverHistoryList) {
        selectionModel = NoSelectionModel()
        cellFormat{...}
}
getServerHistory
向服务器发出
GET
请求并返回解析数据,我可以使用
offset
count
参数轻松询问其他行

但我还没有意识到如何添加监听器来检测列表两侧的
行数(如果我不是从底部而是在10000:10200范围内打开视图),以及如何向现有列表添加数据以避免内存错误

我已经读过很多关于同一个问题的答案,但大多数(如果不是全部的话)都是关于android和java应用程序的

提前感谢所有回复

UPD和改写的答案解决方案(干杯):

导入javafx.collections.FXCollections
导入javafx.scene.control.ListCell
导入javafx.util.Callback
导入tornadofx*
数据类ScrollableItemWrapper(val数据:字符串,val lastItem:Boolean=false){
val id=nextId()//始终在增加
伴星{
private var idgen=1//faux静态类成员
fun nextId()=idgen++
}
}
类endlesscrollview:View(“无止境滚动”){
var currentBatch=1
var last_批次=0
val总批次数=10
private val records=FXCollections.observearraylist()
覆盖val root=listview(记录){
selectionModel=NoSelectionModel()
cellFactory=回调{listView->
对象:ListCell(){
私有val侦听器=更改侦听器{obs,ov,nv->
if(userData!=null){
val scrollable=用户数据作为ScrollableItemWrapper
val pos=listView.height-this.height-nv.toDouble()
如果(可滚动的.lastItem&&pos>0&&(pos
试试我在普通JavaFX应用程序中使用的代码。它依赖于通过多个版本的JavaFX产生的副作用。包装器类用于将数据与一个标志配对,该标志指示一个项是否是批处理中的最后一个项。在单元工厂中,当layoutY属性被赋予空间时,侦听器被注册并激活。如果layoutY从0增加(意味着显示单元格),并且设置了最后一个项标志,则会获取更多数据

该数据已添加,但滚动位置已保存,因此ListView不会跳过整个获取集

data class ScrollableItemWrapper(val data : String, val lastItem : Boolean = false) {

  val id = nextId() // always increasing

  companion object {
    private var idgen = 1 // faux static class member
    fun nextId() = idgen++
  }
}
class EndlessScrollView : View("Endless Scroll") {

    var currentBatch = 1

    val TOTAL_NUM_BATCHES = 10

    val records = mutableListOf<ScrollableItemWrapper>().observable()

    override val root = listview(records) {
        cellFactory = Callback {

            object : ListCell<ScrollableItemWrapper>() {

                private val listener = ChangeListener<Number> { obs, ov, nv ->
                    if ( userData != null ) {
                        val scrollable = userData as ScrollableItemWrapper
                        if( scrollable.lastItem &&
                            ((listView.height - this.height - nv.toDouble()) > 0.0)) {

                            val currentPos = index
                            runAsync {
                                getNextBatch()
                            } ui {
                                listView.scrollTo(currentPos)
                                records.addAll( it )
                            }
                        }
                    }
                }

                init {
                    layoutYProperty().addListener(listener);
                }

                override fun updateItem(item: ScrollableItemWrapper?, empty: Boolean) {
                    super.updateItem(item, empty)
                    if( item != null && !empty ) {
                        text = "[${item.id}]" + item.data!!
                        userData = item
                    } else {
                        text = null
                        userData = null
                    }
                }

                protected fun finalize() {
                    layoutYProperty().removeListener(listener)
                }
            }
        }
    }

    init {
        records.addAll( getNextBatch() )
    }

    private fun getNextBatch() : List<ScrollableItemWrapper> {
        if( currentBatch <= TOTAL_NUM_BATCHES  ) {
            return 'A'.rangeTo('Z').map {
                ScrollableItemWrapper("Batch ${currentBatch} Record ${it}", it == 'Z')
            }.apply {
                currentBatch++
            }.toList()
        } else {
            return emptyList()
        }
    }
}
数据类ScrollableItemWrapper(val数据:字符串,val lastItem:Boolean=false){
val id=nextId()//始终在增加
伴星{
private var idgen=1//faux静态类成员
fun nextId()=idgen++
}
}
类endlesscrollview:View(“无止境滚动”){
var currentBatch=1
val总批次数=10
val records=mutableListOf().observable()
覆盖val root=listview(记录){
cellFactory=回调{
对象:ListCell(){
私有val侦听器=更改侦听器{obs,ov,nv->
if(userData!=null){
val scrollable=用户数据作为ScrollableItemWrapper
如果(scrollable.lastItem)&&
((listView.height-this.height-nv.toDouble())>0.0)){
val currentPos=索引
运行异步{
getNextBatch()
}用户界面{
listView.scrollTo(当前位置)
records.addAll(it)
}
}
}
}
初始化{
layoutYProperty().addListener(listener);
}
重写fun updateItem(项:ScrollableItemWrapper?,空:布尔值){
super.updateItem(项,空)
如果(项!=null&&!空){
text=“[${item.id}]”+item.data!!
userData=item
}否则{
text=null
userData=null
}
}
受保护的文件(){
layoutYProperty().RemovelListener(侦听器)