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.添加一組數據

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



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM