Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic data not working. #14

Open
simphonydeveloper opened this issue Nov 27, 2024 · 11 comments
Open

Dynamic data not working. #14

simphonydeveloper opened this issue Nov 27, 2024 · 11 comments

Comments

@simphonydeveloper
Copy link

class FileActivity : AppCompatActivity() {

    private lateinit var binding: ActivityFileBinding

    //private lateinit var tree: Tree<TreeNode2>
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityFileBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val trees = createTree()

        (binding.treeview as TreeView<TreeNode2>).apply {
            bindCoroutineScope(lifecycleScope)
            this.tree = trees
            binder = ViewBinder()
            nodeEventListener = binder as TreeNodeEventListener<TreeNode2>
        }

        lifecycleScope.launch {
            binding.treeview.refresh()
        }
    }

    private fun createTree(): Tree<TreeNode2> {

        val rootNode1 = TreeNode2("1", "Root 1")
        val child1 =
            TreeNode2("1.1", "Child 1", parent = rootNode1)
        val child2 =
            TreeNode2("1.2", "Child 2", parent = rootNode1)
        val grandChild1 =
            TreeNode2("1.2.1", "Grandchild 1", parent = child2)
        child2.children.add(grandChild1)
        rootNode1.children.addAll(listOf(child1, child2))
        return Tree.createTree<TreeNode2>().apply {
            generator = ItemTreeNodeGenerator(rootNode1)
            initTree()
        }
    }

}


 class ViewBinder : TreeViewBinder<TreeNode2>(),
    TreeNodeEventListener<TreeNode2> {

    override fun createView(parent: ViewGroup, viewType: Int): View {
        val layoutInflater = LayoutInflater.from(parent.context)
        return if (viewType == 1) {
            ItemDirBinding.inflate(layoutInflater, parent, false).root
        } else {
            ItemFileBinding.inflate(layoutInflater, parent, false).root
        }
    }

    override fun getItemViewType(node: TreeNode<TreeNode2>): Int {
        if (node.data!!.children.isNotEmpty()) {
            return 1
        }
        return 0
    }

    override fun bindView(
        holder: TreeView.ViewHolder,
        node: TreeNode<TreeNode2>,
        listener: TreeNodeEventListener<TreeNode2>
    ) {
        if (node.data!!.children.isNotEmpty()) {
            applyDir(holder, node)
        } else {
            applyFile(holder, node)
        }

        val itemView = holder.itemView.findViewById<Space>(R.id.space)

        itemView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
            width = node.depth * 22
        }

    }

    private fun applyFile(holder: TreeView.ViewHolder, node: TreeNode<TreeNode2>) {
        val binding = ItemFileBinding.bind(holder.itemView)
        binding.tvName.text = node.data!!.name.toString()
    }

    private fun applyDir(holder: TreeView.ViewHolder, node: TreeNode<TreeNode2>) {
        val binding = ItemDirBinding.bind(holder.itemView)
        binding.tvName.text = node.data!!.name.toString()

        binding
            .ivArrow
            .animate()
            .rotation(if (node.data!!.isExpanded) 90f else 0f)
            .setDuration(200)
            .start()
    }


    override fun onClick(node: TreeNode<TreeNode2>, holder: TreeView.ViewHolder) {
        if (node.data!!.children.isNotEmpty()) {
            applyDir(holder, node)
        } else {
            //Toast.makeText(this@MainActivity, "Clicked ${node.name}", Toast.LENGTH_LONG).show()
        }
    }

    override fun onToggle(
        node: TreeNode<TreeNode2>,
        isExpand: Boolean,
        holder: TreeView.ViewHolder
    ) {
        applyDir(holder, node)
    }
}

 class ItemTreeNodeGenerator(
    private val rootItem: TreeNode2
) : TreeNodeGenerator<TreeNode2> {
     suspend fun fetchNodeChildData(targetNode: TreeNode<TreeNode2>): Set<TreeNode2> {
         return targetNode.requireData().children.toSet()
     }

    override fun createNode(
        parentNode: TreeNode<TreeNode2>,
        currentData: TreeNode2,
        tree: AbstractTree<TreeNode2>
    ): TreeNode<TreeNode2> {
        return TreeNode(
            data = currentData,
            depth = parentNode.depth + 1,
            name = currentData.name,
            id = tree.generateId(),
            hasChild = currentData.children.isNotEmpty(),
            // It should be taken from the Item
            isChild = currentData.children.isNotEmpty(),
            expand = false
        )
    }

    override fun createRootNode(): TreeNode<TreeNode2> {
        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
        )
    }

     override suspend fun fetchChildData(targetNode: TreeNode<TreeNode2>): Set<TreeNode2> {
         return targetNode.requireData().children.toSet()
     }


 }
data class TreeNode2(
    val id: String,
    val name: String,
    val children: MutableList<TreeNode2> = mutableListOf(),
    var isExpanded: Boolean = false,
    var level: Int = 0,
    var isSwitchOn: Boolean = false,
    var parent: TreeNode2? = null 
)

Part of my inspiration comes from here.
#4
It seems to have entered an infinite loop problem.

this is my version
io.github.dingyi222666:treeview:1.3.1

Thanks
ERROR:

AndroidRuntime          com.example.myapplicationtest        E  	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:61)
                                                                                                    	at java.util.ArrayList.hashCodeRange(ArrayList.java:684)
                                                                                                    	at java.util.ArrayList.hashCode(ArrayList.java:671)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:19)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:61)
                                                                                                    	at java.util.ArrayList.hashCodeRange(ArrayList.java:684)
                                                                                                    	at java.util.ArrayList.hashCode(ArrayList.java:671)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:19)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:61)
                                                                                                    	at java.util.ArrayList.hashCodeRange(ArrayList.java:684)
                                                                                                    	at java.util.ArrayList.hashCode(ArrayList.java:671)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:19)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:61)
                                                                                                    	at java.util.ArrayList.hashCodeRange(ArrayList.java:684)
                                                                                                    	at java.util.ArrayList.hashCode(ArrayList.java:671)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:19)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:61)
                                                                                                    	at java.util.ArrayList.hashCodeRange(ArrayList.java:684)
                                                                                                    	at java.util.ArrayList.hashCode(ArrayList.java:671)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:19)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:61)
                                                                                                    	at java.util.ArrayList.hashCodeRange(ArrayList.java:684)
                                                                                                    	at java.util.ArrayList.hashCode(ArrayList.java:671)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:19)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:61)
                                                                                                    	at java.util.ArrayList.hashCodeRange(ArrayList.java:684)
                                                                                                    	at java.util.ArrayList.hashCode(ArrayList.java:671)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:19)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:61)
                                                                                                    	at java.util.ArrayList.hashCodeRange(ArrayList.java:684)
                                                                                                    	at java.util.ArrayList.hashCode(ArrayList.java:671)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:19)
                                                                                                    	at com.example.myapplicationtest.model.TreeNode2.hashCode(Unknown Source:61)
                                                                                                    	at java.util.HashMap.hash(HashMap.java:337)
                                                                                                    	at java.util.HashMap.put(HashMap.java:617)
                                                                                                    	at java.util.HashSet.add(HashSet.java:229)
                                                                                                    	at kotlin.collections.CollectionsKt___CollectionsKt.toCollection(_Collections.kt:1296)
                                                                                                    	at kotlin.collections.CollectionsKt___CollectionsKt.toSet(_Collections.kt:1348)
                                                                                                    	at com.example.myapplicationtest.ItemTreeNodeGenerator.fetchChildData(FileActivity.kt:197)
                                                                                                    	at io.github.dingyi222666.view.treeview.Tree.refreshInternal(trees.kt:206)
                                                                                                    	at io.github.dingyi222666.view.treeview.Tree.refresh(trees.kt:231)
                                                                                                    	at io.github.dingyi222666.view.treeview.Tree.getChildNodesInternal(trees.kt:103)
                                                                                                    	at io.github.dingyi222666.view.treeview.Tree.visitInternal(trees.kt:338)
                                                                                                    	at io.github.dingyi222666.view.treeview.Tree.visit(trees.kt:297)
                                                                                                    	at io.github.dingyi222666.view.treeview.AbstractTreeKt.toSortedList(AbstractTree.kt:408)
                                                                                                    	at io.github.dingyi222666.view.treeview.AbstractTreeKt.toSortedList$default(AbstractTree.kt:383)
                                                                                                    	at io.github.dingyi222666.view.treeview.TreeView.refresh(TreeView.kt:173)
                                                                                                    	at io.github.dingyi222666.view.treeview.TreeView.refresh$default(TreeView.kt:157)
                                                                                                    	at com.example.myapplicationtest.FileActivity$onCreate$2.invokeSuspend(FileActivity.kt:63)
                                                                                                    	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
                                                                                                    	at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:367)
                                                                                                    	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
                                                                                                    	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
                                                                                                    	at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
                                                                                                    	at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
2024-11-27 13:28:55.527 10530-10530 AndroidRuntime          com.example.myapplicationtest        E  	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
                                                                                                    	at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1)
                                                                                                    	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
                                                                                                    	at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1)
                                                                                                    	at com.example.myapplicationtest.FileActivity.onCreate(FileActivity.kt:62)
                                                                                                    	at android.app.Activity.performCreate(Activity.java:9002)
                                                                                                    	at android.app.Activity.performCreate(Activity.java:8980)
                                                                                                    	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1526)
                                                                                                    	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4030)
                                                                                                    	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4235)
                                                                                                    	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:112)
                                                                                                    	at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:174)
                                                                                                    	at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:109)
                                                                                                    	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:81)
                                                                                                    	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2636)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:107)
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:232)
                                                                                                    	at android.os.Looper.loop(Looper.java:317)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:8705)
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886)
@dingyi222666
Copy link
Owner

dingyi222666 commented Nov 27, 2024

Your TreeNode2 class should not contain the parent field. Remove it and try again.

@simphonydeveloper
Copy link
Author

thank you,
it's working.
i want to know,Can this component work well with Switch?
Thanks again.

@dingyi222666
Copy link
Owner

It's okay. Check out the code

open fun getCheckableView(node: TreeNode<T>, holder: TreeView.ViewHolder): Checkable? {
that supports components that implement the Checkable interface.

@simphonydeveloper
Copy link
Author

微信截图_20241127154717
I haven't implemented getCheckableView. I want to update all child nodes below when changing a parent node, but I couldn't find a parent class in ViewBinding that can operate on all data properties or methods. Perhaps what I did was wrong.

Thanks

@dingyi222666
Copy link
Owner

First implement getCheckableView.

override fun getCheckableView(
node: TreeNode<DataSource<String>>,
holder: TreeView.ViewHolder
): Checkable {
return if (node.isChild) {
ItemDirBinding.bind(holder.itemView).checkbox
} else {
ItemFileBinding.bind(holder.itemView).checkbox
}

Set the selection mode to MULTIPLE_WITH_CHILDREN, then operate on the treeView by calling selectNode to select the corresponding parent node. When this happens, the child nodes will also be updated.

// select node and it's children
selectionMode = TreeView.SelectionMode.MULTIPLE_WITH_CHILDREN
selectNode(binding.treeview.tree.rootNode, true)
expandAll()
selectionMode = TreeView.SelectionMode.MULTIPLE_WITH_CHILDREN

@simphonydeveloper
Copy link
Author

Sorry,I have reviewed your example, and you executed it through the menu item. How do I perform this operation on the switch of a certain node?

Thanks

 class ViewBinder : TreeViewBinder<TreeNode2>(),
    TreeNodeEventListener<TreeNode2> {

    override fun createView(parent: ViewGroup, viewType: Int): View {
        val layoutInflater = LayoutInflater.from(parent.context)
        return if (viewType == 1) {
            ItemDirBinding.inflate(layoutInflater, parent, false).root
        } else {
            ItemFileBinding.inflate(layoutInflater, parent, false).root
        }
    }

    override fun getItemViewType(node: TreeNode<TreeNode2>): Int {
        if (node.data!!.children.isNotEmpty()) {
            return 1
        }
        return 0
    }

    override fun bindView(
        holder: TreeView.ViewHolder,
        node: TreeNode<TreeNode2>,
        listener: TreeNodeEventListener<TreeNode2>
    ) {
        if (node.data!!.children.isNotEmpty()) {
            applyDir(holder, node)
        } else {
            applyFile(holder, node)
        }

        val itemView = holder.itemView.findViewById<Space>(R.id.space)

//        (getCheckableView(node, holder) as Switch).apply {
//            isVisible = node.selected
//            isSelected = node.selected
//        }

        itemView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
            width = node.depth * 22
        }

    }

     override fun getCheckableView(
         node: TreeNode<TreeNode2>,
         holder: TreeView.ViewHolder
     ): Checkable? {

         return if (node.isChild) {
             ItemDirBinding.bind(holder.itemView).switchOn
         } else {
             ItemFileBinding.bind(holder.itemView).switchOn
         }
         //return  super.getCheckableView(node, holder)
     }

    private fun applyFile(holder: TreeView.ViewHolder, node: TreeNode<TreeNode2>) {
        val binding = ItemFileBinding.bind(holder.itemView)
        binding.tvName.text = node.data!!.name.toString()
    }

     private fun applyDir(holder: TreeView.ViewHolder, node: TreeNode<TreeNode2>) {
         val binding = ItemDirBinding.bind(holder.itemView)
         binding.tvName.text = node.data!!.name.toString()
         binding.switchOn.isChecked =  node.data!!.isSwitchOn
         binding.switchOn.setOnCheckedChangeListener(object :
             CompoundButton.OnCheckedChangeListener {
             override fun onCheckedChanged(p0: CompoundButton?, p1: Boolean) {
//                 node.data!!.isSwitchOn = p1
//                 if (node.hasChild) {
//                     flattenTree(node.data!!.children, p1)
//                 }
/// bad code  
                 lifecycleScope.launch {
                     binding.treeview.apply {
                         // select node and it's children
                         selectionMode = TreeView.SelectionMode.MULTIPLE_WITH_CHILDREN
                         selectNode(binding.treeview.tree.rootNode, true)
                         expandAll()
                         selectionMode = TreeView.SelectionMode.MULTIPLE_WITH_CHILDREN
                     }
                 }
             }
         })

         binding
             .ivArrow
             .animate()
             .rotation(if (node.expand) 180f else 0f)
             .setDuration(200)
             .start()
     }

     fun flattenTree(treeNodes: List<TreeNode2>, isSwitchOn: Boolean): List<TreeNode2> {
         val result = mutableListOf<TreeNode2>()
         for (node in treeNodes) {
             node.isSwitchOn = isSwitchOn
             result.add(node)
             if (node.children.isNotEmpty()) {
                 val flattenedChildren = flattenTree(node.children, isSwitchOn)
                 result.addAll(flattenedChildren)
             }
         }
         return result
     }

//     fun flattenTree(): List<TreeNode2> {
//         val result = mutableListOf<TreeNode2>()
//         for (node in treeNodes) {
//             node.isSwitchOn = isSwitchOn
//             result.add(node)
//             if (node.children.isNotEmpty()) {
//                 val flattenedChildren = flattenTree(node.children, isSwitchOn)
//                 result.addAll(flattenedChildren)
//             }
//         }
//         return result
//     }


    override fun onClick(node: TreeNode<TreeNode2>, holder: TreeView.ViewHolder) {
        if (node.data!!.children.isNotEmpty()) {
            applyDir(holder, node)
        } else {
            //Toast.makeText(this@MainActivity, "Clicked ${node.name}", Toast.LENGTH_LONG).show()
        }
    }

    override fun onToggle(
        node: TreeNode<TreeNode2>,
        isExpand: Boolean,
        holder: TreeView.ViewHolder
    ) {
        applyDir(holder, node)
    }
}

 class ItemTreeNodeGenerator(
    private val rootItem: TreeNode2
) : TreeNodeGenerator<TreeNode2> {
     suspend fun fetchNodeChildData(targetNode: TreeNode<TreeNode2>): Set<TreeNode2> {
         return targetNode.requireData().children.toSet()
     }

    override fun createNode(
        parentNode: TreeNode<TreeNode2>,
        currentData: TreeNode2,
        tree: AbstractTree<TreeNode2>
    ): TreeNode<TreeNode2> {
        return TreeNode(
            data = currentData,
            depth = parentNode.depth + 1,
            name = currentData.name,
            id = tree.generateId(),
            hasChild = currentData.children.isNotEmpty(),
            // It should be taken from the Item
            isChild = currentData.children.isNotEmpty(),
            expand = false
        )
    }

    override fun createRootNode(): TreeNode<TreeNode2> {
        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
        )
    }

     override suspend fun fetchChildData(targetNode: TreeNode<TreeNode2>): Set<TreeNode2> {
         return targetNode.requireData().children.toSet()
     }


 }

@dingyi222666
Copy link
Owner

Upload all your example code to github as a repository, I'll read and modify it later.

@simphonydeveloper
Copy link
Author

@dingyi222666
Copy link
Owner

Okay. You can check out my code example:
https://github.com/dingyi222666/AndroidTreeViewSample

@simphonydeveloper
Copy link
Author

Thank you!
Great code, I have implemented the simultaneous selection and closure of Switch according to your code.
When all child nodes are selected, the parent node is also selected. This operation needs to be completed in the class DataSource, right?

@dingyi222666
Copy link
Owner

You need to change on both the DataSource and the Tree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants