Closed lijingdoc closed 1 year ago
For dynamic data, we recommend using TreeNodeGenerator
Is there an example for this? @dingyi222666
Like this.
If you don't understand it for now, it's okay, I may improve the DataSource
api in the coming days.
Thanks, @dingyi222666 I hope to get new update asap.
Yes, but I still recommend using TreeNodeGenerator
to generate the nodes. When I update the API for DataSource
I may also update the example using TreeNodeGenerator
.
Ok, got, thanks
No, it may not be necessary to update the API of the DataSource
.
If this data does not need to be updated later, then this will work.
val item = Item(
"head", listOf(
Item(
"1", listOf(
Item("11", listOf()),
Item("12", listOf())
)
),
Item(
"2", listOf(
Item("21", listOf()),
Item("22", listOf())
)
)
)
)
fun DataSourceScope<Item>.getSubBranches(item: Item): List<Unit> {
val branches: MutableList<Unit> = mutableListOf()
item.childItems.forEach {
branches.add(
Branch(it.name, item) {
getSubBranches(it)
}
)
}
return branches
}
// The dataCreator is not needed because the data is already provided in Branch(name:String,data,T).
// The dataCreator is called only when the set data is null.
return buildTree {
getSubBranches(item)
}
This is an example of using TreeNodeGenerator
.
private fun createTree(): Tree<Item> {
val item = Item(
"head", listOf(
Item(
"1", listOf(
Item("11", listOf()),
Item("12", listOf())
)
),
Item(
"2", listOf(
Item("21", listOf()),
Item("22", listOf())
)
)
)
)
return Tree.createTree<Item>().apply {
generator = ItemTreeNodeGenerator(item)
initTree()
}
}
inner class ItemTreeNodeGenerator(
private val rootItem: Item
) : TreeNodeGenerator<Item> {
override suspend fun fetchNodeChildData(targetNode: TreeNode<Item>): Set<Item> {
return targetNode.requireData().childItems.toSet()
}
override fun createNode(
parentNode: TreeNode<Item>,
currentData: Item,
tree: AbstractTree<Item>
): TreeNode<Item> {
return TreeNode(
data = currentData,
depth = parentNode.depth + 1,
name = currentData.name,
id = tree.generateId(),
hasChild = currentData.childItems.isNotEmpty(),
// It should be taken from the Item
isChild = currentData.childItems.isNotEmpty(),
expand = false
)
}
override fun createRootNode(): TreeNode<Item> {
return TreeNode(
data = rootItem,
depth = 0,
name = rootItem.name,
id = Tree.ROOT_NODE_ID,
hasChild = true,
isChild = true
)
}
}
@dingyi222666 thanks for your kind help It works well.
@dingyi222666 I have one more question.
If there are several root nodes, then how can I implement it? For example:
val items: List<Item> =
listOf(
Item(
"root1", listOf(
Item("1", listOf(
Item("11", listOf()),
Item("12", listOf())
)),
Item("2", listOf(
Item("21", listOf()),
Item("22", listOf())
))
)
),
Item(
"root2", listOf(
Item("1", listOf(
Item("11", listOf()),
Item("12", listOf())
)),
Item("2", listOf(
Item("21", listOf()),
Item("22", listOf())
))
)
)
)
If you are using TreeNodeGenerator
, this will work.
private fun createTree(): Tree<Item> {
val items =
listOf(
Item(
"root1", listOf(
Item("1", listOf(
Item("11", listOf()),
Item("12", listOf())
)),
Item("2", listOf(
Item("21", listOf()),
Item("22", listOf())
))
)
),
Item(
"root2", listOf(
Item("1", listOf(
Item("11", listOf()),
Item("12", listOf())
)),
Item("2", listOf(
Item("21", listOf()),
Item("22", listOf())
))
)
)
)
val rootItem = Item("root_not_show", items)
return Tree.createTree<Item>().apply {
generator = ItemTreeNodeGenerator(rootItem)
initTree()
}
}
inner class ItemTreeNodeGenerator(
private val rootItem: Item
) : TreeNodeGenerator<Item> {
override suspend fun fetchNodeChildData(targetNode: TreeNode<Item>): Set<Item> {
return targetNode.requireData().childItems.toSet()
}
override fun createNode(
parentNode: TreeNode<Item>,
currentData: Item,
tree: AbstractTree<Item>
): TreeNode<Item> {
return TreeNode(
data = currentData,
depth = parentNode.depth + 1,
name = currentData.name,
id = tree.generateId(),
hasChild = currentData.childItems.isNotEmpty(),
// It should be taken from the Item
isChild = currentData.childItems.isNotEmpty(),
expand = false
)
}
override fun createRootNode(): TreeNode<Item> {
return TreeNode(
data = rootItem,
// Set to -1 to not show the root node
depth = -1,
name = rootItem.name,
id = Tree.ROOT_NODE_ID,
hasChild = true,
isChild = true
)
}
}
If you use buildTree
DSL, this will work.
private fun createTree(): Tree<DataSource<Item>> {
val items =
listOf(
Item(
"root1", listOf(
Item("1", listOf(
Item("11", listOf()),
Item("12", listOf())
)),
Item("2", listOf(
Item("21", listOf()),
Item("22", listOf())
))
)
),
Item(
"root2", listOf(
Item("1", listOf(
Item("11", listOf()),
Item("12", listOf())
)),
Item("2", listOf(
Item("21", listOf()),
Item("22", listOf())
))
)
)
)
fun DataSourceScope<Item>.getSubBranches(items:List<Item>) {
items.forEach {
Branch(it.name, it) {
getSubBranches(it.childItems)
}
}
}
return buildTree {
getSubBranches(items)
}
}
Thank you, @dingyi222666 It works well.
@dingyi222666 Now the problem is how do i update the data of the TreeView, if i'm using the TreeNodeGenerator and still maintaining the hierarchy of the Tree
@dingyi222666 Now the problem is how do i update the data of the TreeView, if i'm using the TreeNodeGenerator and still maintaining the hierarchy of the Tree
tree.refresh()
?
@dingyi222666 Now the problem is how do i update the data of the TreeView, if i'm using the TreeNodeGenerator and still maintaining the hierarchy of the Tree
tree.refresh()
?
that doesn't work, i'm loading a folder from the device, with the functionality of creating, delete, rename etc, which i have added already and it works fine, but calling tree.refresh()
seems not work though i'm calling it from a ViewBinder, which the constructor has the Tree instance
@dingyi222666 Now the problem is how do i update the data of the TreeView, if i'm using the TreeNodeGenerator and still maintaining the hierarchy of the Tree
tree.refresh()
?that doesn't work, i'm loading a folder from the device, with the functionality of creating, delete, rename etc, which i have added already and it works fine, but calling
tree.refresh()
seems not work though i'm calling it from a ViewBinder, which the constructor has the Tree instance
Can you show me how your TreeNodeGenerator
is implemented?
@dingyi222666 Now the problem is how do i update the data of the TreeView, if i'm using the TreeNodeGenerator and still maintaining the hierarchy of the Tree
tree.refresh()
?that doesn't work, i'm loading a folder from the device, with the functionality of creating, delete, rename etc, which i have added already and it works fine, but calling
tree.refresh()
seems not work though i'm calling it from a ViewBinder, which the constructor has the Tree instance
No, it should call treeView.refresh()
.
tree.refresh
may only refresh the tree data, and not synchronize it to the treeView.
@dingyi222666
non of the treeView.refresh()
and treeView.refresh(false, node)
works or do anything here, it was called from the ViewBinder
here is the link
the treeView.refresh()
used to work when i was using kotlin dsl thingy, since when i switched to TreeNodeGenerator
it doesn't work, i think the bug is from the library
@BlueCatSoftware
You also need to update your FileSet
data.
The data flow should look like this
raw data -> node generator -> tree -> treeView
@BlueCatSoftware You also need to update your
FileSet
data.The data flow should look like this
raw data -> node generator -> tree -> treeView
private suspend fun refreshTreeView(treeView: TreeView<FileSet>, node: TreeNode<FileSet>){
val rootItem = FileSet(project.root,
transverseTree(project.root) as MutableSet<FileSet>
)
val generator = FileTreeNodeGenerator(rootItem)
val tree = Tree.createTree<FileSet>().apply {
this.generator = generator
initTree()
}
treeView.tree = tree
treeView.refresh(false, node)
}
this works but doesn't retain the tree hierarchy, it literally reloads and collapses the tree.
At this point an example will definitely help
@BlueCatSoftware See https://github.com/dingyi222666/UnLuacTool/blob/main/app/src/main/java/com/dingyi/unluactool/ui/editor/main/FileViewerFragment.kt#L361
yeah i am aware of this method, it fetches the data. i'm still confused, the problem is there's no method to update the node's data. i know how to fetch nodes data but doesn't know how to manipulate it. I'm still very confused with the updating
@BlueCatSoftware The FileSet class should have a parent attribute that points to the parent file. When updating, it needs to get the parent’s FileSet and refresh its child files.
Actually, I recommend you to use File directly, so that the node generator only needs File.listFile
.
@BlueCatSoftware The FileSet class should have a parent attribute that points to the parent file. When updating, it needs to get the parent’s FileSet and refresh its child files.
Actually, I recommend you to use File directly, so that the node generator only needs
File.listFile
.
i get your point, here is my problem UPDATING
, how do i exactly update.
The big question is the UPDATING
, what am i swapping or editing ?, is it the generator or what exactly ?
Sorry for too much question lol.
@BlueCatSoftware I don't think you fully understand what I'm saying.
Let me give you a sample code.
class FileTreeNodeGenerator(val rootItem: FileSet) : TreeNodeGenerator<FileSet> {
override fun createNode(
parentNode: TreeNode<FileSet>,
currentData: FileSet,
tree: AbstractTree<FileSet>
): TreeNode<FileSet> {
return TreeNode(
currentData,
parentNode.depth + 1,
currentData.file.name,
tree.generateId(),
currentData.subDir.isNotEmpty(),
currentData.subDir.isNotEmpty(),
false
)
}
override suspend fun fetchNodeChildData(targetNode: TreeNode<FileSet>): Set<FileSet> = withContext(Dispatcher.IO) {
// refresh data here
val fileSet = targetNode.requireData()
fileSet.subDir.clear()
for (file in dir.listFiles()) {
fileSet.subDir.add(FileSet(fileSet))
}
fileSet.subDir.toSet()
}
override fun createRootNode(): TreeNode<FileSet> {
return TreeNode(
data = rootItem,
depth = 0,
name = rootItem.file.name,
id = Tree.ROOT_NODE_ID,
hasChild = true,
isChild = true
)
}
}
data class FileSet(val file: File, val subDir: MutableSet<FileSet> = mutableSetOf())
@dingyi222666 oh this, i'm gonna try it out. Thanks by the way
override suspend fun fetchNodeChildData(targetNode: TreeNode<FileSet>): Set<FileSet> =
withContext(Dispatchers.IO) {
val set = targetNode.requireData().subDir
set.clear()
val files = targetNode.requireData().file.listFiles() ?: return@withContext set
for (file in files) {
when {
file.isFile -> set.add(FileSet(file))
file.isDirectory -> {
val tempSet = mutableSetOf<FileSet>().apply {
addAll(transverseTree(file))
}
set.add(FileSet(file, subDir = tempSet))
}
}
}
return@withContext set
}
this definitely worked for me, i didn't know treeView.refresh()
calls fetchNodeChildData()
internally though.
what if the data is dynamic, e.g you are not loading data from the device file storage, but rather you want to insert data based on user's interaction?
override suspend fun fetchNodeChildData(targetNode: TreeNode<FileSet>): Set<FileSet> = withContext(Dispatchers.IO) { val set = targetNode.requireData().subDir set.clear() val files = targetNode.requireData().file.listFiles() ?: return@withContext set for (file in files) { when { file.isFile -> set.add(FileSet(file)) file.isDirectory -> { val tempSet = mutableSetOf<FileSet>().apply { addAll(transverseTree(file)) } set.add(FileSet(file, subDir = tempSet)) } } } return@withContext set }
this definitely worked for me, i didn't know
treeView.refresh()
callsfetchNodeChildData()
internally though. what if the data is dynamic, e.g you are not loading data from the device file storage, but rather you want to insert data based on user's interaction?
Let’s take another look at this data flow: raw data -> node generator -> tree -> treeView. If your raw data comes from multiple sources, then you need to provide your raw data externally. To implement your example, you need to provide raw data (which is FileSet). This data needs to be refreshed outside the node generator. I’m afraid I can’t provide any more code examples, I think you need to think more about how to implement it, this is actually a simple problem.
@BlueCatSoftware See here
I'm soon to release version 1.2.0, which will improve some things, so feel free to update and use it.
I have dynamic data, I want to build a tree from it. Let me put an example.
I have my own
Item
I use dynamic Item. For example:
How can I create tree for this dynamic item?
I tried the above code, but I am getting 1 dimension tree and there are no subbranches. How can I approach it? Thank you!