• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 设置浮层(OverlayManager)
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @liyi0309-->
5<!--Designer: @liyi0309-->
6<!--Tester: @lxl007-->
7<!--Adviser: @HelloCrease-->
8
9浮层(OverlayManager)用于在页面(Page)之上展示自定义的UI内容,位于Dialog、Popup、Menu、BindSheet、BindContentCover和Toast等组件之下,展示范围为当前窗口的安全区内,适用于常驻悬浮等场景。
10
11![image](figures/overlayManager.png)
12
13可以通过使用[UIContext](../reference/apis-arkui/arkts-apis-uicontext-uicontext.md)中的[getOverlayManager](../reference/apis-arkui/arkts-apis-uicontext-uicontext.md#getoverlaymanager12)方法获取当前UI上下文关联的[OverlayManager](../reference/apis-arkui/arkts-apis-uicontext-overlaymanager.md)对象,再通过该对象调用对应方法。
14
15## 规格约束
16
17* OverlayManager上节点的层级在Page页面层级之上,在Dialog、Popup、Menu、BindSheet、BindContentCover和Toast等组件之下。
18* OverlayManager添加的节点显示和消失时没有默认动画。
19* OverlayManager上节点安全区域内外的绘制方式与Page一致,键盘避让方式与Page一致。
20* 推荐使用AppStorage存储与OverlayManager相关的属性,以避免页面切换时属性值变化导致业务错误。
21* 当使用API version 19以下版本时,OverlayManager不支持侧滑(左滑/右滑)关闭,需在[onBackPress](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#onbackpress)中添加OverlayManager关闭的逻辑。API 19及以上版本可通过配置[OverlayManagerOptions](../reference/apis-arkui/arkts-apis-uicontext-i.md#overlaymanageroptions15)中的enableBackPressedEvent属性设置OverlayManager是否响应侧滑手势。
22* OverlayManager中的事件机制优先被WrappedBuilder装饰的组件接收。若需实现浮层底部接收事件,可通过设置hitTestBehavior为HitTestMode.Transparent将事件传递至底层。
23
24## 设置浮层
25
26在OverlayManager上[新增指定节点(addComponentContent)](../reference/apis-arkui/arkts-apis-uicontext-overlaymanager.md#addcomponentcontent12)、[删除指定节点(removeComponentContent)](../reference/apis-arkui/arkts-apis-uicontext-overlaymanager.md#removecomponentcontent12)、[显示所有节点(showAllComponentContents)](../reference/apis-arkui/arkts-apis-uicontext-overlaymanager.md#showallcomponentcontents12)和[隐藏所有节点(hideAllComponentContents)](../reference/apis-arkui/arkts-apis-uicontext-overlaymanager.md#hideallcomponentcontents12)。
27
28```ts
29import { ComponentContent, OverlayManager } from '@kit.ArkUI';
30
31class Params {
32  text: string = "";
33  offset: Position;
34
35  constructor(text: string, offset: Position) {
36    this.text = text;
37    this.offset = offset;
38  }
39}
40
41@Builder
42function builderText(params: Params) {
43  Column() {
44    Text(params.text)
45      .fontSize(30)
46      .fontWeight(FontWeight.Bold)
47  }.offset(params.offset)
48}
49
50function initOverlayNode(uiContext: UIContext): OverlayManager {
51  uiContext.setOverlayManagerOptions({
52    enableBackPressedEvent: true
53  });
54  return uiContext.getOverlayManager();
55}
56
57@Entry
58@Component
59struct OverlayExample {
60  @State message: string = 'ComponentContent';
61  private uiContext: UIContext = this.getUIContext();
62  private overlayNode: OverlayManager = this.uiContext.getOverlayManager();
63  @StorageLink('contentArray') contentArray: ComponentContent<Params>[] = [];
64  @StorageLink('componentContentIndex') componentContentIndex: number = 0;
65  @StorageLink('arrayIndex') arrayIndex: number = 0;
66  @StorageLink("componentOffset") componentOffset: Position = { x: 0, y: 30 };
67
68  build() {
69    Column({ space: 10 }) {
70      Button("递增componentContentIndex: " + this.componentContentIndex).onClick(() => {
71        ++this.componentContentIndex;
72      })
73      Button("递减componentContentIndex: " + this.componentContentIndex).onClick(() => {
74        --this.componentContentIndex;
75      })
76      Button("增加ComponentContent" + this.contentArray.length).onClick(() => {
77        let componentContent = new ComponentContent(
78          this.uiContext, wrapBuilder<[Params]>(builderText),
79          new Params(this.message + (this.contentArray.length), this.componentOffset)
80        );
81        this.contentArray.push(componentContent);
82        this.overlayNode.addComponentContent(componentContent, this.componentContentIndex);
83      })
84      Button("递增arrayIndex: " + this.arrayIndex).onClick(() => {
85        ++this.arrayIndex;
86      })
87      Button("递减arrayIndex: " + this.arrayIndex).onClick(() => {
88        --this.arrayIndex;
89      })
90      Button("删除ComponentContent" + this.arrayIndex).onClick(() => {
91        if (this.arrayIndex >= 0 && this.arrayIndex < this.contentArray.length) {
92          let componentContent = this.contentArray.splice(this.arrayIndex, 1);
93          this.overlayNode.removeComponentContent(componentContent.pop());
94        } else {
95          console.info("arrayIndex有误");
96        }
97      })
98      Button("显示ComponentContent" + this.arrayIndex).onClick(() => {
99        if (this.arrayIndex >= 0 && this.arrayIndex < this.contentArray.length) {
100          let componentContent = this.contentArray[this.arrayIndex];
101          this.overlayNode.showComponentContent(componentContent);
102        } else {
103          console.info("arrayIndex有误");
104        }
105      })
106      Button("隐藏ComponentContent" + this.arrayIndex).onClick(() => {
107        if (this.arrayIndex >= 0 && this.arrayIndex < this.contentArray.length) {
108          let componentContent = this.contentArray[this.arrayIndex];
109          this.overlayNode.hideComponentContent(componentContent);
110        } else {
111          console.info("arrayIndex有误");
112        }
113      })
114      Button("显示所有ComponentContent").onClick(() => {
115        this.overlayNode.showAllComponentContents();
116      })
117      Button("隐藏所有ComponentContent").onClick(() => {
118        this.overlayNode.hideAllComponentContents();
119      })
120
121      Button("跳转页面").onClick(() => {
122        this.getUIContext().getRouter().pushUrl({
123          url: 'pages/Second'
124        })
125      })
126    }
127    .width('100%')
128    .height('100%')
129  }
130}
131```
132![overlayManager-demo1](figures/overlaymanager-demo_1.gif)
133
134显示一个始终在屏幕左侧的悬浮球,点击可以弹出alertDialog弹窗。
135
136```ts
137import { ComponentContent, OverlayManager } from '@kit.ArkUI';
138
139class Params {
140  context: UIContext;
141  offset: Position;
142  constructor(context: UIContext, offset: Position) {
143    this.context = context;
144    this.offset = offset;
145  }
146}
147@Builder
148function builderOverlay(params: Params) {
149  Column() {
150    Stack(){
151    }.width(50).height(50).backgroundColor(Color.Yellow).position(params.offset).borderRadius(50)
152    .onClick(() => {
153      params.context.showAlertDialog(
154        {
155          title: 'title',
156          message: 'Text',
157          autoCancel: true,
158          alignment: DialogAlignment.Center,
159          gridCount: 3,
160          confirm: {
161            value: 'Button',
162            action: () => {}
163          },
164          cancel: () => {}
165        }
166      )
167    })
168  }.focusable(false).width('100%').height('100%').hitTestBehavior(HitTestMode.Transparent)
169}
170
171@Entry
172@Component
173struct OverlayExample {
174  @State message: string = 'ComponentContent';
175  private uiContext: UIContext = this.getUIContext();
176  private overlayNode: OverlayManager = this.uiContext.getOverlayManager();
177  private overlayContent:ComponentContent<Params>[] = [];
178  controller: TextInputController = new TextInputController();
179
180  aboutToAppear(): void {
181    let uiContext = this.getUIContext();
182    let componentContent = new ComponentContent(
183      this.uiContext, wrapBuilder<[Params]>(builderOverlay),
184      new Params(uiContext, {x:0, y: 100})
185    );
186    this.overlayNode.addComponentContent(componentContent, 0);
187    this.overlayContent.push(componentContent);
188  }
189
190  aboutToDisappear(): void {
191    let componentContent = this.overlayContent.pop();
192    this.overlayNode.removeComponentContent(componentContent);
193  }
194
195  build() {
196    Column() {
197
198    }
199    .width('100%')
200    .height('100%')
201  }
202}
203
204```
205![overlayManager-demo2](figures/overlaymanager-demo_2.gif)
206
207从API version 18开始,可以通过调用UIContext中getOverlayManager方法获取OverlayManager对象,并利用该对象在指定层级上新增指定节点([addComponentContentWithOrder](../reference/apis-arkui/arkts-apis-uicontext-overlaymanager.md#addcomponentcontentwithorder18)),层次高的浮层会覆盖在层级低的浮层之上。
208
209```ts
210import { ComponentContent, LevelOrder, OverlayManager } from '@kit.ArkUI';
211
212class Params {
213  text: string = "";
214  offset: Position;
215  constructor(text: string, offset: Position) {
216    this.text = text;
217    this.offset = offset;
218  }
219}
220
221@Builder
222function builderTopText(params: Params) {
223  Column() {
224    Stack(){
225      Text(params.text)
226        .fontSize(30)
227        .fontWeight(FontWeight.Bold)
228    }.width(300).height(200).padding(5).backgroundColor('#F7F7F7').alignContent(Alignment.Top)
229  }.offset(params.offset)
230}
231
232@Builder
233function builderNormalText(params: Params) {
234  Column() {
235    Stack(){
236      Text(params.text)
237        .fontSize(30)
238        .fontWeight(FontWeight.Bold)
239    }.width(300).height(400).padding(5).backgroundColor('#D5D5D5').alignContent(Alignment.Top)
240  }.offset(params.offset)
241}
242
243@Entry
244@Component
245struct Index {
246  private ctx: UIContext = this.getUIContext();
247  private overlayManager: OverlayManager = this.ctx.getOverlayManager();
248  @StorageLink('contentArray') contentArray: ComponentContent<Params>[] = [];
249  @StorageLink('componentContentIndex') componentContentIndex: number = 0;
250  @StorageLink('arrayIndex') arrayIndex: number = 0;
251  @StorageLink('componentOffset') componentOffset: Position = {x: 0, y: 80};
252
253  build() {
254    Row() {
255      Column({ space: 5 }) {
256        Button('点击打开置顶弹窗')
257          .onClick(() => {
258            let componentContent = new ComponentContent(
259              this.ctx, wrapBuilder<[Params]>(builderTopText),
260              new Params('我是置顶弹窗', this.componentOffset)
261            );
262            this.contentArray.push(componentContent);
263            this.overlayManager.addComponentContentWithOrder(componentContent, LevelOrder.clamp(100000));
264          })
265        Button('点击打开普通弹窗')
266          .onClick(() => {
267            let componentContent = new ComponentContent(
268              this.ctx, wrapBuilder<[Params]>(builderNormalText),
269              new Params('我是普通弹窗', this.componentOffset)
270            );
271            this.contentArray.push(componentContent);
272            this.overlayManager.addComponentContentWithOrder(componentContent, LevelOrder.clamp(0));
273          })
274        Button("点击移除弹窗").onClick(()=>{
275          if (this.arrayIndex >= 0 && this.arrayIndex < this.contentArray.length) {
276            let componentContent = this.contentArray.splice(this.arrayIndex, 1);
277            this.overlayManager.removeComponentContent(componentContent.pop());
278          } else {
279            console.info("arrayIndex有误");
280          }
281        })
282      }.width('100%')
283    }
284  }
285}
286```
287![overlayManager-demo3](figures/overlaymanager-demo_3.gif)
288