mainActivity
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: TreeAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
// 初始化树形数据
val roots = createInitialTreeData()
Log.d("tree" , "roots.size = ${roots.size}")
// 初始化所有可见节点
val allVisibleNodes = initializeVisibleNodes(roots)
Log.d("tree" , "allVisibleNodes.size = ${allVisibleNodes.size}")
// 传递可见节点到适配器
adapter = TreeAdapter(allVisibleNodes)
recyclerView.adapter = adapter
}
// 初始化数据
private fun createInitialTreeData(): MutableList<TreeNode> {
// 创建根节点
val rootNode1 = TreeNode(1, null, "Root 1", 0, false)
val rootNode2 = TreeNode(2, null, "Root 2", 0, false)
// 创建子节点
val child1 = TreeNode(3, 1, "Child 1-1", 1, true)
val child2 = TreeNode(4, 1, "Child 1-2", 1, true)
val child3 = TreeNode(5, 2, "Child 2-1", 1, true)
val grandChild1 = TreeNode(6, 3, "GrandChild 1-1", 2, true)
val greatGrandChild1 = TreeNode(7, 6, "GreatGrandChild 1-1", 3, true)
val greatGreatGrandChild1 = TreeNode(8, 7, "GreatGreatGrandChild 1-1", 4, true)
val deepChild = TreeNode(9, 8, "DeepChild 1-1", 5, true) // 超过5级的节点
val deepChild1 = TreeNode(10, 9, "DeepChild 1-1-1", 6, true) // 超过5级的节点
// 构建树结构
rootNode1.children.add(child1)
rootNode1.children.add(child2)
rootNode2.children.add(child3)
child1.children.add(grandChild1)
grandChild1.children.add(greatGrandChild1)
greatGrandChild1.children.add(greatGreatGrandChild1)
greatGreatGrandChild1.children.add(deepChild)
deepChild.children.add(deepChild1)
// 返回根节点列表
val roots = mutableListOf(rootNode1, rootNode2)
// 设置初始可见性
for (root in roots) {
setInitialVisibility(root)
}
return roots
}
// 递归设置初始可见性和展开状态
private fun setInitialVisibility(node: TreeNode) {
if (node.level < 5) {
node.isVisible = true
node.isExpanded = true
} else {
node.isVisible = false
node.isExpanded = false
}
Log.d("TreeNode", "NodeName = ${node.name}, isVisable = ${node.isVisible}")
Log.d("TreeNode", "NodeName = ${node.name}, isExpand = ${node.isExpanded}")
// 递归设置子节点
for (child in node.children) {
setInitialVisibility(child)
}
}
private fun initializeVisibleNodes(nodes: MutableList<TreeNode>): MutableList<TreeNode> {
val allNodes = mutableListOf<TreeNode>()
for (node in nodes) {
allNodes.add(node)
Log.d("allNode", "allNode add ${node.id}")
// if (node.level >= 4) {
//// node.isCollapsed = true // 折叠 5 层及以上的节点
//// } else {
//// node.isCollapsed = false
//// if (node.isExpanded) {
//// addVisibleChildren(allNodes, node)
//// }
//// }
addVisibleChildren(allNodes, node)
}
Log.d("treeNode", "allNode size = ${allNodes.size}")
return allNodes
}
// 递归添加子节点
private fun addVisibleChildren(allNodes: MutableList<TreeNode>, node: TreeNode) {
for (child in node.children) {
if(child.level <= 4){
allNodes.add(child)
Log.d("allNode", "allNode add ${child.id}")
if (child.isExpanded) {
addVisibleChildren(allNodes, child) // 递归添加展开的子节点
}
}
}
}
}
item
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="@+id/node_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/show_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Show More"
android:background="@color/white"
android:visibility="gone" /> <!-- 初始状态设为隐藏 -->
</LinearLayout>
Adapter
class TreeAdapter(
private val nodes: MutableList<TreeNode>
) : RecyclerView.Adapter<TreeAdapter.TreeViewHolder>() {
inner class TreeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.node_name)
val showMore: TextView = itemView.findViewById(R.id.show_more)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TreeViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.tree_node_item, parent, false)
return TreeViewHolder(view)
}
override fun onBindViewHolder(holder: TreeViewHolder, position: Int) {
val node = nodes[position]
// 控制节点的可见性,GONE 时不占位
holder.itemView.visibility = if (node.isVisible) View.VISIBLE else View.GONE
holder.textView.text = node.name
// 设置节点缩进
val paddingLeft = node.level * 50
holder.textView.setPadding(paddingLeft, holder.textView.paddingTop, holder.textView.paddingRight, holder.textView.paddingBottom)
// "Show More" 按钮显示逻辑 (在第 4 层显示)
if (node.level == 4 && node.children.isNotEmpty()) {
holder.showMore.visibility = View.VISIBLE
holder.showMore.setOnClickListener {
// 展开所有 5 层及以上的子节点
expandAllChildrenAndNotify(node, position)
holder.showMore.visibility = View.GONE
}
} else {
holder.showMore.visibility = View.GONE
}
}
private fun expandAllChildrenAndNotify(node: TreeNode, position: Int) {
node.isExpanded = true
val startPosition = position + 1 // 子节点插入的位置
val children = expandAllChildrenRecursively(node)
// 更新数据源,插入所有展开的子节点
nodes.addAll(startPosition, children)
// 通知 RecyclerView 插入了这些子节点
notifyItemRangeInserted(startPosition, children.size)
}
// 递归展开所有子节点并返回子节点列表
private fun expandAllChildrenRecursively(node: TreeNode): List<TreeNode> {
val expandedNodes = mutableListOf<TreeNode>()
for (child in node.children) {
child.isVisible = true // 使子节点可见
expandedNodes.add(child)
if (child.children.isNotEmpty()) {
expandedNodes.addAll(expandAllChildrenRecursively(child)) // 递归展开所有子节点
}
}
return expandedNodes
}
override fun getItemCount(): Int {
val res = nodes.count { it.isVisible }
Log.d("Adapter", "total node Size: ${nodes.size }")
Log.d("Adapter", "visiable node Size: ${res}")
return nodes.size // 返回所有可见节点的数量
// return res
}
}
TreeNode
data class TreeNode(
val id: Int,
val parentId: Int? = null,
val name: String,
val level: Int,
val isLeaf: Boolean,
var isExpanded: Boolean = false,
var isVisible: Boolean = false,
var isCollapsed: Boolean = false, // 对于 5 层及以上的节点,默认折叠
val children: MutableList<TreeNode> = mutableListOf()
)
activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tree Structure Example"
android:textSize="20sp"
android:paddingBottom="16dp"
android:layout_gravity="center"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"/>
</LinearLayout>
添加搜索框:
val searchEditText = findViewById<EditText>(R.id.search_edit_text)
searchEditText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
filterNodes(s.toString())
}
override fun afterTextChanged(s: Editable?) {}
})
过滤条件:
private fun filterNodes(query: String) {
// 清空当前的过滤结果
filteredNodes.clear()
if (query.isEmpty()) {
// 如果搜索框为空,显示全部节点
filteredNodes.addAll(allNodes)
} else {
// 遍历所有节点,查找与搜索词匹配的节点(不分层级显示)
for (node in allNodes) {
if (node.name.contains(query, ignoreCase = true)) {
filteredNodes.add(node)
}
// 如果节点有子节点,递归遍历查找符合条件的子节点
filteredNodes.addAll(searchChildrenRecursively(node.children, query))
}
}
// 通知适配器数据已经更新
adapter.notifyDataSetChanged()
}
// 递归搜索子节点
fun searchChildrenRecursively(children: List<TreeNode>, query: String): List<TreeNode> {
val result = mutableListOf<TreeNode>()
for (child in children) {
if (child.name.contains(query, ignoreCase = true)) {
result.add(child)
}
if (child.children.isNotEmpty()) {
result.addAll(searchChildrenRecursively(child.children, query))
}
}
return result
}
想想搜索结果是重新建个recycleView还是就用现在的
现在showmore 和点击文字都能展开了,要改
holder.textView.setOnClickListener {
if (node.isExpanded) {
// 收起当前节点的子节点
collapseNode(node, position)
} else {
// 展开当前节点的子节点
expandNode(node, position)
}
}
private fun expandNode(node: TreeNode, position: Int) {
node.isExpanded = true
val startPosition = position + 1
val children = expandAllChildrenRecursively(node)
// 更新数据源并通知 RecyclerView 插入子节点
nodes.addAll(startPosition, children)
notifyItemRangeInserted(startPosition, children.size)
}
private fun collapseNode(node: TreeNode, position: Int) {
node.isExpanded = false
val children = collapseAllChildrenRecursively(node)
// 更新数据源并通知 RecyclerView 删除子节点
nodes.removeAll(children)
notifyItemRangeRemoved(position + 1, children.size)
}
private fun expandAllChildrenRecursively(node: TreeNode): List<TreeNode> {
val expandedNodes = mutableListOf<TreeNode>()
for (child in node.children) {
child.isVisible = true
expandedNodes.add(child)
if (child.isExpanded && child.children.isNotEmpty()) {
expandedNodes.addAll(expandAllChildrenRecursively(child))
}
}
return expandedNodes
}
private fun collapseAllChildrenRecursively(node: TreeNode): List<TreeNode> {
val collapsedNodes = mutableListOf<TreeNode>()
for (child in node.children) {
child.isVisible = false
collapsedNodes.add(child)
if (child.children.isNotEmpty()) {
collapsedNodes.addAll(collapseAllChildrenRecursively(child))
}
}
return collapsedNodes
}
搜索更新:
searchEditText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
filterNodes(s.toString())
Log.d("filter", "query ${s.toString()}")
}
override fun afterTextChanged(s: Editable?) { }
})
searchEditText.setOnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE || event != null && event.keyCode == KeyEvent.KEYCODE_ENTER) {
filterNodes(searchEditText.text.toString())
true
} else {
false
}
}
private fun filterNodes(query: String) {
// 清空当前的过滤结果
allVisibleNodes.clear()
Log.d("filter", "filterNode.size before = ${allVisibleNodes.size}")
if (query.isEmpty()) {
// 如果搜索框为空,显示全部节点
allVisibleNodes.addAll(roots)
} else {
// 遍历所有节点,查找与搜索词匹配的节点(不分层级显示)
for (node in roots) {
if (node.name.contains(query, ignoreCase = true)) {
allVisibleNodes.add(node.copy(level = -1))
}
// 如果节点有子节点,递归遍历查找符合条件的子节点
allVisibleNodes.addAll(searchChildrenRecursively(node.children, query))
}
}
Log.d("filter", "filterNode.size after = ${allVisibleNodes.size}")
// 通知适配器数据已经更新
adapter.notifyDataSetChanged()
}
// 递归搜索子节点
fun searchChildrenRecursively(children: List<TreeNode>, query: String): List<TreeNode> {
val result = mutableListOf<TreeNode>()
for (child in children) {
if (child.name.contains(query, ignoreCase = true)) {
result.add(child.copy(level = -1))
}
if (child.children.isNotEmpty()) {
result.addAll(searchChildrenRecursively(child.children, query))
}
}
return result
}
结点缩进
if(node.level >= 0){
val paddingLeft = node.level * 50
holder.textView.setPadding(paddingLeft, holder.textView.paddingTop, holder.textView.paddingRight, holder.textView.paddingBottom)
holder.textView.setOnClickListener {
if (node.isExpanded) {
// 收起当前节点的子节点
collapseNode(node, position)
} else {
// 展开当前节点的子节点
expandNode(node, position)
}
}
} else {
holder.textView.setOnClickListener(null)
holder.textView.setPadding(0, holder.textView.paddingTop, holder.textView.paddingRight, holder.textView.paddingBottom)
}
构建树
data class Node(
val id: Int,
val parentId: Int?,
val name: String,
val level: Int,
val children: MutableList<Node> = mutableListOf()
)
fun buildTree(nodes: List<Node>): List<Node> {
val nodeMap = mutableMapOf<Int, Node>()
val rootNodes = mutableListOf<Node>()
nodes.forEach { node ->
nodeMap[node.id] = node
if (node.parentId == null) {
rootNodes.add(node)
} else {
val parentNode = nodeMap.getOrPut(node.parentId) {
Node(node.parentId, null, "Placeholder for ${node.parentId}", node.level - 1)
}
parentNode.children.add(node)
}
}
// 遍历所有节点,清理占位符节点(如果有)
rootNodes.removeIf { it.name.startsWith("Placeholder for") }
rootNodes.forEach { node ->
removePlaceholders(node)
}
return rootNodes
}
fun removePlaceholders(node: Node) {
node.children.removeIf { it.name.startsWith("Placeholder for") }
node.children.forEach { removePlaceholders(it) }
}
fun main() {
val nodeList = listOf(
Node(id = 1, parentId = null, name = "Root", level = 0),
Node(id = 2, parentId = 1, name = "Child 1", level = 1),
Node(id = 3, parentId = 1, name = "Child 2", level = 1),
Node(id = 4, parentId = 2, name = "Child 1.1", level = 2),
Node(id = 5, parentId = 6, name = "Orphan Child", level = 2), // This node's parent will be created as placeholder
Node(id = 6, parentId = 1, name = "Child 3", level = 1)
)
val tree = buildTree(nodeList)
println(tree)
}
使用 nodeMap 来存储所有节点,包括占位符节点。
在遍历节点时,如果 parentId 不为空且父节点还不存在,则创建一个占位符节点并将其存储在 nodeMap 中。
最后,清理根节点中的占位符节点,确保只有实际节点存在。
为了确保树中没有占位符节点,我们递归地清理每个节点的 children 列表。

7225

被折叠的 条评论
为什么被折叠?



