├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── gapo
│ │ └── treeview
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── gapo
│ │ │ └── treeview
│ │ │ ├── MainActivity.kt
│ │ │ ├── MultiChoiceActivity.kt
│ │ │ ├── SampleModel.kt
│ │ │ └── SingleChoiceActivity.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_arrowhead_right.xml
│ │ ├── ic_folder.xml
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── activity_multi_choice.xml
│ │ ├── activity_single_choice.xml
│ │ ├── multi_node_view_item.xml
│ │ └── single_node_view_item.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── gapo
│ └── treeview
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── screenshots
├── multi.gif
└── single.gif
├── settings.gradle
└── treeview
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── gg
│ └── gapo
│ └── treeviewlib
│ └── ExampleInstrumentedTest.kt
├── main
├── AndroidManifest.xml
├── java
│ └── com
│ │ └── gg
│ │ └── gapo
│ │ └── treeviewlib
│ │ ├── DefaultTreeItemDecoration.kt
│ │ ├── GapoTreeView.kt
│ │ ├── TreeViewBuilder.kt
│ │ ├── adapter
│ │ └── TreeViewAdapter.kt
│ │ └── model
│ │ ├── NodeData.kt
│ │ ├── NodeState.kt
│ │ └── NodeViewData.kt
└── res
│ └── values
│ └── dimens.xml
└── test
└── java
└── com
└── gg
└── gapo
└── treeviewlib
└── ExampleUnitTest.kt
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/android,intellij
2 | # Edit at https://www.gitignore.io/?templates=android,intellij
3 |
4 | ### Android ###
5 | # Built application files
6 | *.apk
7 | *.ap_
8 | *.aab
9 |
10 | # Files for the ART/Dalvik VM
11 | *.dex
12 |
13 | # Java class files
14 | *.class
15 |
16 | # Generated files
17 | bin/
18 | gen/
19 | out/
20 |
21 | # Gradle files
22 | .gradle/
23 | build/
24 |
25 | # Local configuration file (sdk path, etc)
26 | local.properties
27 |
28 | # Proguard folder generated by Eclipse
29 | proguard/
30 |
31 | # Log Files
32 | *.log
33 |
34 | # Android Studio Navigation editor temp files
35 | .navigation/
36 |
37 | # Android Studio captures folder
38 | captures/
39 |
40 | # IntelliJ
41 | *.iml
42 | .idea
43 | .idea/workspace.xml
44 | .idea/tasks.xml
45 | .idea/gradle.xml
46 | .idea/assetWizardSettings.xml
47 | .idea/dictionaries
48 | .idea/libraries
49 | .idea/caches
50 | # Android Studio 3 in .gitignore file.
51 | .idea/caches/build_file_checksums.ser
52 | .idea/modules.xml
53 |
54 | # Keystore files
55 | # Uncomment the following lines if you do not want to check your keystore files in.
56 | #*.jks
57 | #*.keystore
58 |
59 | # External native build folder generated in Android Studio 2.2 and later
60 | .externalNativeBuild
61 |
62 | # Google Services (e.g. APIs or Firebase)
63 | # google-services.json
64 |
65 | # Freeline
66 | freeline.py
67 | freeline/
68 | freeline_project_description.json
69 |
70 | # fastlane
71 | fastlane/report.xml
72 | fastlane/Preview.html
73 | fastlane/screenshots
74 | fastlane/test_output
75 | fastlane/readme.md
76 |
77 | # Version control
78 | vcs.xml
79 |
80 | # lint
81 | lint/intermediates/
82 | lint/generated/
83 | lint/outputs/
84 | lint/tmp/
85 | # lint/reports/
86 |
87 | ### Android Patch ###
88 | gen-external-apklibs
89 | output.json
90 |
91 | ### Intellij ###
92 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
93 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
94 |
95 | # User-specific stuff
96 | .idea/**/workspace.xml
97 | .idea/**/tasks.xml
98 | .idea/**/usage.statistics.xml
99 | .idea/**/dictionaries
100 | .idea/**/shelf
101 |
102 | # Generated files
103 | .idea/**/contentModel.xml
104 |
105 | # Sensitive or high-churn files
106 | .idea/**/dataSources/
107 | .idea/**/dataSources.ids
108 | .idea/**/dataSources.local.xml
109 | .idea/**/sqlDataSources.xml
110 | .idea/**/dynamic.xml
111 | .idea/**/uiDesigner.xml
112 | .idea/**/dbnavigator.xml
113 |
114 | # Gradle
115 | .idea/**/gradle.xml
116 | .idea/**/libraries
117 |
118 | # Gradle and Maven with auto-import
119 | # When using Gradle or Maven with auto-import, you should exclude module files,
120 | # since they will be recreated, and may cause churn. Uncomment if using
121 | # auto-import.
122 | # .idea/modules.xml
123 | # .idea/*.iml
124 | # .idea/modules
125 |
126 | # CMake
127 | cmake-build-*/
128 |
129 | # Mongo Explorer plugin
130 | .idea/**/mongoSettings.xml
131 |
132 | # File-based project format
133 | *.iws
134 |
135 | # IntelliJ
136 |
137 | # mpeltonen/sbt-idea plugin
138 | .idea_modules/
139 |
140 | # JIRA plugin
141 | atlassian-ide-plugin.xml
142 |
143 | # Cursive Clojure plugin
144 | .idea/replstate.xml
145 |
146 | # Crashlytics plugin (for Android Studio and IntelliJ)
147 | com_crashlytics_export_strings.xml
148 | crashlytics.properties
149 | crashlytics-build.properties
150 | fabric.properties
151 |
152 | # Editor-based Rest Client
153 | .idea/httpRequests
154 |
155 | # Android studio 3.1+ serialized cache file
156 |
157 | # JetBrains templates
158 | **___jb_tmp___
159 |
160 | ### Intellij Patch ###
161 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
162 |
163 | # *.iml
164 | # modules.xml
165 | # .idea/misc.xml
166 | # *.ipr
167 |
168 | # Sonarlint plugin
169 | .idea/sonarlint
170 |
171 | # End of https://www.gitignore.io/api/android,intellij
172 |
173 | # Other
174 | /local.properties
175 | .DS_Store
176 | /captures
177 | /state.logs
178 | .externalNativeBuild
179 | keystore.properties
180 | sentry.properties
181 | *.jks
182 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2021, Gapo Technology JSC
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | * Neither the name of Gapo Technology JSC nor the names of its contributors
12 | may be used to endorse or promote products derived from this software
13 | without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL GAPO TECHNOLOGY JSC BE LIABLE FOR ANY
19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gapo Tree View
2 | Support TreeView for RecyclerView with highly customizable
3 |
4 | ## Demo
5 |
6 |
7 |
8 |
9 |
10 | ## Features
11 | - Expand/Collapse nodes
12 | - Select/Unselect/Get Selected nodes
13 | - Highly customizable UI with NodeState (example below)
14 | - Support margin by node's level
15 | - Support concat adapters
16 | - Show/Hide Tree: Highly effective when used with concat adapters
17 |
18 | ## Installation
19 | Gradle
20 | ```kotlin
21 | implementation 'vn.gapowork.android:tree-view:1.0.0-alpha01'
22 | ```
23 |
24 | ## Usage
25 | ### Prepare input data, the model need extends NodeData, example:
26 | ```kotlin
27 | data class Example(
28 | //required
29 | override val nodeViewId: String,
30 | val child: List,
31 | //your custom field
32 | val exampleId: String,
33 | val exampleName: String,
34 | ) : NodeData {
35 |
36 | //list child
37 | override fun getNodeChild(): List> {
38 | return child
39 | }
40 |
41 | //for diff util
42 | override fun areItemsTheSame(item: NodeData): Boolean {
43 | ...
44 | }
45 |
46 | //for diff util
47 | override fun areContentsTheSame(item: NodeData): Boolean {
48 | ...
49 | }
50 |
51 | //for diff util
52 | override fun getChangePayload(item: NodeData): Bundle {
53 | return Bundle()
54 | }
55 | }
56 | ```
57 |
58 | ### Create Tree
59 | ```kotlin
60 | val treeView = GapoTreeView.Builder.plant(Context)
61 | .withRecyclerView(binding.treeViewDepartment) //RecyclerView will be supported tree view
62 | .withLayoutRes(R.layout.department_node_view_item) //item layout
63 | .setListener(GapoTreeView.Listener) //listener of tree view
64 | .setData(List) //list NodeData
65 | .itemMargin(Int) //optional: margin by node's level. default = 24dp
66 | .addItemDecoration() //optional: item decoration of RecyclerView. If use this will disable feature itemMargin
67 | .showAllNodes(Boolean) //optional: show all nodes or just show parent node. default = false
68 | .addAdapters(config: ConcatAdapter.Config, adapters: List>) //optional: the adapters to concat
69 | .build()
70 | ```
71 |
72 | ### Basic functions
73 | ```kotlin
74 | //expand node
75 | fun expandNode(nodeId: String)
76 |
77 | //collapse node
78 | fun collapseNode(nodeId: String)
79 |
80 | //select a node by id. This function only updates data, not UI and will trigger [onNodeSelected] for further processing
81 | fun selectNode(nodeId: String, isSelected: Boolean)
82 |
83 | //select multiple nodes. This function only updates data, not UI
84 | fun setSelectedNode(nodes: List>, isSelected: Boolean)
85 |
86 | //unselect all nodes. This function only updates data, not UI
87 | fun clearNodesSelected()
88 |
89 | //get all selected nodes
90 | fun getSelectedNodes(): List
91 |
92 | //update layout
93 | fun requestUpdateTree()
94 | ```
95 |
96 | ### Highly customizable with NodeState
97 | The main purpose of NodeState is to customize the UI. Set NodeState as you need and then update at onBind() (see onBind() of GapoTreeView.Listener below for more details)
98 |
You can optionally create NodeStates like:
99 | ```kotlin
100 | object DisabledNodeState: NodeState
101 | class SpecialNodeState(val label: String): NodeState
102 |
103 | //set NodeState for nodes. This function only updates data, not UI
104 | fun setNodesState(nodeIds: List, nodeState: NodeState?)
105 |
106 | //remove state for all nodes. This function only updates data, not UI
107 | fun clearNodesState()
108 |
109 | //get nodes by state
110 | fun getNodesByState(nodeState: NodeState)
111 | ```
112 |
113 | ### GapoTreeView.Listener
114 | ```kotlin
115 | /**
116 | * @param [holder] view holder of Recyclerview Adapter
117 | * @param [position] current item position
118 | * @param [item] node item info
119 | * @param [bundle] change payload
120 | */
121 | override fun onBind(
122 | holder: View,
123 | position: Int,
124 | item: NodeViewData,
125 | bundle: Bundle?
126 | ){
127 | //find view by id
128 | val rbCheck = holder.findViewById(R.id.rb_check)
129 | val button = holder.findViewById