• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 管理系统窗口(仅Stage模型支持)
2
3## 管理系统窗口概述
4
5在`Stage`模型下, 允许系统应用创建和管理系统窗口,包括音量条、壁纸、通知栏、状态栏、导航栏等。具体支持的系统窗口类型见[API参考-WindowType](../reference/apis/js-apis-window.md#windowtype7)。
6
7在窗口显示、隐藏及窗口间切换时,窗口模块通常会添加动画效果,以使各个交互过程更加连贯流畅。
8
9在OpenHarmony中,应用窗口的动效为默认行为,不需要开发者进行设置或者修改。
10
11相对于应用窗口,在显示系统窗口过程中,开发者可以自定义窗口的显示动画、隐藏动画。
12
13> **说明:**
14>
15> 本文档涉及系统接口的使用,请使用full-SDK进行开发。具体使用可见[full-SDK替换指南](../faqs/full-sdk-switch-guide.md)。
16
17
18## 接口说明
19
20更多API说明请参见[API参考](../reference/apis/js-apis-window.md)。
21
22| 实例名            | 接口名                                                       | 描述                                                         |
23| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
24| window静态方法    | createWindow(config: Configuration, callback: AsyncCallback\<Window>): void | 创建子窗口或系统窗口。<br/>-`config`:创建窗口时的参数。     |
25| Window            | resize(width: number, height: number, callback: AsyncCallback&lt;void&gt;): void | 改变当前窗口大小。                                           |
26| Window            | moveWindowTo(x: number, y: number, callback: AsyncCallback&lt;void&gt;): void | 移动当前窗口位置。                                           |
27| Window            | setUIContent(path: string, callback: AsyncCallback&lt;void&gt;): void | 根据当前工程中某个页面的路径为窗口加载具体的页面内容。<br>其中path为要加载到窗口中的页面内容的路径,在Stage模型下该路径需添加到工程的main_pages.json文件中。                                     |
28| Window            | showWindow(callback: AsyncCallback\<void>): void             | 显示当前窗口。                                               |
29| Window            | on(type: 'touchOutside', callback: Callback&lt;void&gt;): void | 开启本窗口区域外的点击事件的监听。                           |
30| Window            | hide (callback: AsyncCallback\<void>): void                  | 隐藏当前窗口。此接口为系统接口。                             |
31| Window            | destroyWindow(callback: AsyncCallback&lt;void&gt;): void     | 销毁当前窗口。                                               |
32| Window            | getTransitionController(): TransitionController              | 获取窗口属性转换控制器。此接口为系统接口。                   |
33| TransitionContext | completeTransition(isCompleted: boolean): void               | 设置属性转换的最终完成状态。该函数需要在动画函数[animateTo()](../reference/arkui-ts/ts-explicit-animation.md)执行后设置。此接口为系统接口。 |
34| Window            | showWithAnimation(callback: AsyncCallback\<void>): void      | 显示当前窗口,过程中播放动画。此接口为系统接口。             |
35| Window            | hideWithAnimation(callback: AsyncCallback\<void>): void      | 隐藏当前窗口,过程中播放动画。此接口为系统接口。             |
36
37## 系统窗口的开发
38
39本文以音量条窗口为例,介绍系统窗口的基本开发和管理步骤。
40
41### 开发步骤
42
43
441. 创建系统窗口。
45
46   在[ServiceExtensionContext](../reference/apis/js-apis-inner-application-serviceExtensionContext.md)下,使用`window.createWindow`接口创建音量条系统窗口。
47
482. 操作或设置系统窗口的属性。
49
50   系统窗口创建成功后,可以改变其大小、位置等,还可以根据需要设置系统窗口的背景色、亮度等属性。
51
523. 加载显示系统窗口的具体内容。
53
54   通过`setUIContent`和`showWindow`接口加载显示音量条窗口的具体内容。
55
564. 隐藏/销毁系统窗口。
57
58   当不再需要音量条窗口时,可根据具体实现逻辑,使用`hide`接口或`destroyWindow`接口对其进行隐藏或销毁。
59
60```ts
61import ExtensionContext from '@ohos.app.ability.ServiceExtensionAbility';
62import window from '@ohos.window';
63import { BusinessError } from '@ohos.base';
64import Want from '@ohos.app.ability.Want';
65
66export default class ServiceExtensionAbility1 extends ExtensionContext {
67  onCreate(want: Want) {
68    // 1.创建音量条窗口。
69    let windowClass: window.Window | null = null;
70    let config: window.Configuration = {
71      name: "volume", windowType: window.WindowType.TYPE_VOLUME_OVERLAY, ctx: this.context
72    };
73    window.createWindow(config, (err: BusinessError, data) => {
74      let errCode: number = err.code;
75      if (errCode) {
76        console.error('Failed to create the volume window. Cause:' + JSON.stringify(err));
77        return;
78      }
79      console.info('Succeeded in creating the volume window.')
80      windowClass = data;
81      // 2.创建音量条窗口成功之后,可以改变其大小、位置或设置背景色、亮度等属性。
82      windowClass.moveWindowTo(300, 300, (err: BusinessError) => {
83        let errCode: number = err.code;
84        if (errCode) {
85          console.error('Failed to move the window. Cause:' + JSON.stringify(err));
86          return;
87        }
88        console.info('Succeeded in moving the window.');
89      });
90      windowClass.resize(500, 500, (err: BusinessError) => {
91        let errCode: number = err.code;
92        if (errCode) {
93          console.error('Failed to change the window size. Cause:' + JSON.stringify(err));
94          return;
95        }
96        console.info('Succeeded in changing the window size.');
97      });
98      // 3.为音量条窗口加载对应的目标页面。
99      windowClass.setUIContent("pages/page_volume", (err: BusinessError) => {
100        let errCode: number = err.code;
101        if (errCode) {
102          console.error('Failed to load the content. Cause:' + JSON.stringify(err));
103          return;
104        }
105        console.info('Succeeded in loading the content.');
106        // 3.显示音量条窗口。
107        (windowClass as window.Window).showWindow((err: BusinessError) => {
108          let errCode: number = err.code;
109          if (errCode) {
110            console.error('Failed to show the window. Cause:' + JSON.stringify(err));
111            return;
112          }
113          console.info('Succeeded in showing the window.');
114        });
115      });
116      // 4.隐藏/销毁音量条窗口。当不再需要音量条时,可根据具体实现逻辑,对其进行隐藏或销毁。
117      // 此处以监听音量条区域外的点击事件为例实现音量条窗口的隐藏。
118      windowClass.on('touchOutside', () => {
119        console.info('touch outside');
120        (windowClass as window.Window).hide((err: BusinessError) => {
121          let errCode: number = err.code;
122          if (errCode) {
123            console.error('Failed to hide the window. Cause: ' + JSON.stringify(err));
124            return;
125          }
126          console.info('Succeeded in hidinging the window.');
127        });
128      });
129    });
130  }
131};
132```
133
134## 自定义系统窗口的显示与隐藏动画
135
136在显示系统窗口过程中,开发者可以自定义窗口的显示动画。在隐藏系统窗口过程中,开发者可以自定义窗口的隐藏动画。本文以显示和隐藏动画为例介绍主要开发步骤。
137
138### 开发步骤
139
1401. 获取窗口属性转换控制器。
141
142   通过`getTransitionController`接口获取控制器。后续的动画操作都由属性控制器来完成。
143
1442. 配置窗口显示时的动画。
145
146   通过动画函数[animateTo()](../reference/arkui-ts/ts-explicit-animation.md)配置具体的属性动画。
147
1483. 设置属性转换完成。
149
150   通过`completeTransition(true)`来设置属性转换的最终完成状态。如果传入false,则表示撤销本次转换。
151
1524. 显示或隐藏当前窗口,过程中播放动画。
153
154   调用`showWithAnimation`接口,来显示窗口并播放动画。调用`hideWithAnimation`接口,来隐藏窗口并播放动画。
155
156```ts
157// xxx.ts 实现使用ts文件引入showWithAnimation,hideWithAnimation方法
158import window from '@ohos.window';
159
160export class AnimationConfig {
161  private animationForShownCallFunc_: Function = undefined;
162  private animationForHiddenCallFunc_: Function = undefined;
163
164  ShowWindowWithCustomAnimation(windowClass: window.Window, callback) {
165    if (!windowClass) {
166      console.error('LOCAL-TEST windowClass is undefined');
167      return false;
168    }
169    this.animationForShownCallFunc_ = callback;
170    // 获取窗口属性转换控制器
171    let controller: window.TransitionController = windowClass.getTransitionController();
172    controller.animationForShown = (context: window.TransitionContext)=> {
173      this.animationForShownCallFunc_(context);
174    };
175
176    windowClass.showWithAnimation(()=>{
177      console.info('LOCAL-TEST show with animation success');
178    })
179    return true;
180  }
181
182  HideWindowWithCustomAnimation(windowClass: window.Window, callback) {
183    if (!windowClass) {
184      console.error('LOCAL-TEST window is undefined');
185      return false;
186    }
187    this.animationForHiddenCallFunc_ = callback;
188    // 获取窗口属性转换控制器
189    let controller: window.TransitionController = windowClass.getTransitionController();
190    controller.animationForHidden = (context : window.TransitionContext) => {
191      this.animationForHiddenCallFunc_(context);
192    };
193
194    windowClass.hideWithAnimation(()=>{
195      console.info('Hide with animation success');
196    })
197    return true;
198  }
199}
200```
201
202```ts
203// xxx.ets 实现主窗口创建相关操作
204import window from '@ohos.window';
205import Want from '@ohos.app.ability.Want';
206import hilog from '@ohos.hilog';
207import common from '@ohos.app.ability.common';
208import UIAbility from '@ohos.app.ability.UIAbility';
209
210import AbilityConstant from '@ohos.app.ability.AbilityConstant';
211
212export default class EntryAbility extends UIAbility {
213  onCreate(want: Want,launchParam:AbilityConstant.LaunchParam) {
214    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
215  }
216
217  onDestroy(): void {
218    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability Destroy');
219  }
220
221  onWindowStageCreate(windowStage:window.WindowStage): void{
222    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
223
224    windowStage.loadContent('pages/transferControllerPage',(err, data)=>{
225      if(err.code){
226        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause:%{public}s', JSON.stringify(err)??'');
227        return ;
228      }
229      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data:%{public}s', JSON.stringify(data)??'');
230
231    });
232
233    AppStorage.setOrCreate<common.UIAbilityContext>("currentContext",this.context);
234  }
235
236  onWindowStageDestroy(): void{
237    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
238  }
239
240  onForeground(): void{
241    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
242  }
243
244  onBackground(): void{
245    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
246  }
247};
248```
249
250```ts
251// xxx.ets 实现子窗口的属性设置
252import window from '@ohos.window';
253import router from '@ohos.router';
254import common from '@ohos.app.ability.common';
255
256@Entry
257@Component
258struct transferCtrlSubWindow {
259  @State message: string = 'transferCtrlSubWindow'
260
261  build() {
262    Column() {
263      Text("close")
264        .fontSize(24)
265        .fontWeight(FontWeight.Normal)
266        .margin({ left: 10, top: 10 })
267      Button() {
268        Text("close")
269          .fontSize(24)
270          .fontSize(FontWeight.Normal)
271      }.width(220).height(68)
272      .margin({ left: 10, top: 10 })
273      .onClick(() => {
274        let subWin = AppStorage.get<window.Window>("TransferSubWindow");
275        subWin?.destroyWindow();
276        AppStorage.SetOrCreate<window.Window>("TransferSubWindow", undefined);
277      })
278    }.height('100%').width('100%').backgroundColor('#ff556243').shadow({radius: 30,color: '#ff555555',offsetX: 15,offsetY: 15})
279  }
280}
281```
282
283```ts
284// xxx.ets 实现子窗口的创建以及显示、隐藏窗口时的动效操作
285import window from '@ohos.window';
286import router from '@ohos.router';
287import common from '@ohos.app.ability.common';
288import { BusinessError } from '@ohos.base';
289import { AnimationConfig } from '../entryability/AnimationConfig';
290import Want from '@ohos.app.ability.Want';
291
292@Entry
293@Component
294struct Index {
295  @State message: string = 'transferControllerWindow';
296
297  private animationConfig_?:AnimationConfig = undefined;
298  private subWindow_?:window.Window = undefined;
299
300  aboutToAppear(){
301    if(!this.animationConfig_){
302      this.animationConfig_ = new AnimationConfig();
303    }
304  }
305
306  private CreateTransferSubWindow(){
307    if(this.subWindow_){
308      this.subWindow_ = AppStorage.get<window.Window>("TransferSubWindow");
309      if(!this.subWindow_){
310        this.subWindow_ = undefined;
311      }
312    }
313    let context = AppStorage.get<common.UIAbilityContext>("currentContext");
314    console.log('LOCAL-TEST try to CreateTransferSubWindow');
315    let windowConfig:window.Configuration = {
316      name : "systemTypeWindow",
317      windowType : window.WindowType.TYPE_FLOAT,
318      ctx : context,
319    };
320    let promise = window?.createWindow(windowConfig);
321    promise?.then(async(subWin) => {
322      this.subWindow_ = subWin;
323      AppStorage.setOrCreate<window.Window>("systemTypeWindow", subWin);
324      await subWin.setUIContent("pages/transferCtrlSubWindow",()=>{});
325      await subWin.moveWindowTo(100,100);
326      await subWin.resize(200,200);
327    }).catch((err:Error)=>{
328      console.log('LOCAL-TEST createSubWindow err:' + JSON.stringify(err));
329    })
330  }
331  private ShowSUBWindow(){
332    if(!this.subWindow_){
333      console.log('LOCAL-TEST this.subWindow_is null');
334      return ;
335    }
336    let animationConfig = new AnimationConfig();
337    let systemTypeWindow = window.findWindow("systemTypeWindow");
338    console.log("LOCAL-TEST try to ShowWindowWithCustomAnimation");
339    animationConfig.ShowWindowWithCustomAnimation(systemTypeWindow,(context:window.TransitionContext)=>{
340      console.info('LOCAL-TEST start show window animation');
341      let toWindow = context.toWindow;
342      animateTo({
343        duration: 200, // 动画时长
344        tempo: 0.5, // 播放速率
345        curve: Curve.EaseInOut, // 动画曲线
346        delay: 0, // 动画延迟
347        iterations: 1, // 播放次数
348        playMode: PlayMode.Normal, // 动画模式
349        onFinish: () => {
350          console.info('LOCAL-TEST onFinish in show animation');
351          context.completeTransition(true);
352        }
353      },() => {
354        let obj: window.TranslateOptions = {
355          x: 100.0,
356          y: 0.0,
357          z: 0.0
358        };
359        try {
360          toWindow.translate(obj); // 设置动画过程中的属性转换
361        }catch(exception){
362          console.error('Failed to translate. Cause: ' + JSON.stringify(exception));
363        }
364      });
365      console.info('LOCAL-TEST complete transition end');
366    });
367  }
368
369  private HideSUBWindow(){
370    if(!this.subWindow_){
371      console.log('LOCAL-TEST this.subWindow_is null');
372      return ;
373    }
374    let animationConfig = new AnimationConfig();
375    let systemTypeWindow = window.findWindow("systemTypeWindow");
376    console.log("LOCAL-TEST try to HideWindowWithCustomAnimation");
377    animationConfig.HideWindowWithCustomAnimation(systemTypeWindow,(context:window.TransitionContext)=>{
378      console.info('LOCAL-TEST start hide window animation');
379      let toWindow = context.toWindow;
380      animateTo({
381        duration: 200, // 动画时长
382        tempo: 0.5, // 播放速率
383        curve: Curve.EaseInOut, // 动画曲线
384        delay: 0, // 动画延迟
385        iterations: 1, // 播放次数
386        playMode: PlayMode.Normal, // 动画模式
387        onFinish: () => {
388          console.info('LOCAL-TEST onFinish in hide animation');
389          context.completeTransition(true);
390        }
391      },() => {
392        let obj: window.TranslateOptions = {
393          x: 500.0,
394          y: 0.0,
395          z: 0.0
396        };
397        try {
398          toWindow.translate(obj); // 设置动画过程中的属性转换
399        }catch(exception){
400          console.error('Failed to translate. Cause: ' + JSON.stringify(exception));
401        }
402      });
403      console.info('LOCAL-TEST complete transition end');
404    });
405  }
406
407  build() {
408    Column() {
409      Text(this.message)
410        .fontSize(24)
411        .fontWeight(FontWeight.Normal)
412        .margin({left: 10, top: 10})
413
414      Button(){
415        Text("CreateSub")
416          .fontSize(24)
417          .fontWeight(FontWeight.Normal)
418      }.width(220).height(68)
419      .margin({left: 10, top: 10})
420      .onClick(() => {
421        this.CreateTransferSubWindow();
422      })
423
424      Button(){
425        Text("Show")
426          .fontSize(24)
427          .fontWeight(FontWeight.Normal)
428      }.width(220).height(68)
429      .margin({left: 10, top: 10})
430      .onClick(() => {
431        this.ShowSUBWindow();
432      })
433
434      Button(){
435        Text("Hide")
436          .fontSize(24)
437          .fontWeight(FontWeight.Normal)
438      }.width(220).height(68)
439      .margin({left: 10, top: 10})
440      .onClick(() => {
441        this.HideSUBWindow();
442      })
443    }.width('100%').height('100%')
444  }
445}
446```