1# 折叠展开动效
2## 场景介绍
3由于目前移动端需要展示的内容越来越多,但是移动端的空间弥足珍贵,在有限的空间内不可能罗列展示全部种类内容,因此折叠/展开功能就可以解决当前问题,本文就介绍下如何使用ArkTS来实现折叠展开动效。
4
5## 效果呈现
6折叠展开动效定义:点击展开按钮,下拉动画展示内容,点击折叠按钮,折叠动画折叠内容。
7本例最终效果如下:
8![collapse_and_expand](figures/collapse_and_expand.gif)
9
10## 运行环境
11本例基于以下环境开发,开发者也可以基于其它适配的版本进行开发:
12- IDE: DevEco Studio 3.1 Release
13- SDK: Ohos_sdk_public 3.2.12.5(API Version 9 Release)
14## 实现思路
15创建折叠时的文本组件,根据List组件中的groupcollapse和groupexpand事件自定义一个CollapseAndExpand组件,父组件通过维护flag和onFlagChange来控制折叠/展开的动效,设置动效所需的参数,添加逻辑来展示展开后的文本。
16
17## 开发步骤
181. 创建自定义接口IRowItem。
19  具体代码如下:
20
21    ```ts
22    interface IRowItem {
23        id?: number;
24        title?: string;
25        name1?: string;
26        name2?: string;
27        name3?: string;
28        flag?: boolean;
29        type?: string;
30        onFlagChange?: () => void;
31    }
32    ```
33
342. 创建自定义组件CollapseAndExpandDemo,根据自定义接口IRowItem添加内容,创建UI展示文本。
35具体代码如下:
36
37    ```ts
38    @Entry
39    @Component{
40        ...
41        build() {
42            Column() {
43                Row() {
44                    Image($r("app.media.ic_public_back"))
45                    .width(20)
46                    .height(20)
47                    Text('周免英雄')
48                    .fontSize(18)
49                    .fontWeight(FontWeight.Bold)
50                    .margin({ left: 10 })
51                }
52                .width('100%')
53                .margin({ bottom: 30 })
54
55                Column() {
56                    RowItem({ props: { title: 'AAAAA', name1: 'BBBBB', name2: 'CCCCC', name3: '武器大师' } })
57                    // 文本折叠时,type为DOWN
58                    RowItem({ props: { name1: 'DDDDD', name2: 'EEEEE', name3: 'FFFFF', type: 'DOWN', onFlagChange: this.onFlagChange } })
59
60                    //被折叠的文本内容
61                    ...
62
63                    RowItem({ props: { title: '商城', name1: '免费', name2: '特价', name3: 'VIP' } })
64                    RowItem({ props: { title: '分类', name1: '按职业', name2: '按位置', name3: '按城市' } })
65                }
66                .width('100%')
67    }
68    ```
69
70    被折叠文本信息。
71  具体代码如下:
72
73    ```ts
74    CollapseAndExpand({
75        items: [
76            { id: 0, name1: 'GGGGG', name2: 'HHHHH', name3: 'JJJJJ' },
77            { id: 1, name1: 'KKKKK', name2: 'LLLLL', name3: 'MMMMM' },
78            { id: 2, name1: 'NNNNN', name2: 'OOOOO', name3: 'PPPPP' },
79            // 文本展开时,type为UP
80            { id: 3, name1: 'QQQQQ', name2: 'RRRRR', name3: 'SSSSS', type: 'UP', onFlagChange: this.onFlagChange }
81        ],
82    })
83    ```
84
85
863. 将步骤2创建的文本进行渲染。
87具体如下:
88
89    ```ts
90    build() {
91        Flex() {
92          Text(this.props.title)
93            .fontSize(14)
94            .fontWeight(FontWeight.Bold)
95            .layoutWeight(1)
96            .fontColor(Color.Red)
97            .margin({ right: 10 })
98          Flex({ alignItems: ItemAlign.Center }) {
99            Text(this.props.name1).fontSize(14).margin({ right: 10 })
100            Text(this.props.name2).fontSize(14).margin({ right: 10 })
101            Text(this.props.name3).fontSize(14).margin({ right: 10 })
102            ...
103          }
104        }
105      }
106    ```
1074. 创建自定义组件CollapseAndExpand。
108根据自定义组件说明动效,@Provide负责数据更新,并且触发渲染;@Consume在感知数据更新后,重新渲染。
109具体代码如下:
110
111    ```ts
112    @Entry
113    @Component
114    struct CollapseAndExpandDemo {
115      @Provide("flag") flag: boolean = false
116      private onFlagChange = () => {
117        animateTo({
118            duration: 650,
119            curve: Curve.Smooth
120        }, () => {
121            this.flag = !this.flag;
122        })
123      }
124
125      ...
126
127    @Component
128    struct CollapseAndExpand {
129        private items: IRowItem[] = [];
130        @Consume("flag") flag: boolean;
131
132        build() {
133            Column() {
134                ForEach(this.items, (item: IRowItem) => {
135                    RowItem({ props: item })
136                }, (item: IRowItem) => item.id.toString())
137            }
138            .width('100%')
139            .clip(true)
140            .height(this.flag ? 130 : 0)
141        }
142    }
143    ```
1445. 根据步骤4最终的flag以及props的type值,判断折叠展开的效果实现。
145    具体代码如下:
146
147    ```ts
148    build() {
149        ...
150        // 当文本折叠(flag为false且type为down)时,展示展开按钮
151        // 当文本展开(flag为true且type为up)时,展示折叠按钮
152        if (!this.flag && this.props.type === 'DOWN' || this.flag && this.props.type === 'UP') {
153        Image($r("app.media.icon"))
154            .width(16)
155            .height(16)
156            .objectFit(ImageFit.Contain)
157            .rotate({ angle: !this.flag && this.props.type === 'DOWN' ? 0 : 180 })
158            // 点击按钮后旋转180°,展示折叠按钮
159            .onClick(() =>
160            this.props.onFlagChange()
161            )
162            .transition({ type: TransitionType.All, opacity: 0 })
163        }
164    }
165    ```
166
167
168## 完整代码
169示例代码如下:
170```ts
171interface IRowItem {
172    id?: number;
173    title?: string;
174    name1?: string;
175    name2?: string;
176    name3?: string;
177    flag?: boolean;
178    type?: string;
179    onFlagChange?: () => void;
180}
181
182@Entry
183@Component
184struct CollapseAndExpandDemo {
185    @Provide("flag") flag: boolean = false
186    private onFlagChange = () => {
187        animateTo({
188            duration: 650,
189            curve: Curve.Smooth
190            }, () => {
191                this.flag = !this.flag;
192                })
193    }
194
195    build() {
196        Column() {
197            Row() {
198                Image($r("app.media.ic_public_back")).width(20).height(20)
199                Text('周免英雄')
200                .fontSize(18)
201                .fontWeight(FontWeight.Bold)
202                .margin({ left: 10 })
203            }
204            .width('100%')
205            .margin({ bottom: 30 })
206
207            Column() {
208                RowItem({
209                    props: { title: '英雄', name1: 'AAAAA', name2: 'BBBBB', name3: 'CCCCC' } })
210                RowItem({
211                    props: {
212                        name1: 'DDDDD',
213                        name2: 'EEEEE',
214                        name3: 'FFFFF',
215                        // 文本折叠时,type为DOWN
216                        type: 'DOWN',
217                        onFlagChange: this.onFlagChange
218                    }
219                })
220                // 直接调用折叠展开组件
221                CollapseAndExpand({
222                    items: [
223                        { id: 0, name1: 'GGGGG', name2: 'HHHHH', name3: 'JJJJJ' },
224                        { id: 1, name1: 'KKKKK', name2: 'LLLLL', name3: 'MMMMM' },
225                        { id: 2, name1: 'NNNNN', name2: 'OOOOO', name3: 'PPPPP' },
226                        { id: 3,
227                        name1: 'QQQQQ',
228                        name2: 'RRRRR',
229                        name3: 'SSSSS',
230                        // 文本折叠时,type为UP
231                        type: 'UP',
232                        onFlagChange: this.onFlagChange }
233                    ],
234                })
235
236                RowItem({ props: { title: '商城', name1: '免费', name2: '特价', name3: 'VIP' } })
237                RowItem({ props: { title: '分类', name1: '按职业', name2: '按位置', name3: '按城市' } })
238            }
239            .width('100%')
240
241        }
242        .height('100%')
243        .padding({ top: 30, right: 30, left: 30 })
244  }
245}
246
247@Component
248struct RowItem {
249    private props: IRowItem;
250    @Consume("flag") flag: boolean
251
252    build() {
253        Flex() {
254            Text(this.props.title)
255            .fontSize(14)
256            .fontWeight(FontWeight.Bold)
257            .layoutWeight(1)
258            .fontColor(Color.Red)
259            .margin({ right: 10 })
260            Flex({ alignItems: ItemAlign.Center }) {
261                Text(this.props.name1).fontSize(14).margin({ right: 10 })
262                Text(this.props.name2).fontSize(14).margin({ right: 10 })
263                Text(this.props.name3).fontSize(14).margin({ right: 10 })
264
265                // 当文本折叠(flag为false且type为down)时,展示展开按钮
266                // 当文本展开(flag为true且type为up)时,展示折叠按钮
267                if (!this.flag && this.props.type === 'DOWN' || this.flag && this.props.type === 'UP') {
268                    Image($r("app.media.ic_public_arrow_down_0"))
269                    .width(16)
270                    .height(16)
271                    .objectFit(ImageFit.Contain)
272                    .rotate({ angle: !this.flag && this.props.type === 'DOWN' ? 0 : 180 })
273                    // 点击展开按钮后旋转180°,展示折叠按钮
274                    .onClick(() => this.props.onFlagChange())
275                    .transition({ type: TransitionType.All, opacity: 0 })
276                }
277            }
278            .layoutWeight(3)
279        }
280        .width('100%')
281        .height(16)
282        .margin({ top: 15 })
283    }
284}
285
286@Component
287struct CollapseAndExpand {
288    private items: IRowItem[] = [];
289    @Consume("flag") flag: boolean;
290
291    build() {
292        Column() {
293            ForEach(this.items, (item: IRowItem) => {
294                RowItem({ props: item })
295            }, (item: IRowItem) => item.id.toString())
296        }
297        .width('100%')
298        .clip(true)
299        .height(this.flag ? 130 : 0)
300    }
301}
302```
303## 参考
304[显示动画](../application-dev/reference/apis-arkui/arkui-ts/ts-explicit-animation.md)
305
306[@Provide和@Consume:与后代组件双向同步](../application-dev/quick-start/arkts-provide-and-consume.md)
307
308[list开发指导](../application-dev/ui/ui-js-components-list.md)
309
310
311
312
313
314