09窗口管理
小窗(悬浮窗)
窗口管理
入库类中:创建窗口和销毁窗口的核心代码。
设计小窗参数接口
呈现小窗口时需要指定小窗口的位置、尺寸、内容。
src/main/ets/model/SubWindowOption.ets
export default interface SubWindowOption {
// 小窗名称
subWindowName:string
// 小窗位置
x: number
y: number
// 小窗尺寸
width: number
height: number
// 小窗页面路由地址
subWindowPageUrl: string
}
设计工具类
src/main/ets/common/MySubWindow.ets
import { window } from '@kit.ArkUI';
import SubWindowOption from '../model/SubWindowOption';
class MySubWindow{
windowStage_: window.WindowStage | null = null;
// 小窗口对象
sub_windowClass: window.Window | null = null;
// 创建小窗的方法
async showSubWindow(subWindowOption:SubWindowOption) {
// 1.创建应用子窗口
if (this.windowStage_ == null) {
console.error('dxin => 小窗无法创建,因为 windowStage_ is null');
} else {
this.sub_windowClass = await this.windowStage_.createSubWindow(subWindowOption.subWindowName)
// 2 同步代码 设置小窗 位置和尺寸
this.sub_windowClass.moveWindowToAsync(subWindowOption.x, subWindowOption.y)
this.sub_windowClass.resizeAsync(subWindowOption.width, subWindowOption.height)
// 窗口里面得有内容
this.sub_windowClass.setUIContent(subWindowOption.subWindowPageUrl, () => {
// 让自己显示出来 需要问号或者叹号判空
//this.sub_windowClass!.showWindow()
this.sub_windowClass?.showWindow()
})
}
}
destroySubWindow() {
// 4.销毁子窗口。当不再需要子窗口时,可根据具体实现逻辑,使用destroy对其进行销毁。
//this.sub_windowClass!.destroyWindow()
this.sub_windowClass?.destroyWindow();
}
}
let mySubWindow = new MySubWindow()
export default mySubWindow
在入口类中初始化工具类的windowStage_
src/main/ets/entryability/EntryAbility.ets
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Guide', (err) => {
if (err.code) {
return;
}
// 工具类中的那个主舞台对象 要被真正的主舞台(入口类的)对象赋值
mySubWindow.windowStage_ = windowStage
});
}
设计小窗页面
src/main/ets/pages/SubWindowContent.ets
import mySubWindow from '../common/MySubWindow'
@Entry
@Component
struct SubWindowContent {
build() {
Column({ space: 30 }) {
Text('QQ小窗口').fontSize(22).fontColor('red')
Button('关闭当前小窗口')
.onClick(() => {
mySubWindow.destroySubWindow()
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
在页面中调用工具类开启小窗
src/main/ets/pages/Index.ets
Button('开启QQ的小窗')
.onClick(() => {
mySubWindow.showSubWindow({
subWindowName:"QQ ",
x:50,
y:250,
width:500,
height:500,
subWindowPageUrl:"pages/SubWindowContent"
})
})
问题和优化
无法关闭多个小窗。
以上案例中准备了一个QQ的按钮,点击即可显示QQ的小窗,同时也可在显示的小窗上点击关闭自己。
既然封装工具类,传入参数。目的就是为了创建多个小窗。而以上代码则会出现问题。例如:
在 src/main/ets/pages/Index.ets 再准备一个按钮,准 备打开微信的小窗
Button('开启微信的小窗')
.onClick(() => {
mySubWindow.showSubWindow({
subWindowName:"wechat",
x:700,
y:250,
width:500,
height:500,
subWindowPageUrl:"pages/Wechat"
})
})
补充微信小窗对应的页面文件:src/main/ets/pages/Wechat.ets
import mySubWindow from '../common/MySubWindow'
@Entry
@Component
struct Wechat {
build() {
Column({ space: 30 }) {
Text('wechat小窗口').fontSize(22).fontColor('red')
Button('关闭当前小窗口')
.onClick(() => {
mySubWindow.destroySubWindow()
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
此时可以拉起两个小窗口,但是关闭操作无法按照预期实现,分析小窗工具类会发现。每次点击按钮打开窗口都会执行showSubWindow()方法并创建当前小窗对象,紧接着将创建的小窗对象赋值给工具类的sub_windowClass属性。
那么:连续创建两个或者多个窗口时,该sub_windowClass属性存储的是最后一个小窗对象。
所以,点击任何一个小窗的关闭按钮,均会调用和执行工具类中的destroySubWindow()方法,销毁的正是sub_windowClass属性中存储的小窗,即,最后一个小窗。 因此,除最后一个小窗可被关闭外,其他小窗对象均无法被关闭。
实现关闭多个小窗
经过以上分析,无法关闭多个小窗的主要原因是:新创建的小窗对象存在普通变量中导致后来者居上,前者被覆盖。
很容易想到解决办法:将所有的小窗对象使用数组存起来。
随即而来的问题是,如果数组中存放的是单纯的小窗对象,我们在点击不同的关闭按钮时,又该如何从数组中找到我们需要的那个小窗对象呢。
此时,你很容易想到的是:给每一个存入数组的小窗对象添加字的标记即可。恰好,创建的每一个小窗对象,本就有自己的名字。刚好用来作为标记。
设计数组中的数据类型
小窗对象+该对象的名字。
src/main/ets/model/SubWindowDescript.ets
import { window } from "@kit.ArkUI"
export default interface SubWindowDescript{
subWindowName:string
subWindowObj:window.Window
}
优化工具类
使用数组存储替换之前的变量存储
src/main/ets/common/MySubWindow.ets
import { window } from '@kit.ArkUI';
import SubWindowDescript from '../model/SubWindowDescript';
import SubWindowOption from '../model/SubWindowOption';
class MySubWindow{
windowStage_: window.WindowStage | null = null;
// 创建一个数组
mySubWindowArr: SubWindowDescript [] = [ ]
async showSubWindow(options: SubWindowOption) {
if (this.windowStage_) {
// 连续创建新小窗 此处会重新赋值 覆盖上一个小窗对象 所以应该设计成 当前小窗对象数组 根据名称对应
let currentSubWindowObj = await this.windowStage_.createSubWindow(options.subWindowName)
// 存入数组
this.mySubWindowArr.push({
subWindowName:options.subWindowName,
subWindowObj: currentSubWindowObj
})
// 使用当前小窗对象 同步 设置窗口位置
currentSubWindowObj.moveWindowToAsync(options.x, options.y)
// 同步 设置窗口尺寸
currentSubWindowObj.resizeAsync(options.width, options.height)
// 设置窗口的UI内容 指定页面路由 (需要提前准备好UI页面)
currentSubWindowObj.setUIContent(options.subWindowPageUrl, () => {
// 让我自己显示出来 ? 避免this.mySubWindowObj 是null的场景
currentSubWindowObj?.showWindow()
})
} else {
console.error(`dxin => 你的 windowStage_ 是null 创建不出小窗哦`)
}
}
destroySubWindow(subWindowName:string){
// 根据小窗名称 从数组中查出来 当前需要被关闭的那个小窗对象
this.mySubWindowArr.find(item => item.subWindowName === subWindowName)?.subWindowObj?.destroyWindow()
}
}
let mySubWindow = new MySubWindow()
export default mySubWindow
在小窗中调用关闭方法时传入参数
注意,销毁小窗的方法需要的参数,必须是创建该小窗时的小窗名字。
如果有可能手误拼错单词,可设计到string.json或者常量文件中进行引用,以提高正确性和复用性。
关闭QQ小窗/关闭微信小窗,二选一写人对应页面文件的点击事件中
Button('关闭当前小窗口')
.onClick(() => {
mySubWindow.destroySubWindow("QQ")
mySubWindow.destroySubWindow("wechat")
})