天天看點

Tornadofx學習筆記(2)——FxRecyclerView控件的打造

Tornadofx是基于javafx的一個kotlin架構,用來寫些電腦版的小程式

FxRecyclerView基于Scroll Pane控件,仿造Android中的RecyclerView,實作的一款tornadofx的控件

github

需求

由于我的之前做的幾個項目都是那種類似下載下傳清單的功能,藍奏雲批量下載下傳和m3u8下載下傳合并器

之是以抛棄了javafx的原因,是因為javafx中的動态添加控件比較麻煩,于是便是轉到了tornadofx這個架構來。

tornadofx中動态添加控件的步驟雖然比javafx中要簡單,但是,我還是覺得有些麻煩

于是我就參考了Android中的RecyclerView使用思路,打造出這個名為FxRecyclerView的控件,可以更加友善的動态進行控件的增删查改

功能介紹

  • 動态添加ItemView
  • 動态删除ItemView
  • 動态更新itemView
  • 快捷綁定單擊/右擊事件

功能示範

上波gif動态圖就能很好說明了

1.添加一組資料

Tornadofx學習筆記(2)——FxRecyclerView控件的打造

2.添加一個資料

Tornadofx學習筆記(2)——FxRecyclerView控件的打造

3.指定坐标插入一個資料

Tornadofx學習筆記(2)——FxRecyclerView控件的打造

4.更新指定坐标的資料

Tornadofx學習筆記(2)——FxRecyclerView控件的打造

5.單擊/右擊事件

Tornadofx學習筆記(2)——FxRecyclerView控件的打造

6.移出指定坐标資料/移出所有資料

Tornadofx學習筆記(2)——FxRecyclerView控件的打造

測試的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)//右擊
}


           

提問之前,請先看提問須知

點選右側圖示發起提問

Tornadofx學習筆記(2)——FxRecyclerView控件的打造

或者加入QQ群一起學習

Tornadofx學習筆記(2)——FxRecyclerView控件的打造

TornadoFx學習交流群:1071184701

Tornadofx學習筆記(2)——FxRecyclerView控件的打造
Tornadofx學習筆記(2)——FxRecyclerView控件的打造