diff --git a/README.md b/README.md index 2668283..eda96fa 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Gapo Tree View -Support TreeView for RecyclerView with highly customizable +Support TreeView for RecyclerView with highly customizable and search support ## Demo

diff --git a/app/src/main/java/com/gapo/treeview/MultiChoiceActivity.kt b/app/src/main/java/com/gapo/treeview/MultiChoiceActivity.kt index da19f91..0412442 100644 --- a/app/src/main/java/com/gapo/treeview/MultiChoiceActivity.kt +++ b/app/src/main/java/com/gapo/treeview/MultiChoiceActivity.kt @@ -1,13 +1,16 @@ package com.gapo.treeview +import android.annotation.SuppressLint import android.content.res.ColorStateList import android.graphics.Color -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View +import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.AppCompatCheckBox +import androidx.appcompat.widget.AppCompatEditText import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatTextView +import androidx.core.widget.addTextChangedListener import com.gg.gapo.treeviewlib.GapoTreeView import com.gg.gapo.treeviewlib.model.NodeState import com.gg.gapo.treeviewlib.model.NodeViewData @@ -16,6 +19,7 @@ class MultiChoiceActivity : AppCompatActivity(), GapoTreeView.Listener + @SuppressLint("MissingInflatedId") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_multi_choice) @@ -26,6 +30,21 @@ class MultiChoiceActivity : AppCompatActivity(), GapoTreeView.Listener(R.id.searchEditText)?.addTextChangedListener { + treeView.filter { nodeViewData -> + val searchText = it?.toString()?.lowercase() ?: "" + + if (searchText.isBlank()) + return@filter true + + // if any children match the search, show parents too + val hierarchyList = nodeViewData.getData().getHierarchyNodeChild() + hierarchyList.any { + searchText in it.name.lowercase() + } + } + } } override fun onBind( diff --git a/app/src/main/java/com/gapo/treeview/SampleModel.kt b/app/src/main/java/com/gapo/treeview/SampleModel.kt index 98f144d..d92d5cc 100644 --- a/app/src/main/java/com/gapo/treeview/SampleModel.kt +++ b/app/src/main/java/com/gapo/treeview/SampleModel.kt @@ -2,7 +2,6 @@ package com.gapo.treeview import android.os.Bundle import com.gg.gapo.treeviewlib.model.NodeData -import com.gg.gapo.treeviewlib.model.NodeViewData data class SampleModel( override val nodeViewId: String, diff --git a/app/src/main/java/com/gapo/treeview/SingleChoiceActivity.kt b/app/src/main/java/com/gapo/treeview/SingleChoiceActivity.kt index 5a4dc0f..8a0dbb0 100644 --- a/app/src/main/java/com/gapo/treeview/SingleChoiceActivity.kt +++ b/app/src/main/java/com/gapo/treeview/SingleChoiceActivity.kt @@ -1,12 +1,13 @@ package com.gapo.treeview -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.AppCompatEditText import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatRadioButton import androidx.appcompat.widget.AppCompatTextView -import androidx.recyclerview.widget.RecyclerView +import androidx.core.widget.addTextChangedListener import com.gg.gapo.treeviewlib.GapoTreeView import com.gg.gapo.treeviewlib.model.NodeViewData @@ -24,6 +25,21 @@ class SingleChoiceActivity : AppCompatActivity(), GapoTreeView.Listener(R.id.searchEditText)?.addTextChangedListener { + treeView.filter { nodeViewData -> + val searchText = it?.toString()?.lowercase() ?: "" + + if (searchText.isBlank()) + return@filter true + + // if any children match the search, show parents too + val hierarchyList = nodeViewData.getData().getHierarchyNodeChild() + hierarchyList.any { + searchText in it.name.lowercase() + } + } + } } override fun onBind( diff --git a/app/src/main/res/layout/activity_multi_choice.xml b/app/src/main/res/layout/activity_multi_choice.xml index 5384ea3..e65ab36 100644 --- a/app/src/main/res/layout/activity_multi_choice.xml +++ b/app/src/main/res/layout/activity_multi_choice.xml @@ -1,5 +1,19 @@ - \ No newline at end of file + android:layout_height="match_parent" + android:orientation="vertical"> + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_single_choice.xml b/app/src/main/res/layout/activity_single_choice.xml index 5384ea3..e65ab36 100644 --- a/app/src/main/res/layout/activity_single_choice.xml +++ b/app/src/main/res/layout/activity_single_choice.xml @@ -1,5 +1,19 @@ - \ No newline at end of file + android:layout_height="match_parent" + android:orientation="vertical"> + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5fbe90a..b95ad46 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,4 @@ GapoTreeView + Search \ No newline at end of file diff --git a/treeview/src/main/java/com/gg/gapo/treeviewlib/GapoTreeView.kt b/treeview/src/main/java/com/gg/gapo/treeviewlib/GapoTreeView.kt index 3714a39..1f11f77 100644 --- a/treeview/src/main/java/com/gg/gapo/treeviewlib/GapoTreeView.kt +++ b/treeview/src/main/java/com/gg/gapo/treeviewlib/GapoTreeView.kt @@ -29,6 +29,7 @@ class GapoTreeView private constructor( adapters: Pair>> ) { + private var filterPredicate: (NodeViewData) -> Boolean = { true } private val nodes = mutableListOf>() private val nodesShowOnUI = mutableListOf>() private var treeViewAdapter: TreeViewAdapter? = null @@ -191,11 +192,13 @@ class GapoTreeView private constructor( if (isLastNode) { nodesShowOnUI.addAll( nodes.filter { it.parentNodeIds.contains(nodeId) && it.nodeLevel == parentNode.nodeLevel + 1 } + .filter(filterPredicate) ) } else { nodesShowOnUI.addAll( min(nodesShowOnUI.size - 1, parentNodeIndex + 1), nodes.filter { it.parentNodeIds.contains(nodeId) && it.nodeLevel == parentNode.nodeLevel + 1 } + .filter(filterPredicate) ) } requestUpdateTree() @@ -297,6 +300,41 @@ class GapoTreeView private constructor( } } + @Suppress("UNCHECKED_CAST") + fun filter(predicate: (NodeViewData) -> Boolean) { + this.filterPredicate = predicate + + val filterNodes = nodes.filter(predicate) + + nodesShowOnUI.clear() + nodesShowOnUI.addAll(filterNodes) + + // expand all parent nodes + filterNodes.forEach { nodeViewData -> + val parentNodeIds = nodeViewData.parentNodeIds + parentNodeIds.forEach { parentNodeId -> + nodesShowOnUI.find { it.nodeId == parentNodeId }?.isExpanded = true + } + + val data = nodeViewData.getData() as NodeData + val hierarchyList = data.getHierarchyNodeChild() as List> + if (hierarchyList.size <= 1) { + nodeViewData.isExpanded = false + return@forEach + } + + val isContainChildren = + hierarchyList.subList(1, hierarchyList.size).any { hierarchyNodeData -> + nodesShowOnUI.any { it.nodeId == hierarchyNodeData.nodeViewId } + } + + nodeViewData.isExpanded = isContainChildren + nodeViewData.isLeaf = !isContainChildren + } + + requestUpdateTree() + } + private fun findNodeLevel(node: NodeViewData): Int { return node.parentNodeIds.size } diff --git a/treeview/src/main/java/com/gg/gapo/treeviewlib/model/NodeData.kt b/treeview/src/main/java/com/gg/gapo/treeviewlib/model/NodeData.kt index 903e02a..489c4ca 100644 --- a/treeview/src/main/java/com/gg/gapo/treeviewlib/model/NodeData.kt +++ b/treeview/src/main/java/com/gg/gapo/treeviewlib/model/NodeData.kt @@ -16,4 +16,11 @@ interface NodeData { fun areContentsTheSame(item: NodeData): Boolean fun getChangePayload(item: NodeData): Bundle + + @Suppress("UNCHECKED_CAST") + fun getHierarchyNodeChild(): List { + return mutableListOf(this as T).apply { + getNodeChild().forEach { this += it.getHierarchyNodeChild() } + } + } } \ No newline at end of file