Tornadofx是基于javafx的一個kotlin架構,用來寫些電腦版的小程式
FxRecyclerView基于Scroll Pane控件,仿造Android中的RecyclerView,實作的一款tornadofx的控件
github
需求
由于我的之前做的幾個項目都是那種類似下載下傳清單的功能,藍奏雲批量下載下傳和m3u8下載下傳合并器
之是以抛棄了javafx的原因,是因為javafx中的動态添加控件比較麻煩,于是便是轉到了tornadofx這個架構來。
tornadofx中動态添加控件的步驟雖然比javafx中要簡單,但是,我還是覺得有些麻煩
于是我就參考了Android中的RecyclerView使用思路,打造出這個名為FxRecyclerView的控件,可以更加友善的動态進行控件的增删查改
功能介紹
- 動态添加ItemView
- 動态删除ItemView
- 動态更新itemView
- 快捷綁定單擊/右擊事件
功能示範
上波gif動态圖就能很好說明了
1.添加一組資料

2.添加一個資料
3.指定坐标插入一個資料
4.更新指定坐标的資料
5.單擊/右擊事件
6.移出指定坐标資料/移出所有資料
測試的jar包
使用
1.複制FxRecyclerView源碼
下載下傳我下面給出的kt檔案,複制到你的tornadofx項目中
FxRecyclerView.kt
2.建立bean類
這個沒啥好說的,就是一個存資料的bean類,如一個
Person
類,根據自己的情況與需求建立
data class Person(var name: String,var age:String)
3.建立ItemView
這個就是清單中的每一項View,需要繼承tornadofx中的View,我這裡就是顯示Person的name和age屬性,比較簡單示範一下
為了簡單起見,我的ItemView起名為ItemView,各位使用的過程中可以自由更改名字
import javafx.scene.control.Button
import javafx.scene.text.Text
import tornadofx.*
/**
*
* @author StarsOne
* @date Create in 2020/1/21 0021 18:36
* @description
*
*/
class ItemView : View("My View") {
var nameTv by singleAssign<Text>()
var ageTv by singleAssign<Text>()
var deleteBtn by singleAssign<Button>()
override val root = hbox {
spacing = 20.0
nameTv = text()
ageTv = text()
deleteBtn = button("删除")
}
}
data class Person(var name: String,var age:String)
4.界面添加FxRecyclerView
package com.example.demo.view
import tornadofx.*
class MainView : View("Hello TornadoFX") {
//建立FxRecyclerView需要使用到泛型,第一個是bean類,第二個是ItemView
val rv = FxRecyclerView<Person,ItemView>()
override val root = vbox {
//省略...
this+=rv
}
}
4.建立RvAdapter
RvAdapter是抽象類,是以得通過繼承并實作其中的幾個方法
//建立資料
val dataList = arrayListOf<Person>()
for (i in 0..10) {
dataList.add(Person("張三$i",(18+i).toString()))
}
//重寫RVAdapter的方法
val adapter = object :RVAdapter<Person,ItemView>(dataList){
override fun onRightClick(itemView: ItemView, position: Int) {
//右擊事件
println("右擊$position")
}
override fun onClick(itemView: ItemView, position: Int) {
//單擊事件
println("單擊$position")
}
override fun onCreateView(): ItemView {
//必須實作
//傳回ItemVIew的執行個體
return ItemView()
}
override fun onBindData(itemView: ItemView, bean: Person, position: Int) {
//必須實作
//将bean類中的資料綁定到itemView中
itemView.nameTv.text = bean.name
itemView.ageTv.text = bean.age
itemView.deleteBtn.setOnAction {
rv.remove(position)
}
}
}
//設定FxRecyclerView的adapter
rv.adapter = adapter
使用補充
PS:以下的方法都是rv調用(FxRecyclerView對象)
方法名 | 參數說明 | 方法說明 |
---|---|---|
setWidth(double) | double類型的數值 | 設定寬度 |
setHegiht(double) | 設定高度 | |
setIsShowHorizontalBar(String) | 顯示方式,never(不顯示) always(一直顯示) asneed(自動根據需要顯示) | 設定是否顯示水準滾動條 |
addList(arraylist) | ArrayList類型的一組資料 | 添加一組資料,同時更新視圖 |
addList(list) | List類型的一組資料 | |
add(beanT) | 添加一個資料,同時更新視圖 | |
add(bean,int) | 在清單的指定位置插入指定bean資料對應的itemView。 将目前位于該位置的itemView(如果有)和任何後續的itemView(向其索引添加一個)移動。 | |
update(bean,int) | 更新指定位置的資料及itemView視圖 | |
update(bean,oldBean) | 更新清單中存在的資料,替換為新的資料,同時更新視圖 | |
remove(bean) | 移出某個資料,同時更新視圖 | |
remove(index) | 移出清單中指定位置的資料,同時更新視圖 | |
removeAll() | 移出清單所有資料,同時更新視圖 |
FxRecyclerView源碼
由于kotlin檔案可以寫多個類,我的類都寫在了一個檔案裡
package com.starsone.fxrecyclerview.view
import javafx.scene.control.ScrollPane
import javafx.scene.input.MouseButton
import javafx.scene.layout.VBox
import tornadofx.*
/**
*
* @author StarsOne
* @date Create in 2020/1/20 0020 21:19
* @description
*
*/
class FxRecyclerView<beanT : Any, itemViewT : View> : View {
var adapter: RVAdapter<beanT, itemViewT>? = null
set(value) {
field = value
val adapter = value as RVAdapter<beanT, itemViewT>
val beanList = adapter.beanList
val itemViewList = adapter.itemViewList
for (index in 0 until beanList.size) {
val itemView = adapter.onCreateView()
//綁定bean資料到itemView
adapter.onBindData(itemView, beanList[index], index)
//itemView添加到清單中
itemViewList.add(itemView)
//添加到RecyclerView的主容器中
container.add(itemView)
itemView.root.setOnMouseClicked {
if (it.button == MouseButton.PRIMARY) {
//單擊事件回調
adapter.onClick(itemView, index)
}
if (it.button == MouseButton.SECONDARY) {
//右擊事件回調
adapter.onRightClick(itemView, index)
}
}
}
}
var container = vbox { }
constructor() {
root.add(container)
}
constructor(vBox: VBox) {
container = vBox
root.add(container)
}
override val root = scrollpane {
vbox { }
}
/**
* 設定寬度
*/
fun setWidth(width: Double) {
root.prefWidth = width
}
/**
* 設定[height]
*/
fun setHeight(height: Double) {
root.prefHeight = height
}
/**
* 設定水準滾動條的顯示方式
* @param way 顯示方式,never(不顯示) always(一直顯示) asneed(自動根據需要顯示)
*/
fun setIsShowVerticalBar(way: String) {
when (way) {
"never" -> root.hbarPolicy = ScrollPane.ScrollBarPolicy.NEVER
"always" -> root.hbarPolicy = ScrollPane.ScrollBarPolicy.ALWAYS
"asneed" -> root.hbarPolicy = ScrollPane.ScrollBarPolicy.AS_NEEDED
}
}
/**
* 添加一個清單的資料(arraylist)
*/
fun addList(beanList: ArrayList<beanT>) {
for (bean in beanList) {
add(bean)
}
}
/**
* 添加一個清單的資料(list)
*/
fun addList(beanList: List<beanT>) {
for (bean in beanList) {
add(bean)
}
}
fun add(bean: beanT) {
val beanList = adapter?.beanList
val itemViewList = adapter?.itemViewList
val index = beanList?.size as Int - 1
beanList.add(bean)
val itemView = adapter?.onCreateView() as itemViewT
//invoke onBindData method to bind the bean data to te item view
adapter?.onBindData(itemView, bean, index)
//add the item view in the item view list
itemViewList?.add(itemView)
//add to the recyclerview container
container.add(itemView)
itemView.root.setOnMouseClicked {
if (it.button == MouseButton.PRIMARY) {
//單擊
adapter?.onClick(itemView, index)
}
if (it.button == MouseButton.SECONDARY) {
//右擊
adapter?.onRightClick(itemView, index)
}
}
}
/**
* 在清單的指定位置插入指定bean資料對應的itemView。 将目前位于該位置的itemView(如果有)和任何後續的itemView(向其索引添加一個)移動。
* @param bean bean資料
* @param index 要插入的下标
*/
fun add(bean: beanT, index: Int) {
val beanList = adapter?.beanList
val itemViewList = adapter?.itemViewList
beanList?.add(index, bean)
val itemView = adapter?.onCreateView() as itemViewT
//invoke onBindData method to bind the bean data to te item view
adapter?.onBindData(itemView, bean, index)
//add the item view in the item view list
itemViewList?.add(index, itemView)
//add to the recyclerview container
container.addChildIfPossible(itemView.root, index)
itemView.root.setOnMouseClicked {
if (it.button == MouseButton.PRIMARY) {
//單擊
adapter?.onClick(itemView, index)
}
if (it.button == MouseButton.SECONDARY) {
//右擊
adapter?.onRightClick(itemView, index)
}
}
//更新點選事件的回調
for (i in index + 1 until itemViewList?.size as Int) {
val itemView1 = itemViewList[i]
adapter?.onBindData(itemView1, beanList!![i], i)
itemView1.root.setOnMouseClicked {
if (it.button == MouseButton.PRIMARY) {
//單擊
adapter?.onClick(itemView1, i)
}
if (it.button == MouseButton.SECONDARY) {
//右擊
adapter?.onRightClick(itemView1, i)
}
}
}
}
/**
* 更新指定位置的itemView
*/
fun update(bean: beanT, index: Int) {
remove(index)
add(bean, index)
}
/**
* 尋找清單中與oldBean相同的第一個元素,将其内容進行修改,同時更新界面的顯示
* @param bean 新的資料
* @param oldBean 清單中已存在的資料
*/
fun update(bean: beanT, oldBean: beanT) {
val beanList = adapter?.beanList
val index = beanList?.indexOf(oldBean) as Int
if (index != -1) {
update(bean, index)
} else {
println("清單中不存在該元素")
}
}
fun remove(bean: beanT) {
val beanList = adapter?.beanList
val index = beanList?.indexOf(bean) as Int
remove(index)
}
/**
* 移出指定下标的itemview
* @param index 下标
*/
fun remove(index: Int) {
val beanList = adapter?.beanList
val itemViewList = adapter?.itemViewList
beanList?.removeAt(index)
val itemView = itemViewList!![index]
itemView.removeFromParent()
itemViewList.remove(itemView)
for (i in index until beanList?.size as Int) {
adapter?.onBindData(itemViewList[i], beanList[i], i)
val itemView = itemViewList[i]
itemView.root.setOnMouseClicked {
if (it.button == MouseButton.PRIMARY) {
//單擊
adapter?.onClick(itemView, i)
}
if (it.button == MouseButton.SECONDARY) {
//右擊
adapter?.onRightClick(itemView, i)
}
}
}
}
/**
* 移出所有控件
*/
fun removeAll() {
val itemViewList = adapter?.itemViewList as ArrayList<itemViewT>
val beanList = adapter?.beanList as ArrayList<beanT>
for (itemView in itemViewList) {
itemView.removeFromParent()
}
itemViewList.removeAll(itemViewList)
beanList.removeAll(beanList)
}
}
/**
* 擴充卡
* @author StarsOne
* @date Create in 2020/1/20 0020 21:51
* @description
*
*/
abstract class RVAdapter<beanT : Any, itemViewT : View> {
val beanList = arrayListOf<beanT>()
val itemViewList = arrayListOf<itemViewT>()
constructor(bean: beanT) {
beanList.add(bean)
}
constructor(beanList: List<beanT>) {
this.beanList.addAll(beanList)
}
constructor(beanList: ArrayList<beanT>) {
this.beanList.addAll(beanList)
}
/**
* 設定傳回ItemView
*/
abstract fun onCreateView(): itemViewT
abstract fun onBindData(itemView: itemViewT, bean: beanT, position: Int)
abstract fun onClick(itemView: itemViewT, position: Int)//單擊
// abstract fun onDoubleClick(itemView: itemViewT, position: Int)//輕按兩下
abstract fun onRightClick(itemView: itemViewT, position: Int)//右擊
}
提問之前,請先看提問須知
點選右側圖示發起提問
或者加入QQ群一起學習
TornadoFx學習交流群:1071184701