鸿蒙next半屏透明登录页

1,708 阅读4分钟

最近在开发鸿蒙next手游sdk发现通过router打开的page,无法达到背景透明效果,在查找各路大神提供的方法,发现有三种思路可以达到这种效果

  • 使用自定义Dialog
  • 在主页中使用Stack(层叠布局)嵌套view,感兴趣的朋友可以去查看一下官方文档
  • 使用子窗口加载页面

今天想说的是如何使用子窗口加载登录页面达到背景透明的效果,先来看看最终实现的效果

image.png

创建DialogHelper.ets

import { common } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import display from '@ohos.display'

/**
 *使用子窗口加载登录页,达到页面背景透明可看到上一级页面的效果
 * */
export class DialogHelper {
  static dialogHelper: DialogHelper | null = null;
  dialogWindow: window.Window | null = null;
  width: number
  height: number;
  windowWidth: number
  windowHeight: number
  context: common.UIAbilityContext | null = null;
  promisse: Promise<number> | null = null;

  private constructor() {
    this.width = display.getDefaultDisplaySync().width;
    this.height = display.getDefaultDisplaySync().height;
    this.windowWidth = this.width; //子窗口宽度
    this.windowHeight = this.height; //子窗口高度
  }

  static instance(): DialogHelper {
    if (!DialogHelper.dialogHelper) {
      DialogHelper.dialogHelper = new DialogHelper();
    }
    return DialogHelper.dialogHelper;
  }

  //显示登录弹窗
  initDialog(context: common.UIAbilityContext, windowStage: window.WindowStage): void {
    this.context = context
    windowStage.createSubWindow('Login', (err, window) => {
      this.dialogWindow = window
      //监听横竖屏变化, 横竖屏变化后根据当前方向变换位置
      let callback = async () => {
        this.width = display.getDefaultDisplaySync().width;
        this.height = display.getDefaultDisplaySync().height;
        if (this.dialogWindow && this.dialogWindow?.isWindowShowing()) {
          this.showDialog(false)
        } else {
          //释放监听
          display.off("change", callback)
        }
      }
      display.on("change", callback)
      //沉浸式
      this.dialogWindow!.setWindowLayoutFullScreen(true)
      this.showDialog(true);
    })
  }

  //load 是否加载content
  showDialog(load: boolean): void {

    let windowX = (this.width - this.windowWidth) / 2; //子窗口左上角X坐标
    let windowY = (this.height - this.windowHeight) / 1; //子窗口左上角Y坐标

    (this.dialogWindow as window.Window).moveWindowTo(windowX, windowY, (err) => {
    });
    //定义弹窗大小
    (this.dialogWindow as window.Window).resize(this.windowWidth, this.windowHeight, (err) => {
    });

    // 给子窗口设置内容
    if (load) {
      //这里注意如果是在静态module中,需要使用loadContentByName加载页面别名。示例(this.dialogWindow as window.Window).loadContentByName("LoginDialog", (err) => {}
      (this.dialogWindow as window.Window).setUIContent("pages/LoginDialog", (err) => {
        // 显示子窗口。
        (this.dialogWindow as window.Window).showWindow((err) => {
          //设置子窗口的背景为透明的,一定要放在页面加载完成后再设置
          (this.dialogWindow as window.Window).setWindowBackgroundColor('#00000000');
        });
      });
    }
  }

  //在登录页面中点击了提交登录的按钮
  onclickLogin(eventType: number): void {
    //因为窗口是透明的,无法看见 但是真实存在的,使用登录完成后需要关闭窗口避免窗口覆盖上一个页面无法点击
    this.dismissLoginDialog()
  }
  
  //关闭窗口
  private dismissLoginDialog(): void {
    if (this.dialogWindow) {
      this.dialogWindow.destroyWindow((err) => {
      })
    }
  }
}

从上面代码可以看到这里创建窗口需要使用到 common.UIAbilityContextwindow.WindowStage 这两个参数从哪里获取呢,往下看

创建一个Master.ets

import { common } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { DialogHelper } from './dialog/DialogHelper';

export class Master {
  private static mInstance: Master;
  private _uiAbilityContext: common.UIAbilityContext | null = null;
  // 窗口管理器,用来添加弹窗
  private _windowStage: window.WindowStage | null = null;
  private constructor() {
  }

  // 单例模式
  static getInstance(): Master {
    if (!Master.mInstance) {
      Master.mInstance = new Master();
    }
    return Master.mInstance;
  }

  public init(uiAbilityContext: common.UIAbilityContext, windowStage: window.WindowStage): Promise<string> {
    return new Promise<string>((resolve, reject) => {
      this._uiAbilityContext = uiAbilityContext;
      this._windowStage = windowStage;
      resolve("初始化成功")
    })
  }

  login():void{
    DialogHelper.instance().initDialog(this._uiAbilityContext!,this._windowStage!)
  }

}

这里初始化时要求传入 common.UIAbilityContextwindow.WindowStage,然后把初始化的调用时机放在继承了 UIAbility的EntryAbility onWindowStageCreate生命周期中页面加载成功后,如图:

image.png

到这里我们创建子窗口所需要的上下文及窗口管理器就已经获取到了,开始写登录页面 创建一个page为LoginDialog.ets

import { DialogHelper } from '../com/demo/dialog/DialogHelper';

@Entry()
  //这里注意如果是在静态module中,需要给页面设置别名,然后加载页面时使用别名名称,示例@Entry({ routeName: 'LoginDialog' })
@Component
struct LoginDialog {
  //用户登录-用户名
  @State account: string = ''
  //用户登录-用户密码
  @State pws: string = ''
  //半模态页面显示状态控制
  @State isLoginShowSheet: boolean = true
  //半模态页面高度
  @State sheetHeight: number = 450;

  @Builder
  loginBuilder() {
    Column() {

      Column({ space: 30 }) {
        TextInput({ text: "", placeholder: '请输入账号' })
          .maxLength(20)
          .margin({ top: 20 })
          .onChange((value: string) => {
            this.account = value
          })
          .borderRadius(10)
          .borderColor(FontWeight.Bold)

        TextInput({ text: "", placeholder: '请输入密码' })
          .maxLength(20)
          .margin({ bottom: 20 })
          .onChange((value: string) => {
            this.pws = value
          })
          .borderRadius(10)
          .borderColor(FontWeight.Bold)
      }
      .margin({ top: 20, bottom: 20 })

      Button('登 录').width('100%').margin({ top: 20 })
        .enabled(this.account.length > 0 && this.pws.length > 0)
        .onClick(() => {
          //提交登录
        })
    }.padding(20)
  }

  build() {
    Column() {
      Column().height(20)//设置半模态转场
        .bindSheet($$this.isLoginShowSheet, this.loginBuilder(), {
          height: this.sheetHeight,
          onWillAppear: () => {
            console.log("BindSheet onWillAppear.")
          },
          onAppear: () => {
            console.log("BindSheet onAppear.")
          },
          onWillDisappear: () => {
            console.log("BindSheet onWillDisappear.")
          },
          onDisappear: () => {
            console.log("BindSheet onDisappear.")
          },
          onWillDismiss: () => {
            //半模态页面关闭后的处理
            DialogHelper.dialogHelper?.onclickLogin(0)
            this.isLoginShowSheet = false

          }
        }).backgroundColor('#00000000')
    }
    .width('100%')
    .height(10)
    .backgroundColor('#00000000')
    .justifyContent(FlexAlign.Center)
  }
}

这里使用的Column直接被设置成了透明,真正显示出来的登录页面是用的半模态写的,鸿蒙next中的模态转场个人觉得非常方便,感兴趣可以去看看

调用登录示例


import { Master } from '../com/demo/Master';

@Entry
@Component
struct Index {

  build() {
   Stack(){
     Column(){
       Button("打开登录页").fontSize(20).width("80%").height(45).margin({top: 50})
         .onClick(()=>{
          Master.getInstance().login()
         })
     }.alignSelf(Alignment.Center|Alignment.Bottom)
     .width('100%')
     .height('100%')
     .backgroundColor(Color.Pink)
   }
  }
}

到此就完成了一个透明背景的登录页开发,欢迎大家多多交流看看是否还有其他更好的方法实现