1# TreeView
2
3
4树视图作为一种分层显示的列表,适合显示嵌套结构。拥有父列表项和子列表项,可展开或折叠。子列表项支持选中一项或多项。
5
6
7用于效率型应用,如备忘录、电子邮件、图库中的侧边导航栏中。
8
9
10> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
11> 该组件从API Version 10开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
12
13
14## 导入模块
15
16```
17import { TreeView } from "@ohos.arkui.advanced.TreeView"
18```
19
20
21## 子组件
22
2324
25
26## 接口
27
28TreeView({ treeController: TreeController })
29
30**装饰器类型:**\@Component
31
32**系统能力:** SystemCapability.ArkUI.ArkUI.Full
33
34
35**参数:**
36
37
38| 参数名 | 参数类型 | 必填 | 参数描述 |
39| -------- | -------- | -------- | -------- |
40| treeController | [TreeController](#treecontroller) | 是 | 树视图节点信息。 |
41
42
43## TreeController
44
45树视图组件的控制器,可以将此对象绑定至树视图组件,然后通过它控制树的节点信息,同一个控制器不可以控制多个树视图组件。
46
47
48### 导入对象
49
50```
51treeController: TreeController = new TreeController()
52```
53
54
55### addNode
56
57
58addNode(nodeParam?: NodeParam): void
59
60
61点击某个节点后,调用该方法可以触发新增孩子节点
62
63
64**参数:**
65
66
67| 参数名 | 参数类型 | 必填 | 参数描述 |
68| -------- | -------- | -------- | -------- |
69| nodeParam | [NodeParam](#nodeparam) | 否 | 节点信息。 |
70
71
72### removeNode
73
74removeNode(): void
75
76点击某个节点后,调用该方法可以触发删除该节点
77
78
79### modifyNode
80
81
82modifyNode(): void
83
84
85点击某个节点后,调用该方法可以触发修改该节点
86
87
88### buildDone
89
90buildDone(): void
91
92建立树视图。节点增加完毕后,必须调用该方法,触发树信息的保存
93
94
95### refreshNode
96
97refreshNode(parentId: number, parentSubTitle: ResourceStr, currentSubtitle: ResourceStr): void
98
99**参数:**
100
101| 参数名 | 参数类型 | 必填 | 参数描述 |
102| -------- | -------- | -------- | -------- |
103| parentId | number | 是 | 父节点Id。 |
104| parentSubTitle | ResourceStr | 是 | 父节点副文本 |
105| currentSubtitle | ResourceStr | 是 | 当前节点副文本 |
106
107
108## NodeParam
109
110| 名称 | 值 | 是否必填 | 描述 |
111| -------- | -------- | -------- | -------- |
112| parentNodeId | number | 否 | 父亲节点 |
113| currentNodeId | number | 否 | 当前孩子节点 |
114| isFolder | boolean | 否 | 是否是目录 |
115| icon | ResourceStr | 否 | 图标 |
116| selectedIcon | ResourceStr | 否 | 选中图标 |
117| editIcon | ResourceStr | 否 | 编辑图标 |
118| primaryTitle | ResourceStr | 否 | 主标题 |
119| secondaryTitle | ResourceStr | 否 | 副标题 |
120| container | () => void | 否 | 绑定在节点上的子组件,子组件由[@Builder](https://docs.openharmony.cn/pages/v3.1/en/application-dev/ui/ts-component-based-builder.md/)修饰 |
121
122
123## TreeListenerManager对象说明
124
125树视图组件的监听器,可以将此对象绑定至树视图组件,然后通过它监听树的节点的变化,同一个监听器不可以控制多个树视图组件。
126
127
128### 导入对象
129
130```
131import { TreeListenerManager } from '@ohos.arkui.advanced.TreeView'
132```
133
134
135### getInstance
136
137getInstance(): TreeListenerManager
138
139获取监听管理器单例对象
140
141
142### getTreeListener()
143
144getTreeListener(): TreeListener
145
146获取监听器
147
148
149## TreeListener对象说明
150
151树视图组件的监听器,可以将此对象绑定至树视图组件,然后通过它监听树的节点的变化,同一个监听器不可以控制多个树视图组件。
152
153
154### 导入对象
155
156```
157treeListener: TreeListener = TreeListenerManager.getInstance().getTreeListener()
158```
159
160
161### on
162
163on(type: TreeListenType, callback: (callbackParam: CallbackParam) => void): void;
164
165注册监听
166
167**参数:**
168
169| 参数名 | 参数类型 | 必填 | 参数描述 |
170| -------- | -------- | -------- | -------- |
171| type | [TreeListenType](#treelistentype) | 是 | 监听类型 |
172| nodeParam | [NodeParam](#nodeparam) | 是 | 节点信息。 |
173
174
175### once
176
177once(type: TreeListenType, callback: (callbackParam: CallbackParam) => void): void;
178
179注册一次监听
180
181**参数:**
182
183| 参数名 | 参数类型 | 必填 | 参数描述 |
184| -------- | -------- | -------- | -------- |
185| type | [TreeListenType](#treelistentype) | 是 | 监听类型 |
186| nodeParam | [NodeParam](#nodeparam) | 是 | 节点信息。 |
187
188
189### off
190
191
192off(type: TreeListenType, callback?: (callbackParam: CallbackParam) => void): void;
193
194
195取消监听
196
197
198**参数:**
199
200
201| 参数名 | 参数类型 | 必填 | 参数描述 |
202| -------- | -------- | -------- | -------- |
203| type | [TreeListenType](#treelistentype) | 是 | 监听类型 |
204| nodeParam | [NodeParam](#nodeparam) | 是 | 节点信息。 |
205
206
207## TreeListenType
208
209| 名称 | 描述 |
210| -------- | -------- |
211| NODE_CLICK | 监听节点点击事件 |
212| NODE_ADD | 监听节点增加事件 |
213| NODE_DELETE | 监听节点删除事件 |
214| NODE_MODIFY | 监听节点修改事件 |
215| NODE_MOVE | 监听节点移动事件 |
216
217
218## CallbackParam
219
220| 名称 | 值 | 是否必填 | 描述 |
221| -------- | -------- | -------- | -------- |
222| currentNodeId | number | 是 | 当前孩子节点 |
223| parentNodeId | number | 否 | 父亲节点表 |
224| childIndex: number | number | 否 | 孩子索引 |
225
226
227## 示例
228
229```
230import { TreeController, TreeListener, TreeListenerManager, TreeListenType, NodeParam, TreeView, CallbackParam } from '@ohos.arkui.advanced.TreeView'
231
232@Entry
233@Component
234struct TreeViewDemo {
235  private listTreeNode: TreeController = new TreeController();
236  private appEventBus: TreeListener = TreeListenerManager.getInstance().getTreeListener();
237  @State clickNodeId: number = 0;
238  @State numbers: string[] = ['one', 'two', 'three', 'four', 'five', 'six'];
239
240  aboutToDisappear(): void {
241    this.appEventBus.off(TreeListenType.NODE_CLICK, null);
242    this.appEventBus.off(TreeListenType.NODE_ADD, null);
243    this.appEventBus.off(TreeListenType.NODE_DELETE, null);
244  }
245
246  @Builder menuBuilder0() {
247    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
248      Text('删除').fontSize(16).width(100).height(30).textAlign(TextAlign.Center)
249        .onClick((event: ClickEvent) => {
250          this.listTreeNode.removeNode();
251        })
252    }.width(100).border({width: 1, color: 0x80808a, radius: '16dp'})
253  }
254
255  @Builder menuBuilder1() {
256    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
257      Text('新增').fontSize(16).width(100).height(30).textAlign(TextAlign.Center)
258        .onClick((event: ClickEvent) => {
259          this.listTreeNode.addNode();
260        })
261      Divider()
262      Text('删除').fontSize(16).width(100).height(30).textAlign(TextAlign.Center)
263        .onClick((event: ClickEvent) => {
264          this.listTreeNode.removeNode();
265        })
266      Divider()
267      Text('重命名').fontSize(16).width(100).height(30).textAlign(TextAlign.Center)
268        .onClick((event: ClickEvent) => {
269          this.listTreeNode.modifyNode();
270        })
271    }.width(100).border({width: 1, color: 0x80808a, radius: '16dp'})
272  }
273
274  aboutToAppear(): void {
275    this.appEventBus.on(TreeListenType.NODE_MOVE, (callbackParam: CallbackParam) => {
276    })
277    this.appEventBus.on(TreeListenType.NODE_CLICK, (callbackParam: CallbackParam) => {
278    })
279
280    let normalResource: Resource = $r('app.media.ic_public_collect_normal');
281    let selectedResource: Resource = $r('app.media.ic_public_collect_selected');
282    let editResource: Resource = $r('app.media.ic_public_collect_edit');
283    let nodeParam: NodeParam = { parentNodeId:-1, currentNodeId: 1, isFolder: true, icon: normalResource, selectedIcon: selectedResource,
284      editIcon: editResource, primaryTitle: "目录1验证悬浮框自适应效果是否OK",
285      menu: this.menuBuilder1.bind(this), secondaryTitle: "6" };
286    this.listTreeNode
287      .addNode(nodeParam)
288      .addNode({parentNodeId:1, currentNodeId: 2, isFolder: false, primaryTitle: "项目1_1" })
289      .addNode({ parentNodeId:-1, currentNodeId: 7, isFolder: true, primaryTitle: "目录2", menu: this.menuBuilder1.bind(this) })
290      .addNode({ parentNodeId:-1, currentNodeId: 23, isFolder: true, icon: normalResource, selectedIcon: selectedResource,
291        editIcon: editResource, primaryTitle: "目录3", menu: this.menuBuilder1.bind(this) })
292      .addNode({ parentNodeId:-1, currentNodeId: 24, isFolder: false, primaryTitle: "项目4", menu: this.menuBuilder1.bind(this) })
293      .addNode({ parentNodeId:-1, currentNodeId: 31, isFolder: true, icon: normalResource, selectedIcon: selectedResource,
294        editIcon: editResource, primaryTitle: "目录5", menu: this.menuBuilder1.bind(this), secondaryTitle: "0" })
295      .addNode({ parentNodeId:-1, currentNodeId: 32, isFolder: true, icon: normalResource, selectedIcon: selectedResource,
296        editIcon: editResource, primaryTitle: "目录6", menu: this.menuBuilder1.bind(this), secondaryTitle: "0" })
297      .addNode({ parentNodeId:32, currentNodeId: 35, isFolder: true, icon: normalResource, selectedIcon: selectedResource,
298        editIcon: editResource, primaryTitle: "目录6-1", menu: this.menuBuilder1.bind(this), secondaryTitle: "0" })
299      .addNode({ parentNodeId:-1, currentNodeId: 33, isFolder: true, icon: normalResource, selectedIcon: selectedResource,
300        editIcon: editResource, primaryTitle: "目录7", menu: this.menuBuilder1.bind(this), secondaryTitle: "0" })
301      .addNode({ parentNodeId:33, currentNodeId: 34, isFolder: false, primaryTitle: "项目8" })
302      .addNode({ parentNodeId:-1, currentNodeId: 36, isFolder: false, primaryTitle: "项目9" })
303      .buildDone();
304  }
305
306  @Builder subfieldPopup(item: any) {
307    Column() {
308      Text(item)
309        .width('50%').height(60).fontSize(16).borderRadius(10)
310        .textAlign(TextAlign.Center).backgroundColor(Color.Yellow)
311    }
312  }
313
314  build() {
315    SideBarContainer(SideBarContainerType.Embed)
316    {
317      TreeView({ treeController: this.listTreeNode })
318      Row() {
319        Divider().vertical(true).strokeWidth(2).color(0x000000).lineCap(LineCapStyle.Round)
320        Column() {
321          Row() {
322            Text('ClickNodeId=' + this.clickNodeId).fontSize('16fp')
323            Button('Add', { type: ButtonType.Normal, stateEffect: true })
324              .borderRadius(8).backgroundColor(0x317aff).width(90)
325              .onClick((event: ClickEvent) => {
326                this.listTreeNode.addNode();
327              })
328            Button('Modify', { type: ButtonType.Normal, stateEffect: true })
329              .borderRadius(8).backgroundColor(0x317aff).width(90)
330              .onClick((event: ClickEvent) => {
331                this.listTreeNode.modifyNode();
332              })
333            Button('Remove', { type: ButtonType.Normal, stateEffect: true })
334              .borderRadius(8).backgroundColor(0x317aff).width(120)
335              .onClick((event: ClickEvent) => {
336                this.listTreeNode.removeNode();
337              })
338          }
339
340          /* drag item. */
341          List({ space: 10, initialIndex: 0 }) {
342            ForEach(this.numbers, (item) => {
343              ListItem() {
344                Text('' + item)
345                  .width('100%').height(38).fontSize(16).borderRadius(10)
346                  .textAlign(TextAlign.Center).backgroundColor(0xAFEEEE)
347              }
348              .onSelect((isSelected: boolean) => {
349                console.log('listItem selected')
350              })
351              .onDragStart(() => {
352                this.listTreeNode.refreshNode(1,'选择节点','目标节点')
353                return this.subfieldPopup(item)
354              })
355            }, item => item)
356          }
357          .editMode(true)
358          .multiSelectable(true)
359          .onDrop((event: DragEvent, extraParams: string) => {
360            console.log('outer onDrop')
361          })
362
363        }.height('100%').width('70%').alignItems(HorizontalAlign.Start)
364      }
365    }
366    .focusable(true)
367    .sideBarWidth(240)
368    .minSideBarWidth(0)
369    .maxSideBarWidth(900)
370    .showControlButton(false)
371    .showSideBar(true)
372    .onChange((value: boolean) => {
373      console.info('status:' + value);
374    })
375  }
376}
377```
378
379![zh-cn_image_0000001664822257](figures/zh-cn_image_0000001664822257.png)
380