How to create tree layout using constraint layout

2019-08-17 04:58发布

问题:

I want to create a tree layout dynamically using constraint layout.

Something like this:

With tree child nodes retrieved at runtime.

Specifications:
1. All nodes can have a maximum of three child nodes.
2. The whole layout must be created programmatically.
3. Null is passed in data if a node does not have a child.

What I tried:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    createTree(treeNode)
}

private fun createTree(treeNode: TreeNode) {

    val rootView: ConstraintLayout = dataBinding.root as ConstraintLayout
    val treeView = createLayout(treeNode, 0)

    if (treeView != null) {
        val mainViewConstraintSet = ConstraintSet()
        mainViewConstraintSet.clone(treeView)
        mainViewConstraintSet.connect(
                rootView.id,
                ConstraintSet.START,
                treeView.id,
                ConstraintSet.START
        )
        mainViewConstraintSet.connect(
                rootView.id,
                ConstraintSet.END,
                treeView.id,
                ConstraintSet.END
        )
        mainViewConstraintSet.applyTo(treeView)
        rootView.addView(treeView)
        Log.e("Log", "Added Root View")
    }
}

private fun createLayout(treeNode: TreeNode?, depth: Int): ConstraintLayout? {

    if (depth > 1) {
        return null
    }

    if (treeNode == null) {
        return null
    }

    val layoutView = ConstraintLayout(context)

    val layoutViewLayoutParams = ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.WRAP_CONTENT,
            ConstraintLayout.LayoutParams.WRAP_CONTENT
    )
    layoutViewLayoutParams.topMargin = 16
    layoutViewLayoutParams.leftMargin = 16
    layoutViewLayoutParams.rightMargin = 16
    layoutViewLayoutParams.bottomMargin = 16
    layoutView.layoutParams = layoutViewLayoutParams
    layoutView.setBackgroundColor(Color.CYAN)
    layoutView.id = ViewCompat.generateViewId()

    val layoutNode = createNode(treeNode.user)
    Log.e("Log", "Created node for " + treeNode.user.name)

    val leftChildLayout = createLayout(treeNode.left, depth + 1)
    val middleChildLayout = createLayout(treeNode.middle, depth + 1)
    val rightChildLayout = createLayout(treeNode.right, depth + 1)

    val mainViewConstraintSet = ConstraintSet()

    mainViewConstraintSet.connect(
            layoutNode.id,
            ConstraintSet.TOP,
            layoutView.id,
            ConstraintSet.TOP,
            16
    )
    mainViewConstraintSet.connect(
            layoutNode.id,
            ConstraintSet.BOTTOM,
            layoutView.id,
            ConstraintSet.BOTTOM,
            16
    )
    mainViewConstraintSet.connect(
            layoutNode.id,
            ConstraintSet.START,
            layoutView.id,
            ConstraintSet.START,
            16
    )
    mainViewConstraintSet.connect(
            layoutNode.id,
            ConstraintSet.END,
            layoutView.id,
            ConstraintSet.END,
            16
    )

    if (leftChildLayout != null) {
        mainViewConstraintSet.connect(
            layoutNode.id,
            ConstraintSet.BOTTOM,
            leftChildLayout.id,
            ConstraintSet.TOP,
            16
        )
    }

    if (middleChildLayout != null) {
        mainViewConstraintSet.connect(
            layoutNode.id,
            ConstraintSet.BOTTOM,
            middleChildLayout.id,
            ConstraintSet.TOP,
            16
        )
    }

    if (rightChildLayout != null) {
        mainViewConstraintSet.connect(
            layoutNode.id,
            ConstraintSet.BOTTOM,
            rightChildLayout.id,
            ConstraintSet.TOP,
            16
        )
    }

    if (leftChildLayout != null && middleChildLayout != null) {
        mainViewConstraintSet.connect(
            leftChildLayout.id,
            ConstraintSet.END,
            middleChildLayout.id,
            ConstraintSet.START,
            16
        )
        mainViewConstraintSet.connect(
            middleChildLayout.id,
            ConstraintSet.END,
            leftChildLayout.id,
            ConstraintSet.START,
            16
        )
    }

    if (rightChildLayout != null && middleChildLayout != null) {
        mainViewConstraintSet.connect(
            middleChildLayout.id,
            ConstraintSet.END,
            rightChildLayout.id,
            ConstraintSet.START,
            16
        )
        mainViewConstraintSet.connect(
            rightChildLayout.id,
            ConstraintSet.END,
            middleChildLayout.id,
            ConstraintSet.START,
            16
        )
    }

    mainViewConstraintSet.applyTo(layoutView)
    // layoutView.setConstraintSet(mainViewConstraintSet)



    if (leftChildLayout != null) {
        layoutView.addView(leftChildLayout)
    }
    if (middleChildLayout != null) {
        layoutView.addView(middleChildLayout)
    }
    if (rightChildLayout != null) {
        layoutView.addView(rightChildLayout)
    }

    layoutView.addView(layoutNode)

    return layoutView
}

private fun createNode(user: User): ConstraintLayout {

    val node = ConstraintLayout(context)
    node.id = ViewCompat.generateViewId()

    val nodeTextView = TextView(context)
    nodeTextView.id = ViewCompat.generateViewId()

    nodeTextView.text = user.name
    nodeTextView.textSize = 18f
    nodeTextView.setTextColor(Color.BLACK)
    nodeTextView.setTypeface(null, Typeface.BOLD)
    val layoutParams = ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.WRAP_CONTENT, ConstraintLayout.LayoutParams.WRAP_CONTENT
    )
    nodeTextView.layoutParams = layoutParams
    nodeTextView.setBackgroundColor(Color.GREEN)

    val constraintSet = ConstraintSet()
    constraintSet.clone(node)
    constraintSet.connect(nodeTextView.id, ConstraintSet.TOP, node.id, ConstraintSet.TOP, 16)
    constraintSet.connect(nodeTextView.id, ConstraintSet.LEFT, node.id, ConstraintSet.LEFT, 16)
    constraintSet.applyTo(node)

    node.addView(nodeTextView)
    node.setBackgroundColor(Color.RED)

    return node
}

The Tree Data Model:

data class TreeNode( val user: User, var parent: TreeNode?,  
var left: TreeNode?, var middle: TreeNode?, var right: TreeNode?) { }