与kotlin和tornadofx在listview上无休止地滚动

时间:2018-08-15 16:21:31

标签: listview javafx kotlin tornadofx

我想为listview实现无尽的滚动。

该应用程序部分接收来自服务器的数据,当前仅显示最后50行。 我正在使用tornadofx,下面是代码:

MainController.kt

private fun setAllServerHistoryList() {
        if (curSelectedLogId != -1) {
            serverHistoryList.clear()
            runAsync {
                serverAPI.getServerHistory(curSelectedLogId)
            } ui {
                serverHistoryList.setAll(it)
            }
        }
    }

MainView.kt

private val historyListView = listview(controller.serverHistoryList) {
        selectionModel = NoSelectionModel()
        cellFormat{...}
}

getServerHistory向服务器发出GET请求并返回已解析的数据,我可以使用offsetcount参数轻松地询问其他行。

但是我还没有意识到如何在列表的两边添加侦听器来检测amount of rows left(如果我不是从底部打开视图,而是在10000:10200范围内打开视图),并且如何在避免内存错误的情况下将数据添加到现有列表。

我已经阅读了很多有关同一问题的答案,但大多数(如果不是全部)都是关于android和java应用的。

预先感谢所有回复。

UPD具有重写的答案答案(对automatically swap-in https:// URLs欢呼):

import javafx.collections.FXCollections
import javafx.scene.control.ListCell
import javafx.util.Callback
import tornadofx.*


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

    var last_batch = 0

    val TOTAL_NUM_BATCHES = 10

    private val records = FXCollections.observableArrayList<ScrollableItemWrapper>()

    override val root = listview(records) {
        selectionModel = NoSelectionModel()

        cellFactory = Callback { listView ->

            object : ListCell<ScrollableItemWrapper>() {

                private val listener = ChangeListener<Number> { obs, ov, nv ->
                    if ( userData != null ) {
                        val scrollable = userData as ScrollableItemWrapper
                        val pos = listView.height - this.height - nv.toDouble()
                        if ( scrollable.lastItem && pos > 0 && (pos < this.height) && index < last_batch*50) {
                            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
                    }
                }

            }
        }
    }

    init {
        records.addAll( getNextBatch() )
    }

    private fun getNextBatch() : List<ScrollableItemWrapper> {
        if( currentBatch < TOTAL_NUM_BATCHES  ) {
            last_batch++
            return 1.rangeTo(50).map {
                ScrollableItemWrapper("Batch ${currentBatch} Record ${it}", it == 50)
            }.apply {
                currentBatch++
            }.toList()
        } else {
            return emptyList()
        }
    }
}

1 个答案:

答案 0 :(得分:1)

尝试一下我在普通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()
        }
    }
}