Tornadofx學習筆記(2)——FxRecyclerView控制元件的打造
阿新 • • 發佈:2020-02-06
Tornadofx是基於javafx的一個kotlin框架,用來寫些電腦版的小程式
基於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) | 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)//右擊
}