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