├── .gitignore
├── .idea
├── .gitignore
├── .name
├── codeStyles
│ └── Project.xml
├── compiler.xml
├── jarRepositories.xml
├── material_theme_project_new.xml
└── misc.xml
├── LICENSE
├── README.md
├── README_CN.md
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
├── add.gif
├── base1.jpg
├── base2.jpg
├── click.gif
├── dragEdit.gif
├── fit.gif
├── fs.png
├── new.jpg
└── remove.gif
├── library
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── gyso
│ │ └── treeview
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── gyso
│ │ │ └── treeview
│ │ │ ├── GysoTreeView.java
│ │ │ ├── TreeViewContainer.java
│ │ │ ├── TreeViewEditor.java
│ │ │ ├── adapter
│ │ │ ├── DrawInfo.java
│ │ │ ├── TreeViewAdapter.java
│ │ │ └── TreeViewHolder.java
│ │ │ ├── algorithm
│ │ │ ├── force
│ │ │ │ ├── FLink.java
│ │ │ │ ├── FNode.java
│ │ │ │ ├── Force.java
│ │ │ │ ├── ForceListener.java
│ │ │ │ ├── ForceView.java
│ │ │ │ └── QuadTree.java
│ │ │ ├── ring
│ │ │ │ ├── Ring.java
│ │ │ │ ├── RingForCompact.java
│ │ │ │ └── RingForSimple.java
│ │ │ ├── table
│ │ │ │ ├── Table.java
│ │ │ │ └── TableKey.java
│ │ │ └── tight_table
│ │ │ │ └── TightTable.java
│ │ │ ├── cache_pool
│ │ │ ├── HolderPool.java
│ │ │ └── PointPool.java
│ │ │ ├── layout
│ │ │ ├── BoxDownTreeLayoutManager.java
│ │ │ ├── BoxHorizonLeftAndRightLayoutManager.java
│ │ │ ├── BoxLeftTreeLayoutManager.java
│ │ │ ├── BoxRightTreeLayoutManager.java
│ │ │ ├── BoxUpTreeLayoutManager.java
│ │ │ ├── BoxVerticalUpAndDownLayoutManager.java
│ │ │ ├── CompactDownTreeLayoutManager.java
│ │ │ ├── CompactHorizonLeftAndRightLayoutManager.java
│ │ │ ├── CompactLeftTreeLayoutManager.java
│ │ │ ├── CompactRightTreeLayoutManager.java
│ │ │ ├── CompactRingTreeLayoutManager.java
│ │ │ ├── CompactUpTreeLayoutManager.java
│ │ │ ├── CompactVerticalUpAndDownLayoutManager.java
│ │ │ ├── ForceDirectedTreeLayoutManager.java
│ │ │ ├── SimpleRingTreeLayoutManager.java
│ │ │ ├── TableDownTreeLayoutManager.java
│ │ │ ├── TableHorizonLeftAndRightLayoutManager.java
│ │ │ ├── TableLeftTreeLayoutManager.java
│ │ │ ├── TableRightTreeLayoutManager.java
│ │ │ ├── TableUpTreeLayoutManagerTable.java
│ │ │ ├── TableVerticalUpAndTableDownLayoutManager.java
│ │ │ └── TreeLayoutManager.java
│ │ │ ├── line
│ │ │ ├── AngledLine.java
│ │ │ ├── BaseLine.java
│ │ │ ├── DashLine.java
│ │ │ ├── SmoothLine.java
│ │ │ └── StraightLine.java
│ │ │ ├── listener
│ │ │ ├── TreeViewControlListener.java
│ │ │ ├── TreeViewItemClick.java
│ │ │ ├── TreeViewItemLongClick.java
│ │ │ └── TreeViewNotifier.java
│ │ │ ├── model
│ │ │ ├── ITraversal.java
│ │ │ ├── NodeModel.java
│ │ │ └── TreeModel.java
│ │ │ ├── touch
│ │ │ ├── DragBlock.java
│ │ │ └── TouchEventHandler.java
│ │ │ └── util
│ │ │ ├── DensityUtils.java
│ │ │ ├── Interpolators.java
│ │ │ ├── TreeViewLog.java
│ │ │ └── ViewBox.java
│ └── res
│ │ └── values
│ │ └── values.xml
│ └── test
│ └── java
│ └── com
│ └── gyso
│ └── treeview
│ └── ExampleUnitTest.java
├── local.properties
├── sample
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── gyso
│ │ └── gysotreeviewapplication
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── gyso
│ │ │ └── gysotreeviewapplication
│ │ │ ├── MainActivity.java
│ │ │ └── base
│ │ │ ├── Animal.java
│ │ │ └── AnimalTreeViewAdapter.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── card_background.xml
│ │ ├── drag_mode_bg.xml
│ │ ├── ic_01.xml
│ │ ├── ic_02.xml
│ │ ├── ic_03.xml
│ │ ├── ic_04.xml
│ │ ├── ic_05.xml
│ │ ├── ic_06.xml
│ │ ├── ic_07.xml
│ │ ├── ic_08.xml
│ │ ├── ic_09.xml
│ │ ├── ic_10.xml
│ │ ├── ic_11.xml
│ │ ├── ic_12.xml
│ │ ├── ic_13.xml
│ │ ├── ic_14.xml
│ │ ├── ic_15.xml
│ │ ├── ic_16.xml
│ │ ├── ic_17.xml
│ │ ├── ic_18.xml
│ │ ├── ic_19.xml
│ │ ├── ic_20.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_launcher_tree.xml
│ │ └── percent_bg.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── node_base_layout.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── gyso
│ └── gysotreeviewapplication
│ └── ExampleUnitTest.java
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | # Uncomment the following line in case you need and you don't have the release build type files in your app
18 | # release/
19 | ./idea/
20 | # Gradle files
21 | .gradle/
22 | build/
23 |
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
30 | # Log Files
31 | *.log
32 |
33 | # Android Studio Navigation editor temp files
34 | .navigation/
35 |
36 | # Android Studio captures folder
37 | captures/
38 |
39 | # IntelliJ
40 | *.iml
41 | .idea/workspace.xml
42 | .idea/tasks.xml
43 | .idea/gradle.xml
44 | .idea/assetWizardSettings.xml
45 | .idea/dictionaries
46 | .idea/libraries
47 | # Android Studio 3 in .gitignore file.
48 | .idea/caches
49 | .idea/modules.xml
50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
51 | .idea/navEditor.xml
52 |
53 | # Keystore files
54 | # Uncomment the following lines if you do not want to check your keystore files in.
55 | #*.jks
56 | #*.keystore
57 |
58 | # External native build folder generated in Android Studio 2.2 and later
59 | .externalNativeBuild
60 | .cxx/
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 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | GysoTreeViewApplication
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | xmlns:android
32 |
33 | ^$
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | xmlns:.*
43 |
44 | ^$
45 |
46 |
47 | BY_NAME
48 |
49 |
50 |
51 |
52 |
53 |
54 | .*:id
55 |
56 | http://schemas.android.com/apk/res/android
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | .*:name
66 |
67 | http://schemas.android.com/apk/res/android
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | name
77 |
78 | ^$
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | style
88 |
89 | ^$
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | .*
99 |
100 | ^$
101 |
102 |
103 | BY_NAME
104 |
105 |
106 |
107 |
108 |
109 |
110 | .*
111 |
112 | http://schemas.android.com/apk/res/android
113 |
114 |
115 | ANDROID_ATTRIBUTE_ORDER
116 |
117 |
118 |
119 |
120 |
121 |
122 | .*
123 |
124 | .*
125 |
126 |
127 | BY_NAME
128 |
129 |
130 |
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.idea/material_theme_project_new.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | <<<<<<< Updated upstream
4 |
5 | =======
6 |
7 | >>>>>>> Stashed changes
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 怪兽N
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GysoTreeView
2 |
3 | 【[中文](./README_CN.md)】【[English](./README.md)】
4 | **⭐If ok, give me a star⭐**
5 |
6 | ⭐⭐⭐⭐⭐Tree View; Mind map; Think map; tree map; 树状图;思维导图;组织机构图;层次图;树型图
7 |
8 | A custom tree view for Android, designed for easy drawing some tree nodes (e.g. thind mind and tree nodes). Includes smoothly zoom, move, limit and center fix animation support, and allows easy extension so you can add your own child node's customs view and touch event detection.
9 | ```groovy
10 | dependencies {
11 | //请直接使用lib中的代码,1.0.0这个引用库很久没有更新了
12 | implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'
13 | implementation 'io.github.guaishoun:gyso-treeview:1.0.1'
14 | }
15 | ```
16 |
17 | ### Funtions
18 |
19 | - 🍇Smoothly zoom, move
20 | - 🍈Fix your window view port
21 | - 🍉Custom your subview for special node
22 | - 🍊Custom lines between nodes
23 | - 🍋Dynamic remove nodes
24 | - 🍌Dynamic add nodes
25 | - 🥭Drag to rebuild the nodes' relationship
26 |
27 | [Releases & downloads](https://github.com/guaishouN/android-tree-view.git)
28 |
29 | > **Base--Line, LayoutManger, Custom node view**
30 |
31 |
32 |
33 |
34 |
35 | > **Add**
36 |
37 |
38 |
39 | > **Remove**
40 |
41 |
42 |
43 | > **Drag Edit Mode**
44 |
45 |
46 |
47 | > **Click**
48 |
49 |
50 |
51 | > **Zoom and Fit Window**
52 |
53 |
54 |
55 | #### Steps for use
56 |
57 |
58 | ```xml
59 |
64 |
65 | ```
66 |
67 | Before presentation, Animal class means you own bean class, like this:
68 |
69 | ```java
70 | public class Animal {
71 | public int headId;
72 | public String name;
73 | }
74 | ```
75 |
76 |
77 | To use a tree view, you should do **5 steps** as follows:
78 |
79 |
80 |
81 | 1. Customs adapter by extends TreeViewAdapter.
82 |
83 | ```java
84 | public class AnimalTreeViewAdapter extends TreeViewAdapter {
85 | private DashLine dashLine = new DashLine(Color.parseColor("#F06292"),6);
86 | @Override
87 | public TreeViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, NodeModel node) {
88 | //TODO in inflate item view
89 | NodeBaseLayoutBinding nodeBinding = NodeBaseLayoutBinding.inflate(LayoutInflater.from(viewGroup.getContext()),viewGroup,false);
90 | return new TreeViewHolder<>(nodeBinding.getRoot(),node);
91 | }
92 |
93 | @Override
94 | public void onBindViewHolder(@NonNull TreeViewHolder holder) {
95 | //TODO get view and node from holder, and then control your item view
96 | View itemView = holder.getView();
97 | NodeModel node = holder.getNode();
98 | ...
99 | }
100 |
101 | @Override
102 | public Baseline onDrawLine(DrawInfo drawInfo) {
103 | // TODO If you return an BaseLine, line will be draw by the return one instead of TreeViewLayoutManager's
104 | // if(...){
105 | // ...
106 | // return dashLine;
107 | // }
108 | return null;
109 | }
110 | }
111 | ```
112 |
113 | 2. configure layout manager. Space unit is dp. You can custom you line by extends {@link com.gyso.treeview.line.BaseLine}
114 |
115 | ```java
116 | int space_50dp = 50;
117 | int space_20dp = 20;
118 | //choose a demo line or a customs line. StraightLine, PointedLine, DashLine, SmoothLine are available.
119 | Baseline line = new DashLine(Color.parseColor("#4DB6AC"),8);
120 | //choose layoout manager. VerticalTreeLayoutManager,RightTreeLayoutManager are available.
121 | TreeLayoutManager treeLayoutManager = new BoxRightTreeLayoutManager(this,space_50dp,space_20dp,line);
122 |
123 | // TODO Other stable layout managers like below:
124 | // new BoxDownTreeLayoutManager(this,space_50dp,space_20dp,line);
125 | // new BoxLeftTreeLayoutManager(this,space_50dp,space_20dp,line);
126 | // new BoxUpTreeLayoutManager(this,space_50dp,space_20dp,line);
127 | // new BoxHorizonLeftAndRightLayoutManager(this,space_50dp,space_20dp,line);
128 | // new BoxVerticalUpAndDownLayoutManager(this,space_50dp,space_20dp,line);
129 | ```
130 |
131 | 3. setting adapter and layout manager for your tree view.
132 |
133 | ```java
134 | ...
135 | treeView = findViewById(R.id.tree_view);
136 | TreeViewAdapter adapter = new AnimlTreeViewAdapter();
137 | treeView.setAdapter(adapter);
138 | treeView.setTreeLayoutManager(treeLayoutManager);
139 | ...
140 | ```
141 |
142 | 4. nodes data setting
143 | ```java
144 | //Create a TreeModel by using a root node.
145 | NodeModel node0 = new NodeModel<>(new Animal(R.drawable.ic_01,"root"));
146 | TreeModel treeModel = new TreeModel<>(node0);
147 |
148 | //Other nodes.
149 | NodeModel node1 = new NodeModel<>(new Animal(R.drawable.ic_02,"sub0"));
150 | NodeModel node2 = new NodeModel<>(new Animal(R.drawable.ic_03,"sub1"));
151 | NodeModel node3 = new NodeModel<>(new Animal(R.drawable.ic_04,"sub2"));
152 | NodeModel node4 = new NodeModel<>(new Animal(R.drawable.ic_05,"sub3"));
153 | NodeModel node5 = new NodeModel<>(new Animal(R.drawable.ic_06,"sub4"));
154 |
155 |
156 | //Build the relationship between parent node and childs,like:
157 | //treeModel.add(parent, child1, child2, ...., childN);
158 | treeModel.add(node0, node1, node2);
159 | treeModel.add(node1, node3, node4);
160 | treeModel.add(node2, node5);
161 |
162 | //finally set this treeModel to the adapter
163 | adapter.setTreeModel(treeModel);
164 | ```
165 |
166 | 5. If your want to edit your tree view, please use an editor.
167 |
168 | ```java
169 | final TreeViewEditor editor = binding.baseTreeView.getEditor();
170 |
171 | //add nodes
172 | NodeModel a = new NodeModel<>(new Animal(R.drawable.ic_13,"add-" + atomicInteger.getAndIncrement()));
173 | NodeModel b = new NodeModel<>(new Animal(R.drawable.ic_10,"add-" + atomicInteger.getAndIncrement()));
174 | NodeModel c = new NodeModel<>(new Animal(R.drawable.ic_11,"add-" + atomicInteger.getAndIncrement()));
175 | editor.addChildNodes(targetNode,a,b,c);
176 |
177 | //remove node
178 | editor.removeNode(toRemoveNode);
179 |
180 | //view center in window viewport
181 | editor.focusMidLocation()
182 |
183 | //drag to move and build new relationship
184 | editor.requestMoveNodeByDragging(isChecked);
185 | ```
186 |
187 |
188 |
189 |
190 | #### Notes & limitations
191 |
192 | Firstly, only vertical-down derection layout and right direction layout is available, more layout style should be code.
193 |
194 | Secondly, customing lines by extends BaseLine may be a little complicate for Android beginner, and performance issues will happen if you using carefully.
195 |
196 | Finally, this custom view will be continuely improved, if you has some innovative ideas, please tell me. Thanks for you patience.
197 |
198 | I will tell you how it works on [my CSDN blogs](https://blog.csdn.net/guaisou/article/details/116611140).Thx.
199 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | # GysoTreeView
2 |
3 | 【[中文](./README_CN.md)】【[English](./README.md)】
4 |
5 | Tree View; Mind map; Think map; tree map; 树状图;思维导图;
6 |
7 | 目前没发现比较好的Android树状图开源控件,于是决定自己写一个开源控件,对比了一下市面上关于思维导图或者树状图显示(如xMind,mind master等)的app,本文开源框架并不逊色。
8 |
9 | ### 特点
10 |
11 | - 丝滑的跟随手指放缩,拖动,及惯性滑动
12 |
13 | - 自动动画回归屏幕中心
14 |
15 | - 支持子节点复杂布局自定义,并且节点布局点击事件与滑动不冲突
16 |
17 | - 节点间的连接线自定义
18 |
19 | - 可删除动态节点
20 |
21 | - 可动态添加节点
22 |
23 | - 支持拖动调整节点关系
24 |
25 | - 增删、移动结构添加动画效果
26 |
27 |
28 |
29 | [github控件连接](https://github.com/guaishouN/android-tree-view.git)
30 |
31 | > **基础--连接线, 布局, 自定义节点View**
32 |
33 |
34 |
35 |
36 |
37 | > **添加**
38 |
39 |
40 |
41 | > **删除**
42 |
43 |
44 |
45 | > **拖动节点编辑书树状图结构**
46 |
47 |
48 |
49 | > **放缩拖动不影响点击**
50 |
51 |
52 |
53 | > **放缩及适应窗口**
54 |
55 |
56 |
57 | #### 使用步骤:
58 |
59 | 添加依赖
60 | ```groovy
61 | dependencies {
62 | //请直接使用lib中的代码,1.0.0这个引用库很久没有更新了
63 | implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'
64 | implementation 'io.github.guaishoun:gyso-treeview:1.0.1'
65 | }
66 | ```
67 |
68 | ```xml
69 |
74 |
75 | ```
76 | 下面说明中Animal类是仅仅用于举例的bean
77 |
78 | ```java
79 | public class Animal {
80 | public int headId;
81 | public String name;
82 | }
83 | ```
84 |
85 | 按照以下四个步骤使用该开源控件
86 |
87 | 1 通过继承 TreeViewAdapter实现节点数据与节点视图的绑定
88 |
89 | ```java
90 | public class AnimalTreeViewAdapter extends TreeViewAdapter {
91 | private DashLine dashLine = new DashLine(Color.parseColor("#F06292"),6);
92 | @Override
93 | public TreeViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, NodeModel node) {
94 | //TODO in inflate item view
95 | NodeBaseLayoutBinding nodeBinding = NodeBaseLayoutBinding.inflate(LayoutInflater.from(viewGroup.getContext()),viewGroup,false);
96 | return new TreeViewHolder<>(nodeBinding.getRoot(),node);
97 | }
98 |
99 | @Override
100 | public void onBindViewHolder(@NonNull TreeViewHolder holder) {
101 | //TODO get view and node from holder, and then control your item view
102 | View itemView = holder.getView();
103 | NodeModel node = holder.getNode();
104 | ...
105 | }
106 |
107 | @Override
108 | public Baseline onDrawLine(DrawInfo drawInfo) {
109 | // TODO If you return an BaseLine, line will be draw by the return one instead of TreeViewLayoutManager's
110 | // if(...){
111 | // ...
112 | // return dashLine;
113 | // }
114 | return null;
115 | }
116 | }
117 | ```
118 |
119 | 2 配置LayoutManager。主要设置布局风格(向右展开或垂直向下展开)、父节点与子节点的间隙、子节点间的间隙、节点间的连线(已经实现了直线、光滑曲线、虚线、根状线,也可通过BaseLine实现你自己的连线)
120 |
121 | ```java
122 | int space_50dp = 50;
123 | int space_20dp = 20;
124 | //choose a demo line or a customs line. StraightLine, PointedLine, DashLine, SmoothLine are available.
125 | Baseline line = new DashLine(Color.parseColor("#4DB6AC"),8);
126 | //choose layoout manager. VerticalTreeLayoutManager,RightTreeLayoutManager are available.
127 | TreeLayoutManager treeLayoutManager = new BoxRightTreeLayoutManager(this,space_50dp,space_20dp,line);
128 |
129 | // TODO Box[XXX]LayoutManager 系列的布局是稳定的,其他系列的布局还在优化中:
130 | // TODO Other stable layout managers like below:
131 | // new BoxDownTreeLayoutManager(this,space_50dp,space_20dp,line);
132 | // new BoxLeftTreeLayoutManager(this,space_50dp,space_20dp,line);
133 | // new BoxUpTreeLayoutManager(this,space_50dp,space_20dp,line);
134 | // new BoxHorizonLeftAndRightLayoutManager(this,space_50dp,space_20dp,line);
135 | // new BoxVerticalUpAndDownLayoutManager(this,space_50dp,space_20dp,line);
136 | ```
137 |
138 | 3 把Adapter和LayoutManager设置到你的树状图
139 |
140 | ```java
141 | ...
142 | treeView = findViewById(R.id.tree_view);
143 | TreeViewAdapter adapter = new AnimlTreeViewAdapter();
144 | treeView.setAdapter(adapter);
145 | treeView.setTreeLayoutManager(treeLayoutManager);
146 | ...
147 | ```
148 |
149 | 4 设置节点数据
150 |
151 | ```java
152 | //Create a TreeModel by using a root node.
153 | NodeModel node0 = new NodeModel<>(new Animal(R.drawable.ic_01,"root"));
154 | TreeModel treeModel = new TreeModel<>(root);
155 |
156 | //Other nodes.
157 | NodeModel node1 = new NodeModel<>(new Animal(R.drawable.ic_02,"sub0"));
158 | NodeModel node2 = new NodeModel<>(new Animal(R.drawable.ic_03,"sub1"));
159 | NodeModel node3 = new NodeModel<>(new Animal(R.drawable.ic_04,"sub2"));
160 | NodeModel node4 = new NodeModel<>(new Animal(R.drawable.ic_05,"sub3"));
161 | NodeModel node5 = new NodeModel<>(new Animal(R.drawable.ic_06,"sub4"));
162 |
163 |
164 | //Build the relationship between parent node and childs,like:
165 | //treeModel.add(parent, child1, child2, ...., childN);
166 | treeModel.add(node0, node1, node2);
167 | treeModel.add(node1, node3, node4);
168 | treeModel.add(node2, node5);
169 |
170 | //finally set this treeModel to the adapter
171 | adapter.setTreeModel(treeModel);
172 | ```
173 |
174 | 5 如果你想编辑这个树状图
175 |
176 | ```java
177 | final TreeViewEditor editor = binding.baseTreeView.getEditor();
178 |
179 | //add nodes
180 | NodeModel a = new NodeModel<>(new Animal(R.drawable.ic_13,"add-" + atomicInteger.getAndIncrement()));
181 | NodeModel b = new NodeModel<>(new Animal(R.drawable.ic_10,"add-" + atomicInteger.getAndIncrement()));
182 | NodeModel c = new NodeModel<>(new Animal(R.drawable.ic_11,"add-" + atomicInteger.getAndIncrement()));
183 | editor.addChildNodes(targetNode,a,b,c);
184 |
185 | //remove node
186 | editor.removeNode(toRemoveNode);
187 |
188 | //view center in window viewport
189 | editor.focusMidLocation()
190 |
191 | //drag to move and build new relationship
192 | editor.requestMoveNodeByDragging(isChecked);
193 | ```
194 |
195 |
196 |
197 | #### 写在最后
198 |
199 | 目前只写了垂直向下布局及向右展开布局,其他的还得花时间。对于线的实现只是实现了几种,也还得丰富。在自定义自己的BaseLine线时,Android初学者注意因为画线动作是在View的onDraw(canvas)中的,所以在里面不要new对象,不要任何耗时操作。
200 |
201 | 这个项目如果有人用就会持续更新下去。喜欢点个赞,谢谢。
202 |
203 | 关于实现,[我的CSDN](https://blog.csdn.net/guaisou/article/details/116611140)会逐渐更新。
204 |
205 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext.kotlin_version = '1.4.10'
4 | repositories {
5 | google()
6 | mavenCentral()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.0.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | mavenCentral()
21 | }
22 | }
23 |
24 | task clean(type: Delete) {
25 | delete rootProject.buildDir
26 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Feb 14 14:53:19 CST 2025
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/images/add.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/images/add.gif
--------------------------------------------------------------------------------
/images/base1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/images/base1.jpg
--------------------------------------------------------------------------------
/images/base2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/images/base2.jpg
--------------------------------------------------------------------------------
/images/click.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/images/click.gif
--------------------------------------------------------------------------------
/images/dragEdit.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/images/dragEdit.gif
--------------------------------------------------------------------------------
/images/fit.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/images/fit.gif
--------------------------------------------------------------------------------
/images/fs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/images/fs.png
--------------------------------------------------------------------------------
/images/new.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/images/new.jpg
--------------------------------------------------------------------------------
/images/remove.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/images/remove.gif
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | //apply plugin: 'maven-publish'
3 | //apply plugin: 'signing'
4 |
5 | version = '1.0.1'
6 | android {
7 | compileSdkVersion 29
8 | buildToolsVersion "29.0.3"
9 |
10 | defaultConfig {
11 | minSdkVersion 21
12 | targetSdkVersion 30
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | consumerProguardFiles "consumer-rules.pro"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | buildConfigField "boolean", "isDebug", "false"
25 | }
26 | debug {
27 | buildConfigField "boolean", "isDebug", "false"
28 | }
29 | }
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | }
34 | }
35 |
36 | dependencies {
37 | implementation fileTree(dir: "libs", include: ["*.jar"])
38 | implementation 'androidx.appcompat:appcompat:1.2.0'
39 | testImplementation 'junit:junit:4.12'
40 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
41 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
42 | implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'
43 | }
44 |
45 | //task androidSourcesJar(type: Jar) {
46 | // archiveClassifier.set("sources")
47 | // from android.sourceSets.main.java.source
48 | // exclude "**/R.class"
49 | // exclude "**/BuildConfig.class"
50 | //}
51 |
52 | //publishing {
53 | // publications {
54 | // release(MavenPublication) {
55 | // // group id,发布后引用的依赖的 group id
56 | // groupId 'io.github.guaishoun'
57 | // // 发布后引用的依赖的 artifact id
58 | // artifactId 'gyso-treeview'
59 | // // 发布的版本
60 | // version version
61 | // // 发布的 arr 的文件和源码文件
62 | // artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
63 | // artifact androidSourcesJar
64 | // pom {
65 | // // 构件名称,可以自定义
66 | // name = 'gyso-treeview'
67 | // // 构件描述
68 | // description = 'A custom tree view for Android, designed for easy drawing some tree nodes (e.g. think mind and tree nodes). Includes smoothly zoom, move, limit and center fix animation support, and allows easy extension so you can add your own child node\'s customs view and touch event detection.'
69 | // // 构件主页
70 | // url = 'https://github.com/guaishouN/android-thinkmap-treeview'
71 | // // 许可证名称和地址
72 | // licenses {
73 | // license {
74 | // name = 'MIT License'
75 | // url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
76 | // }
77 | // }
78 | // // 开发者信息
79 | // developers {
80 | // developer {
81 | // name = 'GuaishouN'
82 | // email = '674149099@qq.com'
83 | // }
84 | // }
85 | // // 版本控制仓库地址
86 | // scm {
87 | // url = 'https://github.com/guaishouN/android-thinkmap-treeview'
88 | // connection = 'scm:git@github.com:guaishouN/android-thinkmap-treeview.git'
89 | // developerConnection = 'scm:git@github.com:guaishouN/android-thinkmap-treeview.git'
90 | // }
91 | // }
92 | // }
93 | // }
94 | // repositories {
95 | // maven {
96 | // // 发布的位置,这里根据发布的版本区分了 SNAPSHOT 和最终版本两种情况
97 | // def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
98 | // def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
99 | // url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
100 | // credentials {
101 | // // 这里就是之前在 issues.sonatype.org 注册的账号
102 | // username NEXUS_USERNAME
103 | // password NEXUS_PASSWORD
104 | // }
105 | // }
106 | // }
107 | //}
108 | //
109 | //signing {
110 | // sign publishing.publications
111 | //}
--------------------------------------------------------------------------------
/library/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/library/consumer-rules.pro
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/library/src/androidTest/java/com/gyso/treeview/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 | assertEquals("com.gyso.treeview.test", appContext.getPackageName());
25 | }
26 | }
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/GysoTreeView.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.BlurMaskFilter;
7 | import android.graphics.Canvas;
8 | import android.graphics.Color;
9 | import android.graphics.Paint;
10 | import android.graphics.RenderNode;
11 | import android.renderscript.Allocation;
12 | import android.renderscript.Element;
13 | import android.renderscript.RenderScript;
14 | import android.renderscript.ScriptIntrinsicBlur;
15 | import android.util.AttributeSet;
16 | import android.util.Log;
17 | import android.view.MotionEvent;
18 | import android.view.View;
19 | import android.widget.FrameLayout;
20 | import androidx.annotation.NonNull;
21 | import androidx.annotation.Nullable;
22 | import androidx.core.graphics.BitmapCompat;
23 |
24 | import com.gyso.treeview.adapter.TreeViewAdapter;
25 | import com.gyso.treeview.cache_pool.PointPool;
26 | import com.gyso.treeview.layout.TreeLayoutManager;
27 | import com.gyso.treeview.listener.TreeViewControlListener;
28 | import com.gyso.treeview.touch.TouchEventHandler;
29 | import com.gyso.treeview.util.TreeViewLog;
30 |
31 |
32 | /**
33 | * @Author: 怪兽N
34 | * @Time: 2021/4/29 14:09
35 | * @Email: 674149099@qq.com
36 | * @WeChat: guaishouN
37 | * @Describe:
38 | * the main tree view.
39 | */
40 | public class GysoTreeView extends FrameLayout {
41 | public static final String TAG = GysoTreeView.class.getSimpleName();
42 | private final TreeViewContainer treeViewContainer;
43 | private final TouchEventHandler treeViewGestureHandler;
44 | private boolean disallowIntercept = false;
45 |
46 | public GysoTreeView(@NonNull Context context) {
47 | this(context, null,0);
48 | }
49 |
50 | public GysoTreeView(@NonNull Context context, @Nullable AttributeSet attrs) {
51 | this(context, attrs,0);
52 | }
53 | //private Paint paint = new Paint();
54 |
55 | public GysoTreeView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
56 | super(context, attrs, defStyleAttr);
57 | LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
58 | setClipChildren(false);
59 | setClipToPadding(false);
60 | treeViewContainer = new TreeViewContainer(getContext());
61 | treeViewContainer.setLayoutParams(layoutParams);
62 | addView(treeViewContainer);
63 | treeViewGestureHandler = new TouchEventHandler(getContext(), treeViewContainer);
64 |
65 | //Note: do not set setKeepInViewport(true), there still has bug
66 | treeViewGestureHandler.setKeepInViewport(false);
67 |
68 | //set animate default
69 | treeViewContainer.setAnimateAdd(true);
70 | treeViewContainer.setAnimateRemove(true);
71 | treeViewContainer.setAnimateMove(true);
72 | // paint.setColor(Color.RED);
73 | //paint.setStyle(Paint.Style.FILL); ;
74 | //setLayerType(LAYER_TYPE_HARDWARE,paint);
75 | }
76 |
77 | @Override
78 | public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
79 | super.requestDisallowInterceptTouchEvent(disallowIntercept);
80 | this.disallowIntercept = disallowIntercept;
81 | TreeViewLog.e(TAG, "requestDisallowInterceptTouchEvent:"+disallowIntercept);
82 | }
83 |
84 | @Override
85 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
86 | super.onLayout(changed, left, top, right, bottom);
87 | }
88 |
89 | @Override
90 | public boolean onInterceptTouchEvent(MotionEvent event) {
91 | TreeViewLog.e(TAG, "onInterceptTouchEvent: "+MotionEvent.actionToString(event.getAction()));
92 | return (!disallowIntercept && treeViewGestureHandler.detectInterceptTouchEvent(event)) || super.onInterceptTouchEvent(event);
93 | }
94 |
95 | @Override
96 | public boolean onTouchEvent(MotionEvent event) {
97 | TreeViewLog.e(TAG, "onTouchEvent: "+MotionEvent.actionToString(event.getAction()));
98 | return !disallowIntercept && treeViewGestureHandler.onTouchEvent(event);
99 | }
100 | //Bitmap bitmap = null;
101 | @Override
102 | public void draw(Canvas canvas) {
103 | super.draw(canvas);
104 | }
105 | @Override
106 | protected void onDraw(Canvas canvas) {
107 | super.onDraw(canvas);
108 | }
109 |
110 | @Override
111 | protected void dispatchDraw(Canvas canvas) {
112 | //canvas.drawRect(100,100,500,500,paint);
113 | //bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
114 | //canvas.saveLayer(0,0,getWidth(),getHeight(),paint);
115 | super.dispatchDraw(canvas);
116 | //doBlur(bitmap);
117 | //canvas.drawBitmap(bitmap,0,0,new Paint());
118 | //canvas.restore();
119 | }
120 |
121 | private void doBlur(Bitmap bitmap){
122 | Bitmap newmap;
123 | for (int i = 0; i < 10; i++) {
124 | newmap = goBlur(bitmap, 20f, getContext());
125 | bitmap = newmap;
126 | }
127 | }
128 |
129 | public static Bitmap goBlur(Bitmap bitmap,float radius,Context mContext) {
130 | Log.d(TAG, "goBlur: ");
131 | int width = bitmap.getWidth();
132 | int height = bitmap.getHeight();
133 | Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
134 |
135 | //Instantiate a new Renderscript
136 | RenderScript rs = RenderScript.create(mContext);
137 |
138 | //Create an Intrinsic Blur Script using the Renderscript
139 | ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
140 |
141 | //Create the Allocations (in/out) with the Renderscript and the in/out bitmaps
142 | Allocation allIn = Allocation.createFromBitmap(rs, bitmap);
143 | Allocation allOut = Allocation.createFromBitmap(rs, result);
144 |
145 | //Set the radius of the blur: 0 < radius <= 25
146 | blurScript.setRadius(radius);
147 |
148 | //Perform the Renderscript
149 | blurScript.setInput(allIn);
150 | blurScript.forEach(allOut);
151 |
152 | //Copy the final bitmap created by the out Allocation to the outBitmap
153 | allOut.copyTo(result);
154 |
155 | //After finishing everything, we destroy the Renderscript.
156 | rs.destroy();
157 |
158 | return result;
159 | }
160 |
161 | @Override
162 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
163 | treeViewGestureHandler.setViewport(w,h);
164 | }
165 |
166 | public void setAdapter(TreeViewAdapter adapter) {
167 | treeViewContainer.setAdapter(adapter);
168 | }
169 |
170 | public TreeViewAdapter getAdapter() {
171 | return treeViewContainer.getAdapter();
172 | }
173 |
174 | public void setTreeLayoutManager(TreeLayoutManager TreeLayoutManager) {
175 | treeViewContainer.setTreeLayoutManager(TreeLayoutManager);
176 | }
177 |
178 | public TreeViewEditor getEditor(){
179 | return new TreeViewEditor(treeViewContainer);
180 | }
181 |
182 | public void setTreeViewControlListener(TreeViewControlListener listener){
183 | treeViewGestureHandler.setControlListener(listener);
184 | treeViewContainer.setControlListener(listener);
185 | }
186 |
187 | @Override
188 | protected void onDetachedFromWindow() {
189 | super.onDetachedFromWindow();
190 | PointPool.freeAll();
191 | TreeViewLog.d(TAG, "onDetachedFromWindow: ");
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/TreeViewEditor.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview;
2 |
3 | import android.view.View;
4 |
5 | import com.gyso.treeview.adapter.TreeViewAdapter;
6 | import com.gyso.treeview.model.NodeModel;
7 |
8 | import java.io.File;
9 | import java.lang.ref.WeakReference;
10 | import java.util.List;
11 |
12 | /**
13 | * @Author: 怪兽N
14 | * @Time: 2021/6/9 15:31
15 | * @Email: 674149099@qq.com
16 | * @WeChat: guaishouN
17 | * @Describe: Android developer
18 | *
19 | * helper you edit your tree view.
20 | * Move node by dragging and remove node is support now.
21 | *
22 | * Note:
23 | * 1 An adapter must be set to GysoTreeView before you get an editor
24 | * 2 If you has set a new adapter, you should get an new editor
25 | */
26 | public class TreeViewEditor {
27 | private final WeakReference> adapterWeakReference;
28 | private final WeakReference containerWeakReference;
29 | protected TreeViewEditor(TreeViewContainer container){
30 | this.containerWeakReference = new WeakReference<>(container);
31 | this.adapterWeakReference = new WeakReference<>(container.getAdapter());
32 | }
33 |
34 | private TreeViewContainer getContainer(){
35 | return containerWeakReference.get();
36 | }
37 |
38 | private TreeViewAdapter> getAdapter(){
39 | return adapterWeakReference.get();
40 | }
41 | /**
42 | * let add node in window viewport
43 | */
44 | public void focusMidLocation(){
45 | TreeViewContainer container = getContainer();
46 | if (container!=null)container.focusMidLocation();
47 | }
48 |
49 | public View anchorNodeOnMidViewport(NodeModel> targetNode){
50 | //TODO move targetNode at center of viewport
51 | return null;
52 | }
53 |
54 | /**
55 | * change layout algorithm
56 | */
57 | public void changeLayoutAlgorithm(){
58 |
59 | }
60 |
61 | /**
62 | * get current relation ship, you can use when you change
63 | * @param traverse relations node
64 | */
65 | public void getCurrentRelationships(TraverseRelationshipCallback traverse){
66 |
67 | }
68 |
69 | /**
70 | * get current relation ship, you can use when you change
71 | * @param traverse relations node
72 | * @param jsonStringFilePath jsonStringFilePath
73 | */
74 | public boolean save(String jsonStringFilePath, TraverseRelationshipCallback traverse){
75 | return false;
76 | }
77 |
78 | public boolean save(File jsonStringFile, TraverseRelationshipCallback traverse){
79 | return false;
80 | }
81 |
82 | /**
83 | * load data from json String
84 | * @param jsonString string
85 | */
86 | public void load(String jsonString){
87 |
88 | }
89 |
90 | /**
91 | * load data from file
92 | * @param jsonStringFile file
93 | */
94 | public void load(File jsonStringFile){
95 |
96 | }
97 |
98 | /**
99 | * expand by node
100 | * @param targetParentNode targetParentNode
101 | */
102 | public void collapse(NodeModel> targetParentNode){
103 |
104 | }
105 |
106 | /**
107 | * expand by node
108 | * @param targetParentNode targetParentNode
109 | */
110 | public void expand(NodeModel> targetParentNode){
111 |
112 | }
113 |
114 | /**
115 | * focus on node
116 | * @param targetNode targetNode
117 | */
118 | public void focusOn(NodeModel> targetNode){
119 |
120 | }
121 |
122 | /**
123 | * un focus on node
124 | * @param targetNode targetNode
125 | */
126 | public void unFocusOn(NodeModel> targetNode){
127 |
128 | }
129 |
130 | /**
131 | * for support scroll view
132 | */
133 | public void lockDragDirection(){
134 |
135 | }
136 |
137 | /**
138 | * save current state
139 | */
140 | public void saveLastSate(){
141 |
142 | }
143 |
144 | /**
145 | * keep last location and
146 | */
147 | public void restoreLastSate(){
148 |
149 | }
150 |
151 | /**
152 | * add on select listener
153 | */
154 | public void addOnSelectedListener(){
155 |
156 | }
157 |
158 | /**
159 | * default: auto restructure by dragging;
160 | * totally free drag;
161 | * @param status status
162 | */
163 | public void setEditStatus(int status){
164 |
165 | }
166 |
167 | /**
168 | * before you edit, requestMoveNodeByDragging(true), so than you can drag to move the node
169 | * @param wantEdit true for edit mode
170 | */
171 | public void requestMoveNodeByDragging(boolean wantEdit){
172 | TreeViewContainer container = getContainer();
173 | if (container!=null)container.requestMoveNodeByDragging(wantEdit);
174 | }
175 |
176 | /**
177 | * add child nodes
178 | * @param parent parent node should has been in tree model
179 | * @param childNodes new nodes that will be add to tree model
180 | */
181 | public void addChildNodes(NodeModel> parent, NodeModel>... childNodes) {
182 | TreeViewContainer container = getContainer();
183 | if(container!=null){
184 | container.onAddNodes(parent,childNodes);
185 | }
186 | }
187 |
188 | /**
189 | * remove node
190 | * @param nodeToRemove node to remove
191 | */
192 | public void removeNode(NodeModel> nodeToRemove){
193 | TreeViewContainer container = getContainer();
194 | if(container!=null){
195 | container.onRemoveNode(nodeToRemove);
196 | }
197 | }
198 |
199 | /**
200 | * remove children nodes by parent node
201 | * @param parentNode parent node to remove children
202 | */
203 | public void removeNodeChildren(NodeModel> parentNode){
204 | TreeViewContainer container = getContainer();
205 | if(container!=null){
206 | container.onRemoveChildNodes(parentNode);
207 | }
208 | }
209 |
210 | public interface TraverseRelationshipCallback{
211 | void callback(T root, T parent , T child);
212 | default void callbackView(View rootView, View parentView , View childView){};
213 | }
214 |
215 | public interface OnNodeSelectedCallback{
216 | void callback(T clickNode, List selectedList);
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/adapter/DrawInfo.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.adapter;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 | import android.graphics.Path;
6 | import android.graphics.Point;
7 | import android.graphics.PointF;
8 |
9 | /**
10 | * @Author: 怪兽N
11 | * @Time: 2021/5/7 21:15
12 | * @Email: 674149099@qq.com
13 | * @WeChat: guaishouN
14 | * @Describe:
15 | * In order to draw a custom line, there are too many draw info should be passed.
16 | * So, this class is estimated to package the draw element that need to draw a colorful line between two nodes.
17 | */
18 | public class DrawInfo {
19 | /**
20 | * canvas
21 | */
22 | private Canvas canvas;
23 | /**
24 | * node from view
25 | */
26 | private TreeViewHolder> fromHolder;
27 | /**
28 | * node to view
29 | */
30 | private TreeViewHolder> toHolder;
31 | /**
32 | * paint, before use you should reset
33 | */
34 | private Paint paint;
35 | /**
36 | * path, before use you should reset
37 | */
38 | private Path path;
39 | /**
40 | * space between Peer and Peer, means child and child
41 | */
42 | private int spacePeerToPeer;
43 | /**
44 | * space between parent and child
45 | */
46 | private int spaceParentToChild;
47 | /**
48 | * viewport width
49 | */
50 | private int windowWidth;
51 | /**
52 | * viewport height
53 | */
54 | private int windowHeight;
55 |
56 | public Canvas getCanvas() {
57 | return canvas;
58 | }
59 |
60 | public void setCanvas(Canvas canvas) {
61 | this.canvas = canvas;
62 | }
63 |
64 | public TreeViewHolder> getFromHolder() {
65 | return fromHolder;
66 | }
67 |
68 | public void setFromHolder(TreeViewHolder> fromHolder) {
69 | this.fromHolder = fromHolder;
70 | }
71 |
72 | public TreeViewHolder> getToHolder() {
73 | return toHolder;
74 | }
75 |
76 | public void setToHolder(TreeViewHolder> toHolder) {
77 | this.toHolder = toHolder;
78 | }
79 |
80 | public Paint getPaint() {
81 | return paint;
82 | }
83 |
84 | public void setPaint(Paint paint) {
85 | this.paint = paint;
86 | }
87 |
88 | public Path getPath() {
89 | return path;
90 | }
91 |
92 | public void setPath(Path path) {
93 | this.path = path;
94 | }
95 |
96 | public void setSpace(int spacePeerToPeer,int spaceParentToChild) {
97 | this.spacePeerToPeer = spacePeerToPeer;
98 | this.spaceParentToChild = spaceParentToChild;
99 | }
100 |
101 | public int getSpacePeerToPeer() {
102 | return spacePeerToPeer;
103 | }
104 |
105 | public int getSpaceParentToChild() {
106 | return spaceParentToChild;
107 | }
108 |
109 | public int getWindowWidth() {
110 | return windowWidth;
111 | }
112 |
113 | public void setWindowWidth(int windowWidth) {
114 | this.windowWidth = windowWidth;
115 | }
116 |
117 | public int getWindowHeight() {
118 | return windowHeight;
119 | }
120 |
121 | public void setWindowHeight(int windowHeight) {
122 | this.windowHeight = windowHeight;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/adapter/TreeViewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.adapter;
2 |
3 | import android.view.ViewGroup;
4 |
5 | import androidx.annotation.NonNull;
6 |
7 | import com.gyso.treeview.line.BaseLine;
8 | import com.gyso.treeview.listener.TreeViewNotifier;
9 | import com.gyso.treeview.model.NodeModel;
10 | import com.gyso.treeview.model.TreeModel;
11 |
12 | /**
13 | * @Author: 怪兽N
14 | * @Time: 2021/4/23 15:19
15 | * @Email: 674149099@qq.com
16 | * @WeChat: guaishouN
17 | * @Describe:
18 | * The view adapter for the {@link com.gyso.treeview.TreeViewContainer}
19 | */
20 | public abstract class TreeViewAdapter {
21 | private TreeViewNotifier notifier;
22 | private TreeModel treeModel;
23 |
24 | public void setTreeModel(TreeModel treeModel) {
25 | this.treeModel = treeModel;
26 | notifyDataSetChange();
27 | }
28 |
29 | /**
30 | * Get tree model
31 | * @return tree model
32 | */
33 | public TreeModel getTreeModel(){
34 | return treeModel;
35 | }
36 |
37 | /**
38 | * For create view holder by your self
39 | * @param viewGroup parent
40 | * @param model node
41 | * @return holder
42 | */
43 | public abstract TreeViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, NodeModel model);
44 |
45 | /**
46 | * when bind the holder, set up you view
47 | * @param holder holder
48 | */
49 | public abstract void onBindViewHolder(@NonNull TreeViewHolder holder);
50 |
51 | /**
52 | * Draw line between node and node by you decision.
53 | * If you return an BaseLine, line will be draw by the return one instead of TreeViewLayoutManager's.
54 | * @param drawInfo provides all you need to draw you line
55 | * @return the line draw you want to use for different nodes
56 | */
57 | public abstract BaseLine onDrawLine(DrawInfo drawInfo);
58 |
59 | /**
60 | * for recycling holder, exactly for recycling views
61 | * @param node
62 | * @return
63 | */
64 | public int getHolderType(NodeModel> node){
65 | return 0;
66 | }
67 |
68 | public void setNotifier(TreeViewNotifier notifier){
69 | this.notifier = notifier;
70 | }
71 |
72 | public void notifyDataSetChange(){
73 | if(notifier!=null){
74 | notifier.onDataSetChange();
75 | }
76 | }
77 |
78 | public void notifyItemViewChange(NodeModel node){
79 | if(notifier!=null){
80 | notifier.onItemViewChange(node);
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/adapter/TreeViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.adapter;
2 |
3 | import android.view.View;
4 |
5 | import androidx.annotation.NonNull;
6 |
7 | import com.gyso.treeview.layout.TreeLayoutManager;
8 | import com.gyso.treeview.model.NodeModel;
9 |
10 | /**
11 | * @Author: 怪兽N
12 | * @Time: 2021/4/23 15:20
13 | * @Email: 674149099@qq.com
14 | * @WeChat: guaishouN
15 | * @Describe:
16 | * View holder
17 | */
18 | public class TreeViewHolder {
19 | private int holderLayoutType = TreeLayoutManager.LAYOUT_TYPE_NONE;
20 | private View view;
21 | private NodeModel node;
22 | public TreeViewHolder(View view, @NonNull NodeModel node) {
23 | this.view = view;
24 | this.node = node;
25 | }
26 |
27 | public NodeModel getNode() {
28 | return node;
29 | }
30 |
31 | public View getView() {
32 | return view;
33 | }
34 |
35 | public void setView(View view) {
36 | this.view = view;
37 | }
38 |
39 | public void setNode(NodeModel> node) {
40 | this.node = (NodeModel)node;
41 | }
42 |
43 | public int getHolderLayoutType() {
44 | return holderLayoutType;
45 | }
46 |
47 | public void setHolderLayoutType(int holderLayoutType) {
48 | this.holderLayoutType = holderLayoutType;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/algorithm/force/FLink.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.algorithm.force;
2 |
3 | /**
4 | * Created by Z.Pan on 2016/10/9.
5 | */
6 |
7 | public class FLink {
8 |
9 | FNode source;
10 | FNode target;
11 | private String text;
12 |
13 | public FLink(FNode source, FNode target) {
14 | this.source = source;
15 | this.target = target;
16 | }
17 |
18 | public String getText() {
19 | return text;
20 | }
21 |
22 | public void setText(String text) {
23 | this.text = text;
24 | }
25 |
26 | double getNodeDistance() {
27 | float dx = source.x - target.x;
28 | float dy = source.y - target.y;
29 | return Math.sqrt(dx * dx + dy * dy);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/algorithm/force/FNode.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.algorithm.force;
2 |
3 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
4 | import static java.lang.annotation.RetentionPolicy.SOURCE;
5 |
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.Target;
8 |
9 | /**
10 | * 力导向图中显示的节点。
11 | *
12 | * Created by Z.Pan on 2016/10/8.
13 | */
14 |
15 | public class FNode {
16 |
17 | /** 根节点级别 */
18 | public static final int ROOT_NODE_LEVEL = 0;
19 |
20 | static final short DRAG_START = 2;
21 | static final short DRAG = 4;
22 | static final short DRAG_END = 6;
23 |
24 | private String text; // 节点显示的内容
25 | private Object obj; // 用来携带其他数据,如:该节点对应的数据实体 Bean,或数据库中的 _id
26 | private int level; // 级别
27 |
28 | public float x, y; // 当前坐标
29 | float px, py; // 前一个状态的坐标
30 | int weight; // 根据子节点自动计算,weight 越大,该节点越不容易被拖动
31 |
32 | private float radius = 50f; // 节点半径
33 | private short state; // 节点状态,该状态决定了是否处于稳定状态
34 |
35 | public FNode(String text) {
36 | this(text, 50f, ROOT_NODE_LEVEL);
37 | }
38 |
39 | public FNode(String text, float radius, int level) {
40 | this.text = text;
41 | this.radius = radius;
42 | this.level = level;
43 | x=y=-1f;
44 | weight=1;
45 | }
46 |
47 | public void setObj(Object obj) {
48 | this.obj = obj;
49 | }
50 |
51 | public String getText() {
52 | return text;
53 | }
54 |
55 | public Object getObj() {
56 | return obj;
57 | }
58 |
59 | public int getLevel() {
60 | return level;
61 | }
62 |
63 | public void setLevel(int level) {
64 | this.level = level;
65 | }
66 |
67 | float getRadius() {
68 | return radius;
69 | }
70 |
71 | boolean isRootNode() {
72 | return level == ROOT_NODE_LEVEL;
73 | }
74 |
75 | /**
76 | * 给定一个坐标 (x, y),判断该坐标是否在节点所在范围内。用来判断是否点击了该节点。
77 | * @param x x坐标
78 | * @param y y坐标
79 | * @param scale 缩放比例
80 | * @return true 表示 (x, y) 在该节点内部
81 | */
82 | boolean isInside(float x, float y, float scale) {
83 | float left = (this.x - radius) * scale;
84 | float top = (this.y - radius) * scale;
85 | float right = (this.x + radius) * scale;
86 | float bottom = (this.y + radius) * scale;
87 | return x >= left && x <= right && y >= top && y <= bottom;
88 | }
89 |
90 | boolean isStable() {
91 | return state != 0;
92 | }
93 |
94 | /**
95 | * 设置节点的状态是否正在被手指拖动。
96 | * @param state {@linkplain #DRAG_START} 开始拖动;{@linkplain #DRAG_END} 结束拖动。
97 | */
98 | void setDragState(@State short state) {
99 | switch (state) {
100 | case DRAG_START:
101 | this.state |= state;
102 | break;
103 | case DRAG_END:
104 | this.state &= ~state;
105 | break;
106 | }
107 | }
108 |
109 | @ShortDef({DRAG_START, DRAG, DRAG_END})
110 | @Retention(SOURCE)
111 | public @interface State {}
112 |
113 | @Retention(SOURCE)
114 | @Target({ANNOTATION_TYPE})
115 | public @interface ShortDef {
116 | short[] value() default {};
117 | boolean flag() default false;
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/algorithm/force/ForceListener.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.algorithm.force;
2 |
3 | /**
4 | * Created by Z.Pan on 2016/10/10.
5 | */
6 |
7 | public interface ForceListener {
8 | void refresh();
9 | }
10 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/algorithm/force/QuadTree.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.algorithm.force;
2 |
3 | /**
4 | * Created by Z.Pan on 2016/10/10.
5 | */
6 | class QuadTree {
7 | private static final float NULL = -1;
8 |
9 | static class Node {
10 | boolean isLeaf;
11 | Node[] children;
12 | FNode point;
13 | float x = NULL;
14 | float y = NULL;
15 |
16 | float charge;
17 | float pointCharge;
18 | float cx, cy;
19 |
20 | Node(boolean isLeaf, Node[] children, FNode point, float x, float y) {
21 | this.isLeaf = isLeaf;
22 | this.children = children;
23 | this.point = point;
24 | this.x = x;
25 | this.y = y;
26 | }
27 | }
28 |
29 | Node root;
30 |
31 | QuadTree() {
32 | root = generateNode();
33 | }
34 |
35 | void insert(Node n, FNode node, float minX, float minY, float maxX, float maxY) {
36 | if (Float.isNaN(minX) || Float.isNaN(minY) || Float.isNaN(maxX) || Float.isNaN(maxY)) {
37 | return;
38 | }
39 | if (n.isLeaf) {
40 | float nx = n.x;
41 | float ny = n.y;
42 |
43 | if (nx != NULL) {
44 | if (Math.abs(nx - node.x) + Math.abs(ny - node.y) < 0.01) {
45 | insertChild(n, node, minX, minY, maxX, maxY);
46 | } else {
47 | FNode nPoint = n.point;
48 | n.x = n.y = NULL;
49 | n.point = null;
50 | insertChild(n, nPoint, minX, minY, maxX, maxY);
51 | insertChild(n, node, minX, minY, maxX, maxY);
52 | }
53 | } else {
54 | n.x = node.x;
55 | n.y = node.y;
56 | n.point = node;
57 | }
58 | } else {
59 | insertChild(n, node, minX, minY, maxX, maxY);
60 | }
61 | }
62 |
63 | private void insertChild(Node n, FNode node, float minX, float minY, float maxX, float maxY) {
64 | float sx = (minX + maxX) * 0.5f;
65 | float sy = (minY + maxY) * 0.5f;
66 | boolean isRight = node.x >= sx;
67 | boolean isBottom = node.y >= sy;
68 | int right = isRight ? 1 : 0;
69 | int bottom = isBottom ? 1 : 0;
70 | int i = (bottom << 1) + right;
71 |
72 | n.isLeaf = false;
73 | if (n.children[i] == null) {
74 | Node nod = generateNode();
75 | n.children[i] = nod;
76 | n = nod;
77 | } else {
78 | n = n.children[i];
79 | }
80 |
81 | if (isRight) {
82 | minX = sx;
83 | } else {
84 | maxX = sx;
85 | }
86 |
87 | if (isBottom) {
88 | minY = sy;
89 | } else {
90 | maxY = sy;
91 | }
92 |
93 | insert(n, node, minX, minY, maxX, maxY);
94 | }
95 |
96 | /** generate a leaf node for quad tree. */
97 | private Node generateNode() {
98 | return new Node(true, new Node[4], null, NULL, NULL);
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/algorithm/ring/Ring.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.algorithm.ring;
2 |
3 | import android.graphics.PointF;
4 | import android.util.SparseIntArray;
5 | import com.gyso.treeview.model.ITraversal;
6 | import com.gyso.treeview.model.NodeModel;
7 | import com.gyso.treeview.model.TreeModel;
8 | import com.gyso.treeview.util.TreeViewLog;
9 | import java.util.HashMap;
10 | import java.util.LinkedList;
11 | import java.util.Map;
12 | import java.util.concurrent.atomic.AtomicBoolean;
13 | import java.util.concurrent.atomic.AtomicInteger;
14 |
15 | public class Ring {
16 | public static final String TAG = Ring.class.getSimpleName();
17 | private final static Map, Ring> RING_MAP = new HashMap<>();
18 | private final PointF center = new PointF();
19 | private final TreeModel> model;
20 | private final Map, PointF> nodeModelPointFMap = new HashMap<>();
21 | private final Map, Double> nodeModelAngleMap = new HashMap<>();
22 | private final Map, Double> piePartBaseAngleMap = new HashMap<>();
23 | protected SparseIntArray floorStart = new SparseIntArray(200);
24 | private final AtomicBoolean isGenPositionOK = new AtomicBoolean(true);
25 | private final Map, AtomicInteger> multiMap = new HashMap<>();
26 | private final AtomicInteger multi = new AtomicInteger(1);
27 | private Ring(TreeModel> model) {
28 | this.model = model;
29 | }
30 | public static Ring getInstance(TreeModel> model){
31 | if(model==null){
32 | return null;
33 | }
34 | Ring ring = RING_MAP.get(model);
35 | if(ring==null){
36 | ring = new Ring(model);
37 | RING_MAP.put(model,ring);
38 | }
39 | return ring;
40 | }
41 | public Ring setCenter(float x, float y) {
42 | center.x = x;
43 | center.y = y;
44 | TreeViewLog.e(TAG,"center["+x+","+y+"]");
45 | return this;
46 | }
47 |
48 | public Ring setFloorStart(SparseIntArray floorStart) {
49 | this.floorStart = floorStart;
50 | return this;
51 | }
52 |
53 | public boolean isGenPositionOK(){
54 | return isGenPositionOK.get();
55 | }
56 |
57 | public Map, PointF> genPositions() {
58 | if (model == null) {
59 | return null;
60 | }
61 | NodeModel> rootNode = model.getRootNode();
62 | nodeModelPointFMap.clear();
63 | nodeModelPointFMap.put(rootNode, new PointF(center.y, center.x));
64 | isGenPositionOK.set(true);
65 | int leafCount = rootNode.leafCount;
66 | LinkedList extends NodeModel>> rootNodeChildNodes = rootNode.getChildNodes();
67 | if(leafCount == 0 || rootNodeChildNodes.isEmpty()){
68 | return nodeModelPointFMap;
69 | }
70 |
71 | //keep in pie center
72 | double deltaAngle = 2f*Math.PI / (leafCount+multi.get());
73 | double pieAngle = 2f*Math.PI / leafCount;
74 | double sumAngleDelta = 0;
75 | double sumAnglePie = 0;
76 | for (NodeModel> rootChild : rootNodeChildNodes) {
77 | int count = rootChild.leafCount;
78 | double da ,dp;
79 | if(count<2){
80 | da = sumAngleDelta+deltaAngle/2;
81 | dp = sumAnglePie +pieAngle/2;
82 | sumAngleDelta += deltaAngle;
83 | sumAnglePie += pieAngle;
84 | }else{
85 | da = sumAngleDelta+deltaAngle*(count-1)/2f;
86 | dp = sumAnglePie + pieAngle*(count-1)/2f;
87 | sumAngleDelta += deltaAngle*(count-1);
88 | sumAnglePie += pieAngle*(count-1);
89 | }
90 | piePartBaseAngleMap.put(rootChild,dp-da);
91 | }
92 |
93 | //doTraversalNodes
94 | model.doTraversalNodes((ITraversal>) next -> {
95 | if(next.equals(rootNode)){
96 | return;
97 | }
98 | TreeViewLog.e(TAG,next+" -gyso");
99 | NodeModel> nextParentNode = next.getParentNode();
100 | if(nextParentNode !=null && !nextParentNode.equals(rootNode)){
101 | piePartBaseAngleMap.put(next,piePartBaseAngleMap.get(nextParentNode));
102 | }
103 | PointF pointF = nodeModelPointFMap.get(next);
104 | if (pointF == null) {
105 | pointF = new PointF();
106 | int deep = next.deep;
107 | int floor = next.floor;
108 | double angle = deltaAngle * deep;
109 | LinkedList extends NodeModel>> childNodes = next.getChildNodes();
110 | if(!childNodes.isEmpty()){
111 | int count = next.leafCount;
112 | if(count>=2){
113 | double d = deltaAngle*(count-1)/2f;
114 | angle += d;
115 | }
116 | }
117 | Double centerPieAngle = piePartBaseAngleMap.get(next);
118 | if(centerPieAngle!=null){
119 | angle += centerPieAngle;
120 | }
121 |
122 | float radius = floorStart.get(floor);
123 | TreeViewLog.e(TAG,"radius["+radius+"]angle["+angle+"]");
124 | pointF.x = (float) (radius * Math.sin(angle))+center.y;
125 | pointF.y = (float) (radius * Math.cos(angle))+center.x;
126 | nodeModelPointFMap.put(next, pointF);
127 | nodeModelAngleMap.put(next,angle);
128 |
129 | //keep acute angle for parent
130 | if(nextParentNode !=null && !nextParentNode.equals(rootNode)){
131 | PointF parentPosition = nodeModelPointFMap.get(nextParentNode);
132 | PointF rootPosition = nodeModelPointFMap.get(rootNode);
133 | PointF currentPosition = nodeModelPointFMap.get(next);
134 | double p2r = Math.hypot(parentPosition.x-rootPosition.x,parentPosition.y-rootPosition.y);
135 | double c2r = Math.hypot(currentPosition.x-rootPosition.x,currentPosition.y-rootPosition.y);
136 | double dAngle = Math.abs(nodeModelAngleMap.get(nextParentNode)-nodeModelAngleMap.get(next));
137 | double l1 = c2r*Math.abs(Math.cos(dAngle));
138 | TreeViewLog.e(TAG,"p2r["+p2r+"]c2r["+c2r+"]dAngle["+dAngle+"]Math.sin(dAngle)["+Math.sin(dAngle)+"]l1["+l1+"]l1 <= p2r["+(l1 <= p2r)+"]");
139 | //if(false){
140 | if(l1 <= p2r){
141 | //no good layout request layout
142 | isGenPositionOK.set(false);
143 | int d = leafCount/6;
144 | multi.addAndGet(d==0?1:d);
145 | TreeViewLog.e(TAG,"Calculate ring position false!!!");
146 | }
147 | }
148 | }
149 | });
150 | if(isGenPositionOK.get()){
151 | multi.set(1);
152 | }
153 | TreeViewLog.e(TAG,"isGenPositionOK["+isGenPositionOK+"]multi["+multi.get()+"]deltaAngle["+deltaAngle+"]nodeModelPointFMap{"+nodeModelPointFMap+"");
154 | return nodeModelPointFMap;
155 | }
156 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/algorithm/ring/RingForSimple.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.algorithm.ring;
2 |
3 | import android.graphics.Point;
4 | import android.graphics.PointF;
5 | import android.util.SparseIntArray;
6 | import com.gyso.treeview.model.ITraversal;
7 | import com.gyso.treeview.model.NodeModel;
8 | import com.gyso.treeview.model.TreeModel;
9 | import com.gyso.treeview.util.TreeViewLog;
10 |
11 | import java.util.ArrayDeque;
12 | import java.util.Deque;
13 | import java.util.HashMap;
14 | import java.util.LinkedList;
15 | import java.util.Map;
16 | import java.util.concurrent.atomic.AtomicInteger;
17 |
18 | public class RingForSimple {
19 | public static final String TAG = RingForSimple.class.getSimpleName();
20 | private final static Map, RingForSimple> RING_MAP = new HashMap<>();
21 | private final PointF center = new PointF();
22 | private final TreeModel> model;
23 | private final Map, PointF> nodeModelPointFMap = new HashMap<>();
24 | private final Map, Double> nodeModelAngleMap = new HashMap<>();
25 | protected SparseIntArray floorStart = new SparseIntArray(200);
26 | private final AtomicInteger multi = new AtomicInteger(1);
27 | private int maxDeep;
28 | private final Map, Point> nodeWidthMap = new HashMap<>();
29 | private RingForSimple(TreeModel> model) {
30 | this.model = model;
31 | }
32 | public static RingForSimple getInstance(TreeModel> model){
33 | if(model==null){
34 | return null;
35 | }
36 | RingForSimple ring = RING_MAP.get(model);
37 | if(ring==null){
38 | ring = new RingForSimple(model);
39 | RING_MAP.put(model,ring);
40 | }
41 | return ring;
42 | }
43 | public RingForSimple setCenter(float x, float y) {
44 | center.x = x;
45 | center.y = y;
46 | TreeViewLog.e(TAG,"center["+x+","+y+"]");
47 | return this;
48 | }
49 |
50 | public RingForSimple setFloorStart(SparseIntArray floorStart) {
51 | this.floorStart = floorStart;
52 | return this;
53 | }
54 |
55 | public void reconstruction(TreeModel> mTreeModel) {
56 | TreeViewLog.e(TAG,"reconstruction start");
57 | nodeWidthMap.clear();
58 | Deque deque = new ArrayDeque<>();
59 | NodeModel rootNode = mTreeModel.getRootNode();
60 | deque.add(rootNode);
61 | SparseIntArray deepSum = new SparseIntArray();
62 | //calculate base deep
63 | while (!deque.isEmpty()) {
64 | NodeModel cur = deque.poll();
65 | if(cur==null){
66 | return;
67 | }
68 | cur.deep = deepSum.get(cur.floor,0);
69 | record(cur);
70 | NodeModel tmp = cur.parentNode;
71 | while (!tmp.equals(rootNode)){
72 | record(tmp);
73 | tmp = tmp.parentNode;
74 | }
75 | deepSum.put(cur.floor, cur.deep+1);
76 | LinkedList childNodes = cur.getChildNodes();
77 | if (childNodes.size() > 0) {
78 | deque.addAll(childNodes);
79 | }
80 | }
81 | }
82 |
83 | private void record(NodeModel node) {
84 | if (node == null) {
85 | return;
86 | }
87 | Point point = nodeWidthMap.get(node);
88 | if(point==null){
89 | point = new Point();
90 | nodeWidthMap.put(node, point);
91 | }
92 | maxDeep = Math.max(node.deep, maxDeep);
93 | point.x= Math.max(node.deep, point.x);
94 | point.y = Math.min(node.deep, point.y);
95 | }
96 |
97 | public Map, PointF> genPositions() {
98 | if (model == null) {
99 | return null;
100 | }
101 | NodeModel> rootNode = model.getRootNode();
102 | nodeModelPointFMap.clear();
103 | nodeModelPointFMap.put(rootNode, new PointF(center.y, center.x));
104 | int leafCount = maxDeep;
105 | LinkedList extends NodeModel>> rootNodeChildNodes = rootNode.getChildNodes();
106 | if(leafCount == 0 || rootNodeChildNodes.isEmpty()){
107 | return nodeModelPointFMap;
108 | }
109 |
110 | //keep in pie center
111 | double pieAngle = 2f*Math.PI / leafCount;
112 |
113 | //doTraversalNodes
114 | model.doTraversalNodes((ITraversal>) next -> {
115 | if(next.equals(rootNode)){
116 | return;
117 | }
118 | PointF pointF = new PointF();
119 | int deep = next.deep;
120 | int floor = next.floor;
121 | double angle = pieAngle * deep;
122 | LinkedList extends NodeModel>> childNodes = next.getChildNodes();
123 | if(!childNodes.isEmpty()){
124 | Point point = nodeWidthMap.get(next);
125 | int count = point.x-point.y;
126 | if(count>=2){
127 | double d = pieAngle*(count-1)/2f;
128 | angle += d;
129 | }
130 | }
131 | float radius = floorStart.get(floor);
132 | TreeViewLog.e(TAG,"radius["+radius+"]angle["+angle+"]");
133 | pointF.x = (float) (radius * Math.sin(angle))+center.y;
134 | pointF.y = (float) (radius * Math.cos(angle))+center.x;
135 | nodeModelPointFMap.put(next, pointF);
136 | nodeModelAngleMap.put(next,angle);
137 | });
138 | return nodeModelPointFMap;
139 | }
140 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/algorithm/table/TableKey.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.algorithm.table;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.annotation.Nullable;
5 |
6 | public class TableKey {
7 | public int floor;
8 | public int deep;
9 |
10 | public TableKey(int floor, int deep) {
11 | this.floor = floor;
12 | this.deep = deep;
13 | }
14 |
15 | @Override
16 | public int hashCode() {
17 | return floor;
18 | }
19 |
20 | @Override
21 | public boolean equals(@Nullable Object obj) {
22 | if(obj instanceof TableKey){
23 | TableKey o = (TableKey)obj;
24 | return floor == o.floor && deep==o.deep;
25 | }
26 | return false;
27 | }
28 |
29 | @NonNull
30 | @Override
31 | public String toString() {
32 | return "["+floor+","+deep+"]";
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/algorithm/tight_table/TightTable.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.algorithm.tight_table;
2 |
3 | public class TightTable {
4 | public void tightTable(){
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/cache_pool/HolderPool.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.cache_pool;
2 |
3 | import android.graphics.PointF;
4 |
5 | import androidx.core.util.Pools;
6 |
7 | import com.gyso.treeview.adapter.TreeViewHolder;
8 |
9 | /**
10 | * @Author: 怪兽N
11 | * @Time: 2021/6/10 14:37
12 | * @Email: 674149099@qq.com
13 | * @WeChat: guaishouN
14 | * @Describe:
15 | * * Holder pool. {@link com.gyso.treeview.adapter.TreeViewHolder}
16 | * * NOTE: not safe pool, please just use in UI thread
17 | */
18 | public class HolderPool extends Pools.SimplePool> {
19 | public static final int DEFAULT_SIZE = 30;
20 | private final static HolderPool POOL = new HolderPool();
21 | /**
22 | * Creates a new instance.
23 | */
24 | public HolderPool() {
25 | super(DEFAULT_SIZE);
26 | }
27 |
28 | public TreeViewHolder> obtain(){
29 | return POOL.acquire();
30 | }
31 |
32 | public void free(TreeViewHolder> holder){
33 | try {
34 | POOL.release(holder);
35 | }catch (IllegalStateException e){
36 | e.printStackTrace();
37 | }
38 |
39 | }
40 |
41 | public static void freeAll(){
42 | while (POOL.acquire()!=null);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/cache_pool/PointPool.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.cache_pool;
2 |
3 | import android.graphics.PointF;
4 |
5 | import androidx.core.util.Pools;
6 |
7 | /**
8 | * @Author: 怪兽N
9 | * @Time: 2021/5/8 9:56
10 | * @Email: 674149099@qq.com
11 | * @WeChat: guaishouN
12 | * @Describe:
13 | * Point pool.
14 | * NOTE: not safe pool, please just use in UI thread
15 | */
16 | public class PointPool extends Pools.SimplePool {
17 | public static final int DEFAULT_SIZE = 20;
18 | private final static PointPool POOL = new PointPool();
19 | /**
20 | * Creates a new instance.
21 | */
22 | private PointPool() {
23 | super(DEFAULT_SIZE);
24 | }
25 |
26 | public static PointF obtain(){
27 | PointF point = POOL.acquire();
28 | if(point==null){
29 | return new PointF();
30 | }
31 | return point;
32 | }
33 |
34 | public static PointF obtain(float x, float y){
35 | PointF point = POOL.acquire();
36 | if(point==null){
37 | return new PointF(x,y);
38 | }
39 | point.set(x,y);
40 | return point;
41 | }
42 |
43 | public static void free(PointF p){
44 | POOL.release(p);
45 | }
46 |
47 | public static void freeAll(){
48 | while (POOL.acquire()!=null);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/layout/BoxHorizonLeftAndRightLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.layout;
2 |
3 | import android.content.Context;
4 | import android.graphics.Point;
5 | import android.view.View;
6 |
7 | import com.gyso.treeview.TreeViewContainer;
8 | import com.gyso.treeview.adapter.TreeViewHolder;
9 | import com.gyso.treeview.line.BaseLine;
10 | import com.gyso.treeview.model.NodeModel;
11 | import com.gyso.treeview.model.TreeModel;
12 | import com.gyso.treeview.util.ViewBox;
13 |
14 | import java.util.LinkedList;
15 |
16 | public class BoxHorizonLeftAndRightLayoutManager extends BoxRightTreeLayoutManager {
17 | private static final String TAG = BoxHorizonLeftAndRightLayoutManager.class.getSimpleName();
18 | private boolean isJustCalculate;
19 | public BoxHorizonLeftAndRightLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) {
20 | super(context, spaceParentToChild, spacePeerToPeer, baseline);
21 | }
22 |
23 | @Override
24 | public int getTreeLayoutType() {
25 | return LAYOUT_TYPE_HORIZON_LEFT_AND_RIGHT;
26 | }
27 |
28 | @Override
29 | public void onManagerFinishMeasureAllNodes(TreeViewContainer treeViewContainer) {
30 | getPadding(treeViewContainer);
31 | mContentViewBox.bottom += (paddingBox.bottom+paddingBox.top);
32 | extraDeltaX = mContentViewBox.right;
33 | mContentViewBox.right += (paddingBox.left+paddingBox.right+extraDeltaX);
34 | fixedViewBox.setValues(mContentViewBox);
35 | if(winHeight == 0 || winWidth==0){
36 | return;
37 | }
38 | float scale = 1f*winWidth/winHeight;
39 | float wr = 1f* mContentViewBox.getWidth()/winWidth;
40 | float hr = 1f* mContentViewBox.getHeight()/winHeight;
41 | if(wr>=hr){
42 | float bh = mContentViewBox.getWidth()/scale;
43 | fixedViewBox.bottom = (int)bh;
44 | }else{
45 | float bw = mContentViewBox.getHeight()*scale;
46 | fixedViewBox.right = (int)bw;
47 | }
48 | mFixedDx = fixedViewBox.getWidth()/2;
49 | mFixedDy = (fixedViewBox.getHeight()-mContentViewBox.getHeight())/2;
50 | }
51 |
52 | @Override
53 | public void performLayout(final TreeViewContainer treeViewContainer) {
54 | isJustCalculate = true;
55 | super.performLayout(treeViewContainer);
56 | isJustCalculate = false;
57 | final TreeModel> mTreeModel = treeViewContainer.getTreeModel();
58 | if (mTreeModel != null) {
59 | NodeModel> rootNode = mTreeModel.getRootNode();
60 | TreeViewHolder> rootNodeHolder = treeViewContainer.getTreeViewHolder(rootNode);
61 | View rootNodeView = rootNodeHolder == null ? null : rootNodeHolder.getView();
62 | if (rootNodeView == null) {
63 | throw new NullPointerException(" rootNodeView can not be null");
64 | }
65 | int rootCx = rootNodeView.getLeft()+rootNodeView.getMeasuredWidth()/2;
66 | int rootCy = rootNodeView.getTop()+rootNodeView.getMeasuredHeight()/2;
67 | //divide equally by two
68 | LinkedList extends NodeModel>> rootNodeChildNodes = rootNode.getChildNodes();
69 | Point divideDy = getDivideDy(rootNode, treeViewContainer);
70 | int centerAy = divideDy.x;
71 | int centerBy = divideDy.y;
72 | int divider = rootNodeChildNodes.size()/2;
73 | int count = 0;
74 | for (NodeModel> node : rootNodeChildNodes) {
75 | if(count moveDy(n,treeViewContainer, (rootCy - centerAy)));
78 | }else{
79 | //move to other side
80 | node.traverseIncludeSelf(n -> mirrorByCxDy(n,treeViewContainer,rootCx, (rootCy - centerBy)));
81 | }
82 | count++;
83 | }
84 | onManagerFinishLayoutAllNodes(treeViewContainer);
85 | }
86 | }
87 |
88 | private Point getDivideDy(NodeModel> rootNode, TreeViewContainer treeViewContainer){
89 | LinkedList extends NodeModel>> rootNodeChildNodes = rootNode.getChildNodes();
90 | int divider = rootNodeChildNodes.size()/2;
91 | int count = 0;
92 | int minA,maxA,minB,maxB;
93 | minA = minB = Integer.MAX_VALUE;
94 | maxA= maxB = Integer.MIN_VALUE;
95 | for (NodeModel> currentNode : rootNodeChildNodes) {
96 | TreeViewHolder> currentHolder = treeViewContainer.getTreeViewHolder(currentNode);
97 | View currentNodeView = currentHolder == null ? null : currentHolder.getView();
98 | if (currentNodeView == null) {
99 | throw new NullPointerException(" currentNodeView can not be null");
100 | }
101 | int left =currentNodeView.getLeft();
102 | int top = currentNodeView.getTop();
103 | int currentHeight = currentNodeView.getMeasuredHeight();
104 | int currentWidth = currentNodeView.getMeasuredWidth();
105 | if(count currentNode, TreeViewContainer treeViewContainer, int deltaY){
118 | TreeViewHolder> currentHolder = treeViewContainer.getTreeViewHolder(currentNode);
119 | View currentNodeView = currentHolder == null ? null : currentHolder.getView();
120 | if (currentNodeView == null) {
121 | throw new NullPointerException(" currentNodeView can not be null");
122 | }
123 | currentHolder.setHolderLayoutType(LAYOUT_TYPE_HORIZON_RIGHT);
124 | int currentWidth = currentNodeView.getMeasuredWidth();
125 | int currentHeight = currentNodeView.getMeasuredHeight();
126 | int left =currentNodeView.getLeft();
127 | int right = left+currentWidth;
128 | int top = deltaY+ currentNodeView.getTop();
129 | int bottom = top+currentHeight;
130 | ViewBox finalLocation = new ViewBox(top, left, bottom, right);
131 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer);
132 | }
133 |
134 | private void mirrorByCxDy(NodeModel> currentNode, TreeViewContainer treeViewContainer,int centerX, int deltaY){
135 | TreeViewHolder> currentHolder = treeViewContainer.getTreeViewHolder(currentNode);
136 | View currentNodeView = currentHolder == null ? null : currentHolder.getView();
137 | if (currentNodeView == null) {
138 | throw new NullPointerException(" currentNodeView can not be null");
139 | }
140 | currentHolder.setHolderLayoutType(LAYOUT_TYPE_HORIZON_LEFT);
141 | int right =centerX*2- currentNodeView.getLeft();
142 | int left =centerX*2- currentNodeView.getRight();
143 | int top = deltaY+currentNodeView.getTop();
144 | int bottom = deltaY+currentNodeView.getBottom();
145 | ViewBox finalLocation = new ViewBox(top, left, bottom, right);
146 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer);
147 | }
148 |
149 | @Override
150 | public void onManagerLayoutNode(NodeModel> currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) {
151 | if(isJustCalculate){
152 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
153 | return;
154 | }
155 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) {
156 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
157 | }
158 | }
159 |
160 | @Override
161 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) {
162 | if(!isJustCalculate){
163 | super.onManagerFinishLayoutAllNodes(treeViewContainer);
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/layout/BoxLeftTreeLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.layout;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import com.gyso.treeview.TreeViewContainer;
7 | import com.gyso.treeview.adapter.TreeViewHolder;
8 | import com.gyso.treeview.line.BaseLine;
9 | import com.gyso.treeview.model.ITraversal;
10 | import com.gyso.treeview.model.NodeModel;
11 | import com.gyso.treeview.model.TreeModel;
12 | import com.gyso.treeview.util.ViewBox;
13 |
14 | public class BoxLeftTreeLayoutManager extends BoxRightTreeLayoutManager{
15 | private static final String TAG = BoxLeftTreeLayoutManager.class.getSimpleName();
16 | private boolean isJustCalculate;
17 | public BoxLeftTreeLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) {
18 | super(context, spaceParentToChild, spacePeerToPeer, baseline);
19 | }
20 |
21 | @Override
22 | public int getTreeLayoutType() {
23 | return LAYOUT_TYPE_HORIZON_LEFT;
24 | }
25 |
26 | @Override
27 | public void performLayout(final TreeViewContainer treeViewContainer) {
28 | isJustCalculate = true;
29 | super.performLayout(treeViewContainer);
30 | isJustCalculate = false;
31 | final TreeModel> mTreeModel = treeViewContainer.getTreeModel();
32 | if (mTreeModel != null) {
33 | final int cx = fixedViewBox.getWidth()/2;
34 | mTreeModel.doTraversalNodes(new ITraversal>() {
35 | @Override
36 | public void next(NodeModel> next) {
37 | mirrorByCx(next,treeViewContainer,cx);
38 | }
39 | @Override
40 | public void finish() {
41 | onManagerFinishLayoutAllNodes(treeViewContainer);
42 | }
43 | });
44 | }
45 | }
46 |
47 | private void mirrorByCx(NodeModel> currentNode, TreeViewContainer treeViewContainer,int centerX){
48 | TreeViewHolder> currentHolder = treeViewContainer.getTreeViewHolder(currentNode);
49 | View currentNodeView = currentHolder == null ? null : currentHolder.getView();
50 | if (currentNodeView == null) {
51 | throw new NullPointerException(" currentNodeView can not be null");
52 | }
53 | int left = centerX*2 - currentNodeView.getRight();
54 | int right = centerX*2- currentNodeView.getLeft();
55 | int top = currentNodeView.getTop();
56 | int bottom =currentNodeView.getBottom();
57 | ViewBox finalLocation = new ViewBox(top, left, bottom, right);
58 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer);
59 | }
60 |
61 | @Override
62 | public void onManagerLayoutNode(NodeModel> currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) {
63 | if(isJustCalculate){
64 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
65 | return;
66 | }
67 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) {
68 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
69 | }
70 | }
71 |
72 | @Override
73 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) {
74 | if(!isJustCalculate){
75 | super.onManagerFinishLayoutAllNodes(treeViewContainer);
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/layout/BoxUpTreeLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.layout;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import com.gyso.treeview.TreeViewContainer;
7 | import com.gyso.treeview.adapter.TreeViewHolder;
8 | import com.gyso.treeview.line.BaseLine;
9 | import com.gyso.treeview.model.ITraversal;
10 | import com.gyso.treeview.model.NodeModel;
11 | import com.gyso.treeview.model.TreeModel;
12 | import com.gyso.treeview.util.ViewBox;
13 |
14 | public class BoxUpTreeLayoutManager extends BoxDownTreeLayoutManager {
15 | private static final String TAG = BoxUpTreeLayoutManager.class.getSimpleName();
16 | private boolean isJustCalculate;
17 | public BoxUpTreeLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) {
18 | super(context, spaceParentToChild, spacePeerToPeer, baseline);
19 | }
20 | @Override
21 | public int getTreeLayoutType() {
22 | return LAYOUT_TYPE_VERTICAL_UP;
23 | }
24 |
25 | @Override
26 | public void performLayout(final TreeViewContainer treeViewContainer) {
27 | isJustCalculate = true;
28 | super.performLayout(treeViewContainer);
29 | isJustCalculate = false;
30 | final TreeModel> mTreeModel = treeViewContainer.getTreeModel();
31 | if (mTreeModel != null) {
32 | final int cy= fixedViewBox.getHeight()/2;
33 | mTreeModel.doTraversalNodes(new ITraversal>() {
34 | @Override
35 | public void next(NodeModel> next) {
36 | mirrorByCy(next,treeViewContainer,cy);
37 | }
38 | @Override
39 | public void finish() {
40 | onManagerFinishLayoutAllNodes(treeViewContainer);
41 | }
42 | });
43 | }
44 | }
45 |
46 | private void mirrorByCy(NodeModel> currentNode, TreeViewContainer treeViewContainer,int centerY){
47 | TreeViewHolder> currentHolder = treeViewContainer.getTreeViewHolder(currentNode);
48 | View currentNodeView = currentHolder == null ? null : currentHolder.getView();
49 | if (currentNodeView == null) {
50 | throw new NullPointerException(" currentNodeView can not be null");
51 | }
52 | int left =currentNodeView.getLeft();
53 | int right = currentNodeView.getRight();
54 | int bottom= centerY*2- currentNodeView.getTop();
55 | int top = centerY*2- currentNodeView.getBottom();
56 | ViewBox finalLocation = new ViewBox(top, left, bottom, right);
57 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer);
58 | }
59 |
60 | @Override
61 | public void onManagerLayoutNode(NodeModel> currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) {
62 | if(isJustCalculate){
63 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
64 | return;
65 | }
66 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) {
67 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
68 | }
69 | }
70 |
71 | @Override
72 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) {
73 | if(!isJustCalculate){
74 | super.onManagerFinishLayoutAllNodes(treeViewContainer);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/layout/CompactLeftTreeLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.layout;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import com.gyso.treeview.TreeViewContainer;
7 | import com.gyso.treeview.adapter.TreeViewHolder;
8 | import com.gyso.treeview.line.BaseLine;
9 | import com.gyso.treeview.model.ITraversal;
10 | import com.gyso.treeview.model.NodeModel;
11 | import com.gyso.treeview.model.TreeModel;
12 | import com.gyso.treeview.util.ViewBox;
13 |
14 | public class CompactLeftTreeLayoutManager extends CompactRightTreeLayoutManager{
15 | private static final String TAG = TableLeftTreeLayoutManager.class.getSimpleName();
16 | private boolean isJustCalculate;
17 | public CompactLeftTreeLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) {
18 | super(context, spaceParentToChild, spacePeerToPeer, baseline);
19 | }
20 |
21 | @Override
22 | public int getTreeLayoutType() {
23 | return LAYOUT_TYPE_HORIZON_LEFT;
24 | }
25 |
26 | @Override
27 | public void performLayout(final TreeViewContainer treeViewContainer) {
28 | isJustCalculate = true;
29 | super.performLayout(treeViewContainer);
30 | isJustCalculate = false;
31 | final TreeModel> mTreeModel = treeViewContainer.getTreeModel();
32 | if (mTreeModel != null) {
33 | final int cx = fixedViewBox.getWidth()/2;
34 | mTreeModel.doTraversalNodes(new ITraversal>() {
35 | @Override
36 | public void next(NodeModel> next) {
37 | mirrorByCx(next,treeViewContainer,cx);
38 | }
39 | @Override
40 | public void finish() {
41 | onManagerFinishLayoutAllNodes(treeViewContainer);
42 | }
43 | });
44 | }
45 | }
46 |
47 | private void mirrorByCx(NodeModel> currentNode, TreeViewContainer treeViewContainer,int centerX){
48 | TreeViewHolder> currentHolder = treeViewContainer.getTreeViewHolder(currentNode);
49 | View currentNodeView = currentHolder == null ? null : currentHolder.getView();
50 | if (currentNodeView == null) {
51 | throw new NullPointerException(" currentNodeView can not be null");
52 | }
53 | int left = centerX*2 - currentNodeView.getRight();
54 | int right = centerX*2- currentNodeView.getLeft();
55 | int top = currentNodeView.getTop();
56 | int bottom =currentNodeView.getBottom();
57 | ViewBox finalLocation = new ViewBox(top, left, bottom, right);
58 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer);
59 | }
60 |
61 | @Override
62 | public void onManagerLayoutNode(NodeModel> currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) {
63 | if(isJustCalculate){
64 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
65 | return;
66 | }
67 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) {
68 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
69 | }
70 | }
71 |
72 | @Override
73 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) {
74 | if(!isJustCalculate){
75 | super.onManagerFinishLayoutAllNodes(treeViewContainer);
76 | }
77 | }
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/layout/CompactRightTreeLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.layout;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import com.gyso.treeview.R;
7 | import com.gyso.treeview.TreeViewContainer;
8 | import com.gyso.treeview.adapter.TreeViewHolder;
9 | import com.gyso.treeview.algorithm.table.Table;
10 | import com.gyso.treeview.line.BaseLine;
11 | import com.gyso.treeview.model.ITraversal;
12 | import com.gyso.treeview.model.NodeModel;
13 | import com.gyso.treeview.model.TreeModel;
14 | import com.gyso.treeview.util.DensityUtils;
15 | import com.gyso.treeview.util.TreeViewLog;
16 | import com.gyso.treeview.util.ViewBox;
17 |
18 | public class CompactRightTreeLayoutManager extends TreeLayoutManager {
19 | private static final String TAG = CompactRightTreeLayoutManager.class.getSimpleName();
20 |
21 | public CompactRightTreeLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) {
22 | super(context, spaceParentToChild, spacePeerToPeer, baseline);
23 | }
24 |
25 | @Override
26 | public void calculateByLayoutAlgorithm(TreeModel> mTreeModel) {
27 | new Table().reconstruction(mTreeModel,Table.COMPACT_TABLE);
28 | }
29 |
30 | @Override
31 | public int getTreeLayoutType() {
32 | return LAYOUT_TYPE_HORIZON_RIGHT;
33 | }
34 |
35 | @Override
36 | public void performMeasure(TreeViewContainer treeViewContainer) {
37 | final TreeModel> mTreeModel = treeViewContainer.getTreeModel();
38 | if (mTreeModel != null) {
39 | mContentViewBox.clear();
40 | floorMax.clear();
41 | deepMax.clear();
42 | ITraversal> traversal = new ITraversal>() {
43 | @Override
44 | public void next(NodeModel> next) {
45 | measure(next, treeViewContainer);
46 | }
47 |
48 | @Override
49 | public void finish() {
50 | onManagerFinishMeasureAllNodes(treeViewContainer);
51 | }
52 | };
53 | mTreeModel.doTraversalNodes(traversal);
54 | }
55 | }
56 |
57 | @Override
58 | public void onManagerFinishMeasureAllNodes(TreeViewContainer treeViewContainer) {
59 | getPadding(treeViewContainer);
60 | mContentViewBox.bottom += (paddingBox.bottom+paddingBox.top);
61 | mContentViewBox.right += (paddingBox.left+paddingBox.right);
62 | fixedViewBox.setValues(mContentViewBox);
63 | if(winHeight == 0 || winWidth==0){
64 | return;
65 | }
66 | float scale = 1f*winWidth/winHeight;
67 | float wr = 1f* mContentViewBox.getWidth()/winWidth;
68 | float hr = 1f* mContentViewBox.getHeight()/winHeight;
69 | if(wr>=hr){
70 | float bh = mContentViewBox.getWidth()/scale;
71 | fixedViewBox.bottom = (int)bh;
72 | }else{
73 | float bw = mContentViewBox.getHeight()*scale;
74 | fixedViewBox.right = (int)bw;
75 | }
76 | mFixedDx = (fixedViewBox.getWidth()-mContentViewBox.getWidth())/2;
77 | mFixedDy = (fixedViewBox.getHeight()-mContentViewBox.getHeight())/2;
78 |
79 | //compute floor start position
80 | for (int i = 0; i <= floorMax.size(); i++) {
81 | int fn = (i == floorMax.size())?floorMax.size():floorMax.keyAt(i);
82 | int preStart = floorStart.get(fn - 1, 0);
83 | int preMax = floorMax.get(fn - 1, 0);
84 | int startPos = (fn==0?(mFixedDx + paddingBox.left):spaceParentToChild) + preStart + preMax;
85 | floorStart.put(fn,startPos);
86 | }
87 |
88 | //compute deep start position
89 | for (int i = 0; i <= deepMax.size(); i++) {
90 | int dn = (i == deepMax.size())?deepMax.size():deepMax.keyAt(i);
91 | int preStart = deepStart.get(dn - 1, 0);
92 | int preMax = deepMax.get(dn - 1, 0);
93 | int startPos = (dn==0?(mFixedDy + paddingBox.top):spacePeerToPeer) + preStart + preMax;
94 | deepStart.put(dn,startPos);
95 | }
96 | }
97 |
98 | private void measure(NodeModel> node, TreeViewContainer treeViewContainer) {
99 | TreeViewHolder> currentHolder = treeViewContainer.getTreeViewHolder(node);
100 | View currentNodeView = currentHolder==null?null:currentHolder.getView();
101 | if(currentNodeView==null){
102 | throw new NullPointerException(" currentNodeView can not be null");
103 | }
104 | int preMaxW = floorMax.get(node.floor);
105 | int curW = currentNodeView.getMeasuredWidth();
106 | if(preMaxW < curW){
107 | floorMax.put(node.floor,curW);
108 | int delta = spaceParentToChild +curW-preMaxW;
109 | mContentViewBox.right += delta;
110 | }
111 |
112 | int preMaxH = deepMax.get(node.deep);
113 | int curH = currentNodeView.getMeasuredHeight();
114 | if(preMaxH < curH){
115 | deepMax.put(node.deep,curH);
116 | int delta = spacePeerToPeer +curH-preMaxH;
117 | mContentViewBox.bottom += delta;
118 | }
119 | }
120 |
121 | @Override
122 | public void performLayout(final TreeViewContainer treeViewContainer) {
123 | final TreeModel> mTreeModel = treeViewContainer.getTreeModel();
124 | if (mTreeModel != null) {
125 | mTreeModel.doTraversalNodes(new ITraversal>() {
126 | @Override
127 | public void next(NodeModel> next) {
128 | layoutNodes(next, treeViewContainer);
129 | }
130 |
131 | @Override
132 | public void finish() {
133 | onManagerFinishLayoutAllNodes(treeViewContainer);
134 | }
135 | });
136 | }
137 | }
138 |
139 | @Override
140 | public ViewBox getTreeLayoutBox() {
141 | return fixedViewBox;
142 | }
143 |
144 | private void layoutNodes(NodeModel> currentNode, TreeViewContainer treeViewContainer){
145 | TreeViewLog.e(TAG,"onLayout: "+currentNode);
146 | TreeViewHolder> currentHolder = treeViewContainer.getTreeViewHolder(currentNode);
147 | View currentNodeView = currentHolder==null?null:currentHolder.getView();
148 | int deep = currentNode.deep;
149 | int floor = currentNode.floor;
150 | if(currentNodeView==null){
151 | throw new NullPointerException(" currentNodeView can not be null");
152 | }
153 |
154 | int currentWidth = currentNodeView.getMeasuredWidth();
155 | int currentHeight = currentNodeView.getMeasuredHeight();
156 | int horizonCenterFix = Math.abs(currentHeight - deepMax.get(deep))/2;
157 |
158 |
159 | int top = deepStart.get(deep)+horizonCenterFix+extraDeltaY;
160 | int left = floorStart.get(floor)+extraDeltaX;
161 | int bottom = top+currentHeight;
162 | int right = left+currentWidth;
163 |
164 | ViewBox finalLocation = new ViewBox(top, left, bottom, right);
165 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer);
166 | }
167 |
168 | @Override
169 | public void onManagerLayoutNode(NodeModel> currentNode,
170 | View currentNodeView,
171 | ViewBox finalLocation,
172 | TreeViewContainer treeViewContainer){
173 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) {
174 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
175 | }
176 | }
177 | @Override
178 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer){
179 | layoutAnimate(treeViewContainer);
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/layout/CompactUpTreeLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.layout;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import com.gyso.treeview.TreeViewContainer;
7 | import com.gyso.treeview.adapter.TreeViewHolder;
8 | import com.gyso.treeview.line.BaseLine;
9 | import com.gyso.treeview.model.ITraversal;
10 | import com.gyso.treeview.model.NodeModel;
11 | import com.gyso.treeview.model.TreeModel;
12 | import com.gyso.treeview.util.ViewBox;
13 |
14 | public class CompactUpTreeLayoutManager extends CompactDownTreeLayoutManager {
15 | private static final String TAG = CompactUpTreeLayoutManager.class.getSimpleName();
16 | private boolean isJustCalculate;
17 | public CompactUpTreeLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) {
18 | super(context, spaceParentToChild, spacePeerToPeer, baseline);
19 | }
20 | @Override
21 | public int getTreeLayoutType() {
22 | return LAYOUT_TYPE_VERTICAL_UP;
23 | }
24 |
25 | @Override
26 | public void performLayout(final TreeViewContainer treeViewContainer) {
27 | isJustCalculate = true;
28 | super.performLayout(treeViewContainer);
29 | isJustCalculate = false;
30 | final TreeModel> mTreeModel = treeViewContainer.getTreeModel();
31 | if (mTreeModel != null) {
32 | final int cy= fixedViewBox.getHeight()/2;
33 | mTreeModel.doTraversalNodes(new ITraversal>() {
34 | @Override
35 | public void next(NodeModel> next) {
36 | mirrorByCy(next,treeViewContainer,cy);
37 | }
38 | @Override
39 | public void finish() {
40 | onManagerFinishLayoutAllNodes(treeViewContainer);
41 | }
42 | });
43 | }
44 | }
45 |
46 | private void mirrorByCy(NodeModel> currentNode, TreeViewContainer treeViewContainer,int centerY){
47 | TreeViewHolder> currentHolder = treeViewContainer.getTreeViewHolder(currentNode);
48 | View currentNodeView = currentHolder == null ? null : currentHolder.getView();
49 | if (currentNodeView == null) {
50 | throw new NullPointerException(" currentNodeView can not be null");
51 | }
52 | int left =currentNodeView.getLeft();
53 | int right = currentNodeView.getRight();
54 | int bottom= centerY*2- currentNodeView.getTop();
55 | int top = centerY*2- currentNodeView.getBottom();
56 | ViewBox finalLocation = new ViewBox(top, left, bottom, right);
57 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer);
58 | }
59 |
60 | @Override
61 | public void onManagerLayoutNode(NodeModel> currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) {
62 | if(isJustCalculate){
63 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
64 | return;
65 | }
66 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) {
67 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
68 | }
69 | }
70 |
71 | @Override
72 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) {
73 | if(!isJustCalculate){
74 | super.onManagerFinishLayoutAllNodes(treeViewContainer);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/layout/TableLeftTreeLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.layout;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import com.gyso.treeview.TreeViewContainer;
6 | import com.gyso.treeview.adapter.TreeViewHolder;
7 | import com.gyso.treeview.line.BaseLine;
8 | import com.gyso.treeview.model.ITraversal;
9 | import com.gyso.treeview.model.NodeModel;
10 | import com.gyso.treeview.model.TreeModel;
11 | import com.gyso.treeview.util.ViewBox;
12 |
13 | public class TableLeftTreeLayoutManager extends TableRightTreeLayoutManager {
14 | private static final String TAG = TableLeftTreeLayoutManager.class.getSimpleName();
15 | private boolean isJustCalculate;
16 | public TableLeftTreeLayoutManager(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) {
17 | super(context, spaceParentToChild, spacePeerToPeer, baseline);
18 | }
19 |
20 | @Override
21 | public int getTreeLayoutType() {
22 | return LAYOUT_TYPE_HORIZON_LEFT;
23 | }
24 |
25 | @Override
26 | public void performLayout(final TreeViewContainer treeViewContainer) {
27 | isJustCalculate = true;
28 | super.performLayout(treeViewContainer);
29 | isJustCalculate = false;
30 | final TreeModel> mTreeModel = treeViewContainer.getTreeModel();
31 | if (mTreeModel != null) {
32 | final int cx = fixedViewBox.getWidth()/2;
33 | mTreeModel.doTraversalNodes(new ITraversal>() {
34 | @Override
35 | public void next(NodeModel> next) {
36 | mirrorByCx(next,treeViewContainer,cx);
37 | }
38 | @Override
39 | public void finish() {
40 | onManagerFinishLayoutAllNodes(treeViewContainer);
41 | }
42 | });
43 | }
44 | }
45 |
46 | private void mirrorByCx(NodeModel> currentNode, TreeViewContainer treeViewContainer,int centerX){
47 | TreeViewHolder> currentHolder = treeViewContainer.getTreeViewHolder(currentNode);
48 | View currentNodeView = currentHolder == null ? null : currentHolder.getView();
49 | if (currentNodeView == null) {
50 | throw new NullPointerException(" currentNodeView can not be null");
51 | }
52 | int left = centerX*2 - currentNodeView.getRight();
53 | int right = centerX*2- currentNodeView.getLeft();
54 | int top = currentNodeView.getTop();
55 | int bottom =currentNodeView.getBottom();
56 | ViewBox finalLocation = new ViewBox(top, left, bottom, right);
57 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer);
58 | }
59 |
60 | @Override
61 | public void onManagerLayoutNode(NodeModel> currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) {
62 | if(isJustCalculate){
63 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
64 | return;
65 | }
66 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) {
67 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
68 | }
69 | }
70 |
71 | @Override
72 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) {
73 | if(!isJustCalculate){
74 | super.onManagerFinishLayoutAllNodes(treeViewContainer);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/layout/TableUpTreeLayoutManagerTable.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.layout;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 |
6 | import com.gyso.treeview.TreeViewContainer;
7 | import com.gyso.treeview.adapter.TreeViewHolder;
8 | import com.gyso.treeview.line.BaseLine;
9 | import com.gyso.treeview.model.ITraversal;
10 | import com.gyso.treeview.model.NodeModel;
11 | import com.gyso.treeview.model.TreeModel;
12 | import com.gyso.treeview.util.ViewBox;
13 |
14 | public class TableUpTreeLayoutManagerTable extends TableDownTreeLayoutManager {
15 | private static final String TAG = TableUpTreeLayoutManagerTable.class.getSimpleName();
16 | private boolean isJustCalculate;
17 | public TableUpTreeLayoutManagerTable(Context context, int spaceParentToChild, int spacePeerToPeer, BaseLine baseline) {
18 | super(context, spaceParentToChild, spacePeerToPeer, baseline);
19 | }
20 | @Override
21 | public int getTreeLayoutType() {
22 | return LAYOUT_TYPE_VERTICAL_UP;
23 | }
24 |
25 | @Override
26 | public void performLayout(final TreeViewContainer treeViewContainer) {
27 | isJustCalculate = true;
28 | super.performLayout(treeViewContainer);
29 | isJustCalculate = false;
30 | final TreeModel> mTreeModel = treeViewContainer.getTreeModel();
31 | if (mTreeModel != null) {
32 | final int cy= fixedViewBox.getHeight()/2;
33 | mTreeModel.doTraversalNodes(new ITraversal>() {
34 | @Override
35 | public void next(NodeModel> next) {
36 | mirrorByCy(next,treeViewContainer,cy);
37 | }
38 | @Override
39 | public void finish() {
40 | onManagerFinishLayoutAllNodes(treeViewContainer);
41 | }
42 | });
43 | }
44 | }
45 |
46 | private void mirrorByCy(NodeModel> currentNode, TreeViewContainer treeViewContainer,int centerY){
47 | TreeViewHolder> currentHolder = treeViewContainer.getTreeViewHolder(currentNode);
48 | View currentNodeView = currentHolder == null ? null : currentHolder.getView();
49 | if (currentNodeView == null) {
50 | throw new NullPointerException(" currentNodeView can not be null");
51 | }
52 | int left =currentNodeView.getLeft();
53 | int right = currentNodeView.getRight();
54 | int bottom= centerY*2- currentNodeView.getTop();
55 | int top = centerY*2- currentNodeView.getBottom();
56 | ViewBox finalLocation = new ViewBox(top, left, bottom, right);
57 | onManagerLayoutNode(currentNode, currentNodeView, finalLocation, treeViewContainer);
58 | }
59 |
60 | @Override
61 | public void onManagerLayoutNode(NodeModel> currentNode, View currentNodeView, ViewBox finalLocation, TreeViewContainer treeViewContainer) {
62 | if(isJustCalculate){
63 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
64 | return;
65 | }
66 | if (!layoutAnimatePrepare(currentNode, currentNodeView, finalLocation, treeViewContainer)) {
67 | currentNodeView.layout(finalLocation.left, finalLocation.top, finalLocation.right, finalLocation.bottom);
68 | }
69 | }
70 |
71 | @Override
72 | public void onManagerFinishLayoutAllNodes(TreeViewContainer treeViewContainer) {
73 | if(!isJustCalculate){
74 | super.onManagerFinishLayoutAllNodes(treeViewContainer);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/line/AngledLine.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.line;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Path;
8 | import android.graphics.PointF;
9 | import android.view.View;
10 |
11 | import com.gyso.treeview.adapter.DrawInfo;
12 | import com.gyso.treeview.adapter.TreeViewHolder;
13 | import com.gyso.treeview.cache_pool.PointPool;
14 | import com.gyso.treeview.layout.TreeLayoutManager;
15 | import com.gyso.treeview.model.NodeModel;
16 | import com.gyso.treeview.util.DensityUtils;
17 |
18 | /**
19 | * @Author: 怪兽N
20 | * @Time: 2021/5/18 16:47
21 | * @Email: 674149099@qq.com
22 | * @WeChat: guaishouN
23 | * @Describe:
24 | * AngledLine between two nodes
25 | */
26 | public class AngledLine extends BaseLine {
27 | public static final int DEFAULT_LINE_WIDTH_DP = 3;
28 | private int lineColor = Color.parseColor("#055287");
29 | private int lineWidth = DEFAULT_LINE_WIDTH_DP;
30 | public AngledLine() {
31 | super();
32 | }
33 |
34 | public AngledLine(int lineColor, int lineWidth_dp) {
35 | this();
36 | this.lineColor = lineColor;
37 | this.lineWidth = lineWidth_dp;
38 | }
39 |
40 | public void setLineColor(int lineColor) {
41 | this.lineColor = lineColor;
42 | }
43 |
44 | public void setLineWidth(int lineWidth) {
45 | this.lineWidth = lineWidth;
46 | }
47 | @Override
48 | public void draw(DrawInfo drawInfo) {
49 | Canvas canvas = drawInfo.getCanvas();
50 | TreeViewHolder> fromHolder = drawInfo.getFromHolder();
51 | TreeViewHolder> toHolder = drawInfo.getToHolder();
52 | Paint mPaint = drawInfo.getPaint();
53 | Path mPath = drawInfo.getPath();
54 | int holderLayoutType = toHolder.getHolderLayoutType();
55 | int spacePeerToPeer = drawInfo.getSpacePeerToPeer();
56 | int spaceParentToChild = drawInfo.getSpaceParentToChild();
57 |
58 | //get view and node
59 | View fromView = fromHolder.getView();
60 | NodeModel> fromNode = fromHolder.getNode();
61 | View toView = toHolder.getView();
62 | NodeModel> toNode = toHolder.getNode();
63 | Context context = fromView.getContext();
64 |
65 | PointF startPoint,point1,endPoint,point2;
66 | if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_RIGHT){
67 | startPoint = PointPool.obtain(fromView.getRight(),(fromView.getTop()+fromView.getBottom())/2f);
68 | point1 = PointPool.obtain(startPoint.x+spaceParentToChild/3f,startPoint.y);
69 | endPoint = PointPool.obtain(toView.getLeft(),(toView.getTop()+toView.getBottom())/2f);
70 | point2 = PointPool.obtain(startPoint.x+spaceParentToChild/3f,endPoint.y);
71 |
72 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_LEFT){
73 | startPoint = PointPool.obtain(fromView.getLeft(),(fromView.getTop()+fromView.getBottom())/2f);
74 | point1 = PointPool.obtain(startPoint.x-spaceParentToChild/3f,startPoint.y);
75 | endPoint = PointPool.obtain(toView.getRight(),(toView.getTop()+toView.getBottom())/2f);
76 | point2 = PointPool.obtain(startPoint.x-spaceParentToChild/3f,endPoint.y);
77 |
78 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_DOWN){
79 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getBottom());
80 | point1 = PointPool.obtain(startPoint.x,startPoint.y+spaceParentToChild/3f);
81 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getTop());
82 | point2 = PointPool.obtain(endPoint.x,startPoint.y+spaceParentToChild/3f);
83 |
84 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_UP){
85 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getTop());
86 | point1 = PointPool.obtain(startPoint.x,startPoint.y-spaceParentToChild/3f);
87 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getBottom());
88 | point2 = PointPool.obtain(endPoint.x,startPoint.y-spaceParentToChild/3f);
89 |
90 | }else{
91 | super.draw(drawInfo);
92 | return;
93 | }
94 |
95 | //set paint
96 | mPath.reset();
97 | mPaint.reset();
98 | mPaint.setColor(lineColor);
99 | mPaint.setStyle(Paint.Style.STROKE);
100 | mPaint.setStrokeWidth(DensityUtils.dp2px(context,lineWidth));
101 | mPaint.setAntiAlias(true);
102 |
103 | mPath.moveTo(startPoint.x,startPoint.y);
104 | mPath.lineTo(point1.x,point1.y);
105 | mPath.lineTo(point2.x,point2.y);
106 | mPath.lineTo(endPoint.x,endPoint.y);
107 |
108 | //do not forget release
109 | PointPool.free(startPoint);
110 | PointPool.free(point1);
111 | PointPool.free(point2);
112 | PointPool.free(endPoint);
113 | //draw
114 | canvas.drawPath(mPath,mPaint);
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/line/BaseLine.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.line;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Path;
8 | import android.view.View;
9 | import com.gyso.treeview.adapter.DrawInfo;
10 | import com.gyso.treeview.adapter.TreeViewHolder;
11 | import com.gyso.treeview.util.DensityUtils;
12 |
13 | /**
14 | * @Author: 怪兽N
15 | * @Time: 2021/5/7 21:02
16 | * @Email: 674149099@qq.com
17 | * @WeChat: guaishouN
18 | * @Describe:
19 | * line to connect the fromNodeView and toNodeView
20 | */
21 | public class BaseLine {
22 | /**
23 | * this method will be invoke when the tree view is onDispatchDraw
24 | */
25 | public void draw(DrawInfo drawInfo){
26 | Canvas canvas = drawInfo.getCanvas();
27 | TreeViewHolder> fromHolder = drawInfo.getFromHolder();
28 | TreeViewHolder> toHolder = drawInfo.getToHolder();
29 | Paint mPaint = drawInfo.getPaint();
30 | Path mPath = drawInfo.getPath();
31 |
32 | //get view and node
33 | View fromView = fromHolder.getView();
34 | View toView = toHolder.getView();
35 | Context context = fromView.getContext();
36 |
37 | //set paint
38 | mPaint.reset();
39 | mPath.reset();
40 | mPaint.setColor(Color.MAGENTA);
41 | mPaint.setStyle(Paint.Style.STROKE);
42 | mPaint.setStrokeWidth(DensityUtils.dp2px(context,3));
43 | mPaint.setAntiAlias(true);
44 |
45 | int fromCenterX = (fromView.getLeft()+fromView.getRight())/2;
46 | int fromCenterY = (fromView.getTop()+fromView.getBottom())/2;
47 | int toCenterX = (toView.getLeft()+toView.getRight())/2;
48 | int toCenterY = (toView.getTop()+toView.getBottom())/2;
49 |
50 | mPath.moveTo(fromCenterX, fromCenterY);
51 | mPath.lineTo(toCenterX, toCenterY);
52 |
53 | //draw
54 | canvas.drawPath(mPath,mPaint);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/line/DashLine.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.line;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.DashPathEffect;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.PointF;
10 | import android.view.View;
11 |
12 | import com.gyso.treeview.adapter.DrawInfo;
13 | import com.gyso.treeview.adapter.TreeViewHolder;
14 | import com.gyso.treeview.cache_pool.PointPool;
15 | import com.gyso.treeview.layout.TreeLayoutManager;
16 | import com.gyso.treeview.model.NodeModel;
17 | import com.gyso.treeview.util.DensityUtils;
18 |
19 | /**
20 | * @Author: 怪兽N
21 | * @Time: 2021/5/8 17:41
22 | * @Email: 674149099@qq.com
23 | * @WeChat: guaishouN
24 | * @Describe:
25 | * DashPathEffect line
26 | */
27 | public class DashLine extends BaseLine {
28 | public static final int DEFAULT_LINE_WIDTH_DP = 3;
29 | private int lineColor = Color.parseColor("#E57373");
30 | private int lineWidth = DEFAULT_LINE_WIDTH_DP;
31 | private final static DashPathEffect effect = new DashPathEffect(new float[]{20,10},10);
32 | public DashLine() {
33 | super();
34 | }
35 |
36 | public DashLine(int lineColor, int lineWidth_dp) {
37 | this();
38 | this.lineColor = lineColor;
39 | this.lineWidth = lineWidth_dp;
40 | }
41 |
42 | public void setLineColor(int lineColor) {
43 | this.lineColor = lineColor;
44 | }
45 |
46 | public void setLineWidth(int lineWidth) {
47 | this.lineWidth = lineWidth;
48 | }
49 |
50 | @Override
51 | public void draw(DrawInfo drawInfo) {
52 | Canvas canvas = drawInfo.getCanvas();
53 | TreeViewHolder> fromHolder = drawInfo.getFromHolder();
54 | TreeViewHolder> toHolder = drawInfo.getToHolder();
55 | Paint mPaint = drawInfo.getPaint();
56 | Path mPath = drawInfo.getPath();
57 | int holderLayoutType = toHolder.getHolderLayoutType();
58 | int spacePeerToPeer = drawInfo.getSpacePeerToPeer();
59 | int spaceParentToChild = drawInfo.getSpaceParentToChild();
60 |
61 | //get view and node
62 | View fromView = fromHolder.getView();
63 | NodeModel> fromNode = fromHolder.getNode();
64 | View toView = toHolder.getView();
65 | NodeModel> toNode = toHolder.getNode();
66 | Context context = fromView.getContext();
67 |
68 | PointF startPoint,point1,endPoint,point2;
69 | if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_RIGHT){
70 | startPoint = PointPool.obtain(fromView.getRight(),(fromView.getTop()+fromView.getBottom())/2f);
71 | point1 = PointPool.obtain(startPoint.x+DensityUtils.dp2px(context,15),startPoint.y);
72 | endPoint = PointPool.obtain(toView.getLeft(),(toView.getTop()+toView.getBottom())/2f);
73 | point2 = PointPool.obtain(startPoint.x,endPoint.y);
74 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_LEFT){
75 | startPoint = PointPool.obtain(fromView.getLeft(),(fromView.getTop()+fromView.getBottom())/2f);
76 | point1 = PointPool.obtain(startPoint.x-DensityUtils.dp2px(context,15),startPoint.y);
77 | endPoint = PointPool.obtain(toView.getRight(),(toView.getTop()+toView.getBottom())/2f);
78 | point2 = PointPool.obtain(startPoint.x,endPoint.y);
79 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_DOWN){
80 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getBottom());
81 | point1 = PointPool.obtain(startPoint.x,startPoint.y+DensityUtils.dp2px(context,15));
82 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getTop());
83 | point2 = PointPool.obtain(endPoint.x,startPoint.y);
84 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_UP){
85 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getTop());
86 | point1 = PointPool.obtain(startPoint.x,startPoint.y-DensityUtils.dp2px(context,15));
87 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getBottom());
88 | point2 = PointPool.obtain(endPoint.x,startPoint.y);
89 | }else{
90 | super.draw(drawInfo);
91 | return;
92 | }
93 |
94 | mPaint.reset();
95 | mPath.reset();
96 | //set paint
97 | mPaint.setColor(lineColor);
98 | mPaint.setStyle(Paint.Style.STROKE);
99 | mPaint.setStrokeWidth(DensityUtils.dp2px(context,lineWidth));
100 | mPaint.setAntiAlias(true);
101 | mPaint.setPathEffect(effect);
102 |
103 | mPath.moveTo(startPoint.x,startPoint.y);
104 | mPath.cubicTo(
105 | point1.x,point1.y,
106 | point2.x,point2.y,
107 | endPoint.x,endPoint.y);
108 |
109 | //do not forget release
110 | PointPool.free(startPoint);
111 | PointPool.free(point1);
112 | PointPool.free(point2);
113 | PointPool.free(endPoint);
114 | //draw
115 | canvas.drawPath(mPath,mPaint);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/line/SmoothLine.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.line;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Path;
8 | import android.graphics.PointF;
9 | import android.view.View;
10 |
11 | import com.gyso.treeview.adapter.DrawInfo;
12 | import com.gyso.treeview.adapter.TreeViewHolder;
13 | import com.gyso.treeview.cache_pool.PointPool;
14 | import com.gyso.treeview.layout.TreeLayoutManager;
15 | import com.gyso.treeview.model.NodeModel;
16 | import com.gyso.treeview.util.DensityUtils;
17 |
18 | /**
19 | * @Author: 怪兽N
20 | * @Time: 2021/5/8 9:47
21 | * @Email: 674149099@qq.com
22 | * @WeChat: guaishouN
23 | * @Describe:
24 | * Simple smooth line
25 | */
26 | public class SmoothLine extends BaseLine {
27 | public static final int DEFAULT_LINE_WIDTH_DP = 3;
28 | private int lineColor = Color.parseColor("#055287");
29 | private int lineWidth = DEFAULT_LINE_WIDTH_DP;
30 | public SmoothLine() {
31 | super();
32 | }
33 |
34 | public SmoothLine(int lineColor, int lineWidth_dp) {
35 | this();
36 | this.lineColor = lineColor;
37 | this.lineWidth = lineWidth_dp;
38 | }
39 |
40 | public void setLineColor(int lineColor) {
41 | this.lineColor = lineColor;
42 | }
43 |
44 | public void setLineWidth(int lineWidth) {
45 | this.lineWidth = lineWidth;
46 | }
47 |
48 | @Override
49 | public void draw(DrawInfo drawInfo) {
50 | Canvas canvas = drawInfo.getCanvas();
51 | TreeViewHolder> fromHolder = drawInfo.getFromHolder();
52 | TreeViewHolder> toHolder = drawInfo.getToHolder();
53 | Paint mPaint = drawInfo.getPaint();
54 | Path mPath = drawInfo.getPath();
55 | int holderLayoutType = toHolder.getHolderLayoutType();
56 | int spacePeerToPeer = drawInfo.getSpacePeerToPeer();
57 | int spaceParentToChild = drawInfo.getSpaceParentToChild();
58 |
59 | //get view and node
60 | View fromView = fromHolder.getView();
61 | NodeModel> fromNode = fromHolder.getNode();
62 | View toView = toHolder.getView();
63 | NodeModel> toNode = toHolder.getNode();
64 | Context context = fromView.getContext();
65 |
66 | PointF startPoint,point1,endPoint,point2;
67 | if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_RIGHT){
68 | startPoint = PointPool.obtain(fromView.getRight(),(fromView.getTop()+fromView.getBottom())/2f);
69 | point1 = PointPool.obtain(startPoint.x+DensityUtils.dp2px(context,15),startPoint.y);
70 | endPoint = PointPool.obtain(toView.getLeft(),(toView.getTop()+toView.getBottom())/2f);
71 | point2 = PointPool.obtain(startPoint.x,endPoint.y);
72 |
73 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_HORIZON_LEFT){
74 | startPoint = PointPool.obtain(fromView.getLeft(),(fromView.getTop()+fromView.getBottom())/2f);
75 | point1 = PointPool.obtain(startPoint.x-DensityUtils.dp2px(context,15),startPoint.y);
76 | endPoint = PointPool.obtain(toView.getRight(),(toView.getTop()+toView.getBottom())/2f);
77 | point2 = PointPool.obtain(startPoint.x,endPoint.y);
78 |
79 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_DOWN){
80 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getBottom());
81 | point1 = PointPool.obtain(startPoint.x,startPoint.y+DensityUtils.dp2px(context,15));
82 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getTop());
83 | point2 = PointPool.obtain(endPoint.x,startPoint.y);
84 |
85 | }else if(holderLayoutType== TreeLayoutManager.LAYOUT_TYPE_VERTICAL_UP){
86 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getTop());
87 | point1 = PointPool.obtain(startPoint.x,startPoint.y-DensityUtils.dp2px(context,15));
88 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getBottom());
89 | point2 = PointPool.obtain(endPoint.x,startPoint.y);
90 |
91 | }else{
92 | super.draw(drawInfo);
93 | return;
94 | }
95 |
96 | mPaint.reset();
97 | mPath.reset();
98 | //set paint
99 | mPaint.setColor(lineColor);
100 | mPaint.setStyle(Paint.Style.STROKE);
101 | mPaint.setStrokeWidth(DensityUtils.dp2px(context,lineWidth));
102 | mPaint.setAntiAlias(true);
103 | mPath.moveTo(startPoint.x,startPoint.y);
104 | mPath.cubicTo(
105 | point1.x,point1.y,
106 | point2.x,point2.y,
107 | endPoint.x,endPoint.y);
108 | //do not forget release
109 | PointPool.free(startPoint);
110 | PointPool.free(point1);
111 | PointPool.free(point2);
112 | PointPool.free(endPoint);
113 | //draw
114 | canvas.drawPath(mPath,mPaint);
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/line/StraightLine.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.line;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Path;
8 | import android.graphics.PointF;
9 | import android.view.View;
10 |
11 | import com.gyso.treeview.adapter.DrawInfo;
12 | import com.gyso.treeview.adapter.TreeViewHolder;
13 | import com.gyso.treeview.cache_pool.PointPool;
14 | import com.gyso.treeview.layout.TreeLayoutManager;
15 | import com.gyso.treeview.model.NodeModel;
16 | import com.gyso.treeview.util.DensityUtils;
17 |
18 | /**
19 | * @Author: 怪兽N
20 | * @Time: 2021/5/8 9:40
21 | * @Email: 674149099@qq.com
22 | * @WeChat: guaishouN
23 | * @Describe:
24 | * Straight Line
25 | */
26 | public class StraightLine extends BaseLine {
27 | public static final int DEFAULT_LINE_WIDTH_DP = 3;
28 | private int lineColor = Color.parseColor("#055287");
29 | private int lineWidth = DEFAULT_LINE_WIDTH_DP;
30 | public StraightLine() {
31 | super();
32 | }
33 |
34 | public StraightLine(int lineColor, int lineWidth_dp) {
35 | this();
36 | this.lineColor = lineColor;
37 | this.lineWidth = lineWidth_dp;
38 | }
39 |
40 | public void setLineColor(int lineColor) {
41 | this.lineColor = lineColor;
42 | }
43 |
44 | public void setLineWidth(int lineWidth) {
45 | this.lineWidth = lineWidth;
46 | }
47 |
48 | @Override
49 | public void draw(DrawInfo drawInfo) {
50 | Canvas canvas = drawInfo.getCanvas();
51 | TreeViewHolder> fromHolder = drawInfo.getFromHolder();
52 | TreeViewHolder> toHolder = drawInfo.getToHolder();
53 | Paint mPaint = drawInfo.getPaint();
54 | Path mPath = drawInfo.getPath();
55 | int holderLayoutType = toHolder.getHolderLayoutType();
56 | int spacePeerToPeer = drawInfo.getSpacePeerToPeer();
57 | int spaceParentToChild = drawInfo.getSpaceParentToChild();
58 | //get view and node
59 | View fromView = fromHolder.getView();
60 | NodeModel> fromNode = fromHolder.getNode();
61 | View toView = toHolder.getView();
62 | NodeModel> toNode = toHolder.getNode();
63 | Context context = fromView.getContext();
64 |
65 |
66 | PointF startPoint,endPoint;
67 | if(holderLayoutType == TreeLayoutManager.LAYOUT_TYPE_HORIZON_RIGHT){
68 | startPoint = PointPool.obtain(fromView.getRight(),(fromView.getTop()+fromView.getBottom())/2f);
69 | endPoint = PointPool.obtain(toView.getLeft(),(toView.getTop()+toView.getBottom())/2f);
70 |
71 | }else if(holderLayoutType == TreeLayoutManager.LAYOUT_TYPE_HORIZON_LEFT){
72 | startPoint = PointPool.obtain(fromView.getLeft(),(fromView.getTop()+fromView.getBottom())/2f);
73 | endPoint = PointPool.obtain(toView.getRight(),(toView.getTop()+toView.getBottom())/2f);
74 |
75 | }else if(holderLayoutType == TreeLayoutManager.LAYOUT_TYPE_VERTICAL_UP){
76 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getTop());
77 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getBottom());
78 |
79 | }else if (holderLayoutType == TreeLayoutManager.LAYOUT_TYPE_VERTICAL_DOWN){
80 | startPoint = PointPool.obtain((fromView.getLeft()+fromView.getRight())/2f,fromView.getBottom());
81 | endPoint = PointPool.obtain((toView.getLeft()+toView.getRight())/2f,toView.getTop());
82 |
83 | }else{
84 | super.draw(drawInfo);
85 | return;
86 | }
87 |
88 | //set paint
89 | mPaint.reset();
90 | mPath.reset();
91 | mPaint.setColor(lineColor);
92 | mPaint.setStyle(Paint.Style.STROKE);
93 | mPaint.setStrokeWidth(DensityUtils.dp2px(context,lineWidth));
94 | mPaint.setAntiAlias(true);
95 |
96 | mPath.moveTo(startPoint.x,startPoint.y);
97 | mPath.lineTo(endPoint.x,endPoint.y);
98 | //release
99 | PointPool.free(startPoint);
100 | PointPool.free(endPoint);
101 | //draw
102 | canvas.drawPath(mPath,mPaint);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/listener/TreeViewControlListener.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.listener;
2 |
3 | import android.view.View;
4 |
5 | import androidx.annotation.Nullable;
6 |
7 | import com.gyso.treeview.model.NodeModel;
8 |
9 | /**
10 | * @Author: 怪兽N
11 | * @Time: 2021/6/15
12 | * @Email: 674149099@qq.com
13 | * @WeChat: guaishouN
14 | * @Describe:
15 | * Listener for drag-move node, scale, drag the hold treeView
16 | */
17 | public interface TreeViewControlListener {
18 | int MIN_SCALE = -1;
19 | int FREE_SCALE = 0;
20 | int MAX_SCALE = 1;
21 | void onScaling(int state, int percent);
22 | void onDragMoveNodesHit(@Nullable NodeModel> draggingNode, @Nullable NodeModel> hittingNode, @Nullable View draggingView, @Nullable View hittingView);
23 | }
24 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/listener/TreeViewItemClick.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.listener;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * guaishouN 674149099@qq.com
7 | */
8 |
9 | public interface TreeViewItemClick {
10 | void onItemClick(View item);
11 | }
12 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/listener/TreeViewItemLongClick.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.listener;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * guaishouN 674149099@qq.com
7 | */
8 |
9 | public interface TreeViewItemLongClick {
10 | void onLongClick(View view);
11 | }
12 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/listener/TreeViewNotifier.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.listener;
2 |
3 |
4 | import com.gyso.treeview.model.NodeModel;
5 |
6 | /**
7 | * @Author: 怪兽N
8 | * @Time: 2021/4/23
9 | * @Email: 674149099@qq.com
10 | * @WeChat: guaishouN
11 | * @Describe:
12 | */
13 | public interface TreeViewNotifier{
14 | void onDataSetChange();
15 | void onRemoveNode(NodeModel>nodeModel);
16 | void onRemoveChildNodes(NodeModel> parentNode);
17 | void onItemViewChange(NodeModel> nodeModel);
18 | void onAddNodes(NodeModel> parent, NodeModel>... childNodes);
19 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/model/ITraversal.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.model;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * traversal callback
7 | * guaishouN 674149099@qq.com
8 | */
9 |
10 | public interface ITraversal extends Serializable {
11 | void next(T next);
12 | default void finish(){}
13 | }
14 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/model/TreeModel.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.model;
2 |
3 | import android.util.SparseArray;
4 | import java.io.Serializable;
5 | import java.util.ArrayDeque;
6 | import java.util.Deque;
7 | import java.util.LinkedList;
8 | import java.util.List;
9 | import java.util.Stack;
10 |
11 | /**
12 | * manager the tree data nodes
13 | * guaishouN 674149099@qq.com
14 | */
15 | public class TreeModel implements Serializable {
16 | private static final String TAG = TreeModel.class.getSimpleName();
17 | /**
18 | * the root for the tree
19 | */
20 | private NodeModel rootNode;
21 | private SparseArray> arrayByFloor = new SparseArray<>(10);
22 | private transient ITraversal> iTraversal;
23 | private int maxDeep =0;
24 | private int minDeep =0;
25 | public TreeModel(NodeModel rootNode) {
26 | this.rootNode = rootNode;
27 | }
28 |
29 | private boolean finishTraversal = false;
30 |
31 | /**
32 | * add the node in some father node
33 | * @param parent
34 | * @param childNodes
35 | */
36 | @SafeVarargs
37 | public final void addNode(NodeModel> parent, NodeModel>... childNodes) {
38 | if(parent!=null&&childNodes!=null && childNodes.length>0){
39 | parent.treeModel = this;
40 | List> nodeModels = new LinkedList<>();
41 | for (int i = 0; i < childNodes.length; i++) {
42 | nodeModels.add((NodeModel)childNodes[i]);
43 | childNodes[i].treeModel = this;
44 | }
45 | ((NodeModel)parent).addChildNodes(nodeModels);
46 | for(NodeModel> child:childNodes){
47 | child.traverseIncludeSelf(next->{
48 | next.floor = next.parentNode.floor+1;
49 | List floorList = getFloorList(next.floor);
50 | floorList.add(next);
51 | });
52 | }
53 | }
54 | }
55 |
56 | /**
57 | * remove
58 | * @param parent p node
59 | * @param childNode c node
60 | */
61 | public void removeNode(NodeModel> parent, NodeModel> childNode) {
62 | if(parent!=null&&childNode!=null){
63 | parent.removeChildNode(childNode);
64 | childNode.traverseIncludeSelf(next->{
65 | List nf = getFloorList(next.floor);
66 | nf.remove(next);
67 | next.floor = 0;
68 | });
69 | }
70 | }
71 |
72 | public NodeModel getRootNode() {
73 | return rootNode;
74 | }
75 |
76 |
77 | /**
78 | *child nodes will ergodic in the last
79 | * 广度遍历
80 | * breadth search
81 | * For every floor , child nodes has been sort 0--->n;
82 | * And node will been display one by one floor.
83 | */
84 | private void ergodicTreeByFloor() {
85 | Deque> deque = new ArrayDeque<>();
86 | NodeModel rootNode = getRootNode();
87 | deque.add(rootNode);
88 | while (!deque.isEmpty()) {
89 | rootNode = deque.poll();
90 | if (iTraversal != null) {
91 | iTraversal.next(rootNode);
92 | }
93 | if(this.finishTraversal){
94 | break;
95 | }
96 | if(rootNode==null){
97 | continue;
98 | }
99 | LinkedList> childNodes = rootNode.getChildNodes();
100 | if (childNodes.size() > 0) {
101 | deque.addAll(childNodes);
102 | }
103 | }
104 | if (iTraversal != null) {
105 | iTraversal.finish();
106 | this.finishTraversal = false;
107 | }
108 | }
109 |
110 | public SparseArray> getArrayByFloor() {
111 | return arrayByFloor;
112 | }
113 |
114 | /**
115 | * @param floor level
116 | * @return all nodes in the same floor
117 | */
118 | public List getFloorList(int floor){
119 | LinkedList nodeModels = arrayByFloor.get(floor);
120 | if(nodeModels==null){
121 | nodeModels = new LinkedList<>();
122 | arrayByFloor.put(floor,nodeModels);
123 | }
124 | return nodeModels;
125 | }
126 |
127 | public void setFinishTraversal(boolean finishTraversal) {
128 | this.finishTraversal = finishTraversal;
129 | }
130 |
131 | public void doTraversalNodes(ITraversal> ITraversal){
132 | doTraversalNodes(ITraversal,true);
133 | }
134 |
135 | /**
136 | * when ergodic this tree, it will call back on {@link ITraversal)}
137 | * @param ITraversal node
138 | */
139 | public void doTraversalNodes(ITraversal> ITraversal, boolean isOrderByFloor) {
140 | this.iTraversal = ITraversal;
141 | this.finishTraversal = false;
142 | if(isOrderByFloor){
143 | ergodicTreeByFloor();
144 | }else{
145 | ergodicTreeByDeep();
146 | }
147 | }
148 |
149 | /**
150 | *child nodes will ergodic by deep
151 | * 深度遍历
152 | * depth search
153 | * For every child node list has been sort 0--->n;
154 | * And node will been display one then the children by deep until end.
155 | */
156 | private void ergodicTreeByDeep(){
157 | Stack> stack = new Stack<>();
158 | NodeModel rootNode = getRootNode();
159 | stack.add(rootNode);
160 | while (!stack.isEmpty()) {
161 | rootNode = stack.pop();
162 | if (iTraversal != null) {
163 | iTraversal.next(rootNode);
164 | }
165 | if(this.finishTraversal){
166 | break;
167 | }
168 | if(rootNode==null){
169 | continue;
170 | }
171 | LinkedList> childNodes = rootNode.getChildNodes();
172 | if (childNodes.size() > 0) {
173 | stack.addAll(childNodes);
174 | }
175 | }
176 | if (iTraversal != null) {
177 | iTraversal.finish();
178 | this.finishTraversal = false;
179 | }
180 | }
181 |
182 | public int getMaxDeep() {
183 | return maxDeep;
184 | }
185 |
186 | public void setMaxDeep(int maxDeep) {
187 | this.maxDeep = maxDeep;
188 | }
189 |
190 | public int getMinDeep() {
191 | return minDeep;
192 | }
193 |
194 | public void setMinDeep(int minDeep) {
195 | this.minDeep = minDeep;
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/touch/DragBlock.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.touch;
2 |
3 | import android.graphics.PointF;
4 | import android.os.Handler;
5 | import android.os.Looper;
6 | import android.os.SystemClock;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.view.ViewParent;
10 | import android.view.animation.Interpolator;
11 | import android.widget.OverScroller;
12 |
13 | import com.gyso.treeview.R;
14 | import com.gyso.treeview.TreeViewContainer;
15 | import com.gyso.treeview.adapter.TreeViewHolder;
16 | import com.gyso.treeview.cache_pool.PointPool;
17 | import com.gyso.treeview.model.NodeModel;
18 | import com.gyso.treeview.util.ViewBox;
19 |
20 | import java.util.ArrayList;
21 | import java.util.HashMap;
22 | import java.util.LinkedList;
23 | import java.util.List;
24 | import java.util.Map;
25 |
26 | /**
27 | * @Author: 怪兽N
28 | * @Time: 2021/6/7 17:56
29 | * @Email: 674149099@qq.com
30 | * @WeChat: guaishouN
31 | * @Describe:
32 | * drag block
33 | */
34 | public class DragBlock {
35 | private final List tmp;
36 | private volatile boolean isDragging;
37 | private final TreeViewContainer container;
38 | private final Map originPositionMap;
39 | private final OverScroller mScroller;
40 | private PointF prePointF = null;
41 | /**
42 | * Interpolator defining the animation curve for mScroller
43 | */
44 | private static final Interpolator sInterpolator = t -> {
45 | t -= 1.0f;
46 | return t * t * t * t * t + 1.0f;
47 | };
48 |
49 | public DragBlock(TreeViewContainer parent){
50 | tmp = new ArrayList<>();
51 | container = parent;
52 | this.mScroller = new OverScroller(container.getContext(), sInterpolator);
53 | originPositionMap = new HashMap<>();
54 | }
55 |
56 | public boolean load(View view){
57 | if(originPositionMap.isEmpty() && tmp.isEmpty()){
58 | tmp.add(view);
59 | originPositionMap.put(view,new ViewBox(view));
60 | addItem(view);
61 | return true;
62 | }
63 | return false;
64 | }
65 |
66 | private void addItem(View view){
67 | Object tag = view.getTag(R.id.item_holder);
68 | if(tag instanceof TreeViewHolder){
69 | TreeViewHolder> holder = (TreeViewHolder>)tag;
70 | NodeModel> node = holder.getNode();
71 | for (NodeModel> n:node.getChildNodes()){
72 | TreeViewHolder> h = container.getTreeViewHolder(n);
73 | tmp.add(h.getView());
74 | originPositionMap.put(h.getView(),new ViewBox(h.getView()));
75 | addItem(h.getView());
76 | }
77 | }
78 | }
79 |
80 | public void drag(int dx, int dy){
81 | if(!mScroller.isFinished()){
82 | return;
83 | }
84 | this.isDragging = true;
85 | for (int i = 0; i < tmp.size(); i++) {
86 | View view = tmp.get(i);
87 | view.offsetLeftAndRight(dx);
88 | view.offsetTopAndBottom(dy);
89 | }
90 | }
91 |
92 | public void setDragging(boolean dragging) {
93 | isDragging = dragging;
94 | }
95 |
96 | private void autoRelease(){
97 | if(mScroller.isFinished() && !isDragging){
98 | originPositionMap.clear();
99 | tmp.clear();
100 | System.gc();
101 | }
102 | }
103 |
104 | public void smoothRecover(View referenceView) {
105 | if(!mScroller.isFinished()){
106 | return;
107 | }
108 | ViewBox rBox = originPositionMap.get(referenceView);
109 | if(rBox ==null){
110 | return;
111 | }
112 | prePointF=PointPool.obtain(0f,0f);
113 | mScroller.startScroll(0,0,referenceView.getLeft()-rBox.left,referenceView.getTop()-rBox.top);
114 | }
115 |
116 | public boolean computeScroll() {
117 | boolean isSuc = mScroller.computeScrollOffset();
118 | if(isSuc){
119 | PointF curPointF = PointPool.obtain(mScroller.getCurrX(), mScroller.getCurrY());
120 | mScroller.getCurrY();
121 | for (int i = 0; i < tmp.size(); i++) {
122 | View view = tmp.get(i);
123 | int dx = (int)(curPointF.x-prePointF.x);
124 | int dy = (int)(curPointF.y-prePointF.y);
125 | view.offsetLeftAndRight(-dx);
126 | view.offsetTopAndBottom(-dy);
127 | }
128 | if(prePointF!=null){
129 | prePointF.set(curPointF);
130 | }
131 | PointPool.free(curPointF);
132 | return true;
133 | }
134 | autoRelease();
135 | return false;
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/util/DensityUtils.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.util;
2 |
3 | import android.content.Context;
4 | import android.util.TypedValue;
5 |
6 | /**
7 | * 单位转换 工具类
8 | */
9 | public class DensityUtils {
10 |
11 | /**
12 | * dp转px
13 | */
14 | public static int dp2px(Context context, float dpVal) {
15 | int result = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources()
16 | .getDisplayMetrics());
17 | return result;
18 | }
19 |
20 | /**
21 | * sp转px
22 | */
23 | public static int sp2px(Context context, float spVal) {
24 | int result = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, context.getResources()
25 | .getDisplayMetrics());
26 | return result;
27 | }
28 |
29 | /**
30 | * px转dp
31 | */
32 | public static int px2dp(Context context, float pxVal) {
33 | final float scale = context.getResources().getDisplayMetrics().density;
34 | int result = (int) (pxVal / scale);
35 | return result;
36 | }
37 |
38 | /**
39 | * px转sp
40 | */
41 | public static float px2sp(Context context, float pxVal) {
42 | int result = (int) (pxVal / context.getResources().getDisplayMetrics().scaledDensity);
43 | return result;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/util/Interpolators.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.util;
2 |
3 |
4 | import android.view.animation.AccelerateDecelerateInterpolator;
5 | import android.view.animation.AccelerateInterpolator;
6 | import android.view.animation.BounceInterpolator;
7 | import android.view.animation.DecelerateInterpolator;
8 | import android.view.animation.Interpolator;
9 | import android.view.animation.LinearInterpolator;
10 | import android.view.animation.PathInterpolator;
11 |
12 |
13 | /**
14 | * @Author: 怪兽N
15 | * @Time: 2021/6/10 16:24
16 | * @Email: 674149099@qq.com
17 | * @WeChat: guaishouN
18 | * @Describe:
19 | * Utility class to receive interpolators from
20 | */
21 | public class Interpolators {
22 | public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
23 |
24 | /**
25 | * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
26 | * goes from 1 to 0 instead of 0 to 1).
27 | */
28 | public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
29 | new PathInterpolator(0.8f, 0f, 0.6f, 1f);
30 | public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
31 | public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
32 | public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
33 | public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
34 | public static final Interpolator LINEAR = new LinearInterpolator();
35 | public static final Interpolator ACCELERATE = new AccelerateInterpolator();
36 | public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
37 | public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
38 | public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
39 | public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
40 | public static final Interpolator PANEL_CLOSE_ACCELERATED
41 | = new PathInterpolator(0.3f, 0, 0.5f, 1);
42 | public static final Interpolator BOUNCE = new BounceInterpolator();
43 |
44 | /**
45 | * Interpolator to be used when animating a move based on a click. Pair with enough duration.
46 | */
47 | public static final Interpolator TOUCH_RESPONSE =
48 | new PathInterpolator(0.3f, 0f, 0.1f, 1f);
49 |
50 | /**
51 | * Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t
52 | * goes from 1 to 0 instead of 0 to 1).
53 | */
54 | public static final Interpolator TOUCH_RESPONSE_REVERSE =
55 | new PathInterpolator(0.9f, 0f, 0.7f, 1f);
56 | }
57 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/util/TreeViewLog.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.util;
2 |
3 | import android.util.Log;
4 |
5 | import com.gyso.treeview.BuildConfig;
6 |
7 | /**
8 | * @Author: 怪兽N
9 | * @Time: 2021/5/8 15:10
10 | * @Email: 674149099@qq.com
11 | * @WeChat: guaishouN
12 | * @Describe:
13 | * logger
14 | */
15 | public class TreeViewLog{
16 | private static boolean isDebug = BuildConfig.isDebug;
17 | public static void d(String tag, String msg){
18 | if(isDebug){
19 | Log.d(tag, msg);
20 | }
21 | }
22 | public static void e(String tag, String msg){
23 | if(isDebug){
24 | Log.e(tag, msg);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/library/src/main/java/com/gyso/treeview/util/ViewBox.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview.util;
2 |
3 | import android.graphics.Matrix;
4 | import android.view.View;
5 |
6 | public class ViewBox {
7 |
8 | public int top;
9 | public int left;
10 | public int right;
11 | public int bottom;
12 |
13 | public ViewBox(){
14 |
15 | }
16 | public ViewBox(View view){
17 | this(view.getTop(),view.getLeft(),view.getBottom(),view.getRight());
18 | }
19 |
20 | public ViewBox(int top, int left, int bottom, int right) {
21 | this.top = top;
22 | this.left = left;
23 | this.right = right;
24 | this.bottom = bottom;
25 | }
26 |
27 | public void setValues(ViewBox viewBox) {
28 | this.top = viewBox.top;
29 | this.left = viewBox.left;
30 | this.right = viewBox.right;
31 | this.bottom = viewBox.bottom;
32 | }
33 |
34 | public void setValues(int top, int left,int bottom,int right) {
35 | this.top = top;
36 | this.left = left;
37 | this.bottom = bottom;
38 | this.right = right;
39 | }
40 |
41 | public static ViewBox getViewBox(View view) {
42 | return new ViewBox(view);
43 | }
44 |
45 | /**
46 | *.scaleX(centerM[0])
47 | *.translationX(centerM[2])
48 | *.scaleY(centerM[4])
49 | *.translationY(centerM[5])
50 | *
51 | *float left = v.getLeft()*v.getScaleX()+v.getTranslationX();
52 | *float top = v.getTop()*v.getScaleY()+v.getTranslationY();
53 | *float right = v.getRight()*v.getScaleX()+v.getTranslationX();
54 | *float bottom = v.getBottom()*v.getScaleY()+v.getTranslationY();
55 | */
56 | public ViewBox convert(Matrix matrix) {
57 | float[] fs = new float[9];
58 | matrix.getValues(fs);
59 | float scaleX = fs[Matrix.MSCALE_X];
60 | float scaleY = fs[Matrix.MSCALE_Y];
61 | float translX = fs[Matrix.MTRANS_X];
62 | float translY = fs[Matrix.MTRANS_Y];
63 | float leftI = left*scaleX+translX;
64 | float topI = top*scaleY+translY;
65 | float rightI = right*scaleX+translX;
66 | float bottomI = bottom*scaleY+translY;
67 | return new ViewBox((int)topI,(int)leftI,(int)bottomI,(int)rightI);
68 | }
69 |
70 | public ViewBox invert(Matrix matrix){
71 | float[] fs = new float[9];
72 | matrix.getValues(fs);
73 | float scaleX = fs[Matrix.MSCALE_X];
74 | float scaleY = fs[Matrix.MSCALE_Y];
75 | float translX = fs[Matrix.MTRANS_X];
76 | float translY = fs[Matrix.MTRANS_Y];
77 | float leftI = (left-translX)/scaleX;
78 | float topI = (top-translY)/scaleY;
79 | float rightI = (right-translX)/scaleX;
80 | float bottomI = (bottom-translY)/scaleY;
81 | setValues((int)topI,(int)leftI,(int)rightI,(int)bottomI);
82 | return new ViewBox((int)topI,(int)leftI,(int)bottomI,(int)rightI);
83 | }
84 |
85 | public boolean contain(ViewBox other){
86 | return other!=null &&
87 | top<=other.top &&
88 | left<=other.left &&
89 | right>=other.right &&
90 | bottom>=other.bottom;
91 | }
92 |
93 | public ViewBox multiply(float radio) {
94 | return new ViewBox(
95 | (int)(top * radio),
96 | (int)(left * radio),
97 | (int)(bottom * radio),
98 | (int)(right * radio)
99 | );
100 | }
101 |
102 | public ViewBox add(ViewBox other) {
103 | if (other == null) {
104 | return this;
105 | }
106 | return new ViewBox(
107 | top + other.top,
108 | left + other.left,
109 | bottom + other.bottom,
110 | right + other.right
111 | );
112 | }
113 |
114 | public ViewBox subtract(ViewBox other){
115 | if (other == null) {
116 | return this;
117 | }
118 | return new ViewBox(
119 | top - other.top,
120 | left - other.left,
121 | bottom - other.bottom,
122 | right - other.right
123 | );
124 | }
125 |
126 | /**
127 | * get the box height which added 2*dy
128 | * @return height
129 | */
130 | public int getHeight(){
131 | return bottom-top;
132 | }
133 |
134 | /**
135 | * get the box width which added 2*dy
136 | * @return width
137 | */
138 | public int getWidth(){
139 | return right-left;
140 | }
141 |
142 | public void clear() {
143 | this.top = 0;
144 | this.left = 0;
145 | this.right = 0;
146 | this.bottom = 0;
147 | }
148 |
149 | @Override
150 | public String toString() {
151 | return "{" +
152 | "t:" + top +
153 | " l:" + left +
154 | " b:" + bottom +
155 | " r:" + right +
156 | '}';
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/library/src/main/res/values/values.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | #E1BEE7
16 |
--------------------------------------------------------------------------------
/library/src/test/java/com/gyso/treeview/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.gyso.treeview;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | ## This file must *NOT* be checked into Version Control Systems,
2 | # as it contains information specific to your local configuration.
3 | #
4 | # Location of the SDK. This is only used by Gradle.
5 | # For customization when using a Version Control System, please read the
6 | # header note.
7 | #Tue Mar 22 09:08:11 CST 2022
8 | #sdk.dir=/home/V01.NET/uidq3640/Downloads/android-sdk-linux
9 | sdk.dir=D\:\\programfiles\\sdk
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "29.0.3"
6 |
7 | defaultConfig {
8 | applicationId "com.gyso.gysotreeviewapplication"
9 | minSdkVersion 21
10 | targetSdkVersion 30
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | buildFeatures {
25 | dataBinding = true
26 | viewBinding = true
27 | }
28 | compileOptions {
29 | sourceCompatibility JavaVersion.VERSION_1_8
30 | targetCompatibility JavaVersion.VERSION_1_8
31 | }
32 | }
33 |
34 | dependencies {
35 | implementation fileTree(dir: "libs", include: ["*.jar"])
36 | implementation 'androidx.appcompat:appcompat:1.2.0'
37 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
38 | implementation "com.google.android.material:material:1.2.1"
39 | implementation project(path: ':library')
40 | testImplementation 'junit:junit:4.12'
41 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
42 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
43 | }
--------------------------------------------------------------------------------
/sample/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/sample/src/androidTest/java/com/gyso/gysotreeviewapplication/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.gyso.gysotreeviewapplication;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 | assertEquals("com.gyso.gysotreeviewapplication", appContext.getPackageName());
25 | }
26 | }
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/gyso/gysotreeviewapplication/base/Animal.java:
--------------------------------------------------------------------------------
1 | package com.gyso.gysotreeviewapplication.base;
2 |
3 | /**
4 | * @Author: 怪兽N
5 | * @Time: 2021/5/7 19:12
6 | * @Email: 674149099@qq.com
7 | * @WeChat: guaishouN
8 | * @Describe:
9 | * node bean
10 | */
11 | public class Animal {
12 | public int headId;
13 | public String name;
14 | public Animal(int headId, String name) {
15 | this.headId = headId;
16 | this.name = name;
17 | }
18 |
19 | @Override
20 | public String toString() {
21 | return "Animal["+name+"]";
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/gyso/gysotreeviewapplication/base/AnimalTreeViewAdapter.java:
--------------------------------------------------------------------------------
1 | package com.gyso.gysotreeviewapplication.base;
2 |
3 | import android.graphics.Color;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ImageView;
8 | import android.widget.TextView;
9 |
10 | import androidx.annotation.NonNull;
11 |
12 | import com.gyso.gysotreeviewapplication.R;
13 | import com.gyso.gysotreeviewapplication.databinding.NodeBaseLayoutBinding;
14 | import com.gyso.treeview.adapter.DrawInfo;
15 | import com.gyso.treeview.adapter.TreeViewAdapter;
16 | import com.gyso.treeview.adapter.TreeViewHolder;
17 | import com.gyso.treeview.line.BaseLine;
18 | import com.gyso.treeview.line.DashLine;
19 | import com.gyso.treeview.model.NodeModel;
20 |
21 | /**
22 | * @Author: 怪兽N
23 | * @Time: 2021/4/23 16:48
24 | * @Email: 674149099@qq.com
25 | * @WeChat: guaishouN
26 | * @Describe:
27 | * Tree View Adapter for node data to view
28 | */
29 | public class AnimalTreeViewAdapter extends TreeViewAdapter {
30 | private DashLine dashLine = new DashLine(Color.parseColor("#F06292"),6);
31 | private OnItemClickListener listener;
32 |
33 | public void setOnItemListener(OnItemClickListener listener) {
34 | this.listener = listener;
35 | }
36 |
37 | @Override
38 | public TreeViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, NodeModel node) {
39 | NodeBaseLayoutBinding nodeBinding = NodeBaseLayoutBinding.inflate(LayoutInflater.from(viewGroup.getContext()),viewGroup,false);
40 | return new TreeViewHolder<>(nodeBinding.getRoot(),node);
41 | }
42 |
43 | @Override
44 | public void onBindViewHolder(@NonNull TreeViewHolder holder) {
45 | //todo get view and node from holder, and then show by you
46 | View itemView = holder.getView();
47 | NodeModel node = holder.getNode();
48 | TextView nameView = itemView.findViewById(R.id.name);
49 | ImageView headView = itemView.findViewById(R.id.portrait);
50 | final Animal animal = node.value;
51 | nameView.setText(animal.name);
52 | headView.setImageResource(animal.headId);
53 | headView.setOnClickListener(v -> {
54 | if(listener!=null){
55 | listener.onItemClick(v,node);
56 | }
57 | });
58 | }
59 |
60 | @Override
61 | public BaseLine onDrawLine(DrawInfo drawInfo) {
62 | // TODO If you return an BaseLine, line will be draw by the return one instead of TreeViewLayoutManager's
63 | // TreeViewHolder> toHolder = drawInfo.getToHolder();
64 | // NodeModel> node = toHolder.getNode();
65 | // Object value = node.getValue();
66 | // if(value instanceof Animal){
67 | // Animal animal = (Animal) value;
68 | // if("sub4".compareToIgnoreCase(animal.name)<=0){
69 | // return dashLine;
70 | // }
71 | // }
72 | return null;
73 | }
74 |
75 | public interface OnItemClickListener{
76 | void onItemClick(View item, NodeModel node);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/card_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/drag_mode_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_07.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_16.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/ic_launcher_tree.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/percent_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
20 |
21 |
26 |
40 |
41 |
55 |
56 |
70 |
71 |
85 |
86 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/node_base_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
18 |
29 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guaishouN/android-thinkmap-treeview/285c2f3e2163abbe5644b080286b8030ac06409a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 | #EEE9E9
7 | #055287
8 | #E57373
9 | #F06292
10 | #F1286C
11 | #80055287
12 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | GysoTreeViewApplication
3 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
13 |
--------------------------------------------------------------------------------
/sample/src/test/java/com/gyso/gysotreeviewapplication/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.gyso.gysotreeviewapplication;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':library'
2 | include ':sample'
3 | rootProject.name = "GysoTreeViewApplication"
--------------------------------------------------------------------------------