想自定义广告-

场景: app冷启动/热启动-有广告需求,就打开广告页,没有的话就去登录或者主页

  • 有的app有的需要广告页,有的不需要,搞个配置呗!!!
  1. 通过首选项配置存储我们的一些常用配置,比如要不要广告页,还有广告页的路由地址,点击广告页跳转的链接,广告页倒计时的秒数
  2. 在入口处进行判断是否需要广告页,需要的话,跳转广告页-广告页根据设置的参数进行渲染
  3. 广告页能不能设置???--- 因为运营人员肯定不能每次都去改我们底层的代码-这里我还可以设置成动态的-就是初始化的时候通过请求去读一下云端的请求,然后把我们的图片和一些参数配置下来,这样每次你启动app就是运营人员给你配置的广告和设置了

1.新建一个关于广告类的数据模型

export class AdvertClass {
  showAd: boolean = false // 是否展示广告
  isFull: boolean = true // 是否全屏
  adTime: number = 5 // 倒计时数据
  adUrl?: string = "" // 要跳转的连接
  adImg?: ResourceStr = "" // 图片连接
}

// 全屏广告 半屏广告

2.在utils中新建一个关于读取首选项的类,用来读取和设置首选项的广告设置- utils/setting.ets


export const defaultAd: AdvertClass = {
  showAd: true,
  isFull: true,
  adTime: 5,
  adImg: $r('[basic].media.start')
}

export const USER_SETTING = 'fast_driver_setting' // 用来存储用户设置的首选项的key

export const USER_SETTING_AD = 'fast_driver_setting_ad' // 用来存储用户设置广告首选项的key

// 负责首选项的读取
export class UserSetting {
  context: Context

  constructor(context: Context) {
    this.context = context
  }

  // 获取仓库
  getStore() {
    return preferences.getPreferencesSync(this.context || getContext(), {
      name: USER_SETTING
    })
  }

  // 设置用户广告
  async setUserAd(ad: AdvertClass) {
    const store = this.getStore()
    store.putSync(USER_SETTING_AD, ad)
    await store.flush() // 让外界能够控制自己的流程
  }

  // 获取用户广告
  getUserAd() {
    const store = this.getStore()
    return store.getSync(USER_SETTING_AD, defaultAd) as AdvertClass
  }
}

export const userSetting = new UserSetting(getContext()) // 导出一个单例

3.实现Start页的页面结构及倒计时逻辑

import { window } from '@kit.ArkUI'
import { AdvertClass, UserSetting } from 'home'

@Entry
@Component
struct Start {
  userSetting: UserSetting = new UserSetting(getContext(this))
  @State
  adObj: AdvertClass = {
    showAd: false,
    adTime: 0
  }
  timer: number = -1

  closeWin() {
    window.findWindow("ad_window").destroyWindow()
  }

  async aboutToAppear() {
    this.adObj = await this.userSetting.getUserAd()
    this.timer = setInterval(() => {
      if (this.adObj.adTime === 0) {
        clearInterval(this.timer)
        this.closeWin()
        return
      }
      this.adObj.adTime--
    }, 1000)
  }

  aboutToDisappear(): void {
    clearInterval(this.timer)
  }

  build() {
    Stack({ alignContent: Alignment.TopEnd }) {
      Image(this.adObj.adImg).objectFit(ImageFit.Cover)
      Text(`${this.adObj.adTime}秒后跳过`)
        .padding({ left: 10, right: 10 })
        .margin({ right: 20, top: 20 })
        .height(30)
        .fontSize(14)
        .borderRadius(15)
        .backgroundColor($r('[basic].color.MW'))
        .textAlign(TextAlign.Center)
        .onClick(() => {
          this.closeWin()
        })
    }
    .height('100%')
    .width('100%')
    .padding({ top: 40 })
  }
}

4.封装window窗口广告模式

import { display, window } from '@kit.ArkUI'
import { util } from '@kit.ArkTS'

export class AdManager {
  context?: Context // 是给ability使用的
  private winNames: string [] = []

  // 展示广告 采用windows窗口的创建和销毁的方式
  async showAd(url: string, width?: number, height?: number) {
    if (url) {
      let name = `win_${util.generateRandomUUID()}`
      const win = await window.createWindow({
        name,
        windowType: window.WindowType.TYPE_DIALOG,
        ctx: this.context || getContext()
      })
      if (width && width >= 320 && height && height >= 240) {
        const screen = display.getDefaultDisplaySync()
        let mainWidth = vp2px(width)
        let mainHeight = vp2px(height)
        win.resizeAsync(mainWidth, mainHeight)
        win.moveWindowToAsync((screen.width - mainWidth) / 2, (screen.height - mainHeight) / 2)
      }
      await win.showWindow() // 展示窗口
      win.setUIContent(url) // 设置地址
      this.winNames.push(name)
      return name
    }
    return ""
  }

  // 关闭广告
  async closeAd(name?: string) {
    if (name) {
      window.findWindow(name).destroyWindow()
      this.winNames = this.winNames.filter(item => item !== name) //清空数组内容
    } else {
      // 不传就认为 想关闭所有
      let index = 0
      while (index < this.winNames.length) {
        await window.findWindow(this.winNames[index]).destroyWindow()
        index++
      }
      this.winNames = [] // 清空数组
    }

  }
}

export const adManger = new AdManager()

5.采用与页面解耦的模式实现广告

import { AdvertClass } from '../viewmodels'
import { ComponentContent, promptAction, window } from '@kit.ArkUI'
import { util } from '@kit.ArkTS'

// 展示广告的结构最重要写的代码
@Builder
function AdBuilder(ad: AdvertClass) {
  Column() {
    Image(ad.adImg)
      .width("100%")
      .height("100%")
      .objectFit(ImageFit.Cover)
      .borderRadius(10)

    Row() {
      Image($r("app.media.ic_btn_close"))
        .width(14)
        .aspectRatio(1)
        .fillColor("#ccc")
    }
    .width(30)
    .aspectRatio(1)
    .justifyContent(FlexAlign.Center)
    .borderRadius(15)
    .border({
      color: '#ff343232',
      width: 2
    })
    .margin({
      top: 40
    })
    .onClick(() => {
      if (ad.dialogName) {
        adManagerFinal.closeAd(ad.dialogName) //  ? name从哪里进来
      }

    })

  }
  .width(ad.isFull ? "100%" : "80%")
  .height(ad.isFull ? "100%" : "50%")
}

export class AdManagerFinal {
  context?: Context
  // 所有的弹窗都放到这个map中 通过name来标识
  private map: Map<string, ComponentContent<AdvertClass>> = new Map()

  // 实际上需要广告
  async showAd(ad: AdvertClass) {
    // 按照文档实现
    // UIContext上下文必须得等到页面初始化之后才可以进行获取
    // 生成一个name
    let name = `dialog_${util.generateRandomUUID()}`
    // 通过当前的主窗口来获取
    const mainWin = await window.getLastWindow(this.context || getContext())
    let uiContext = mainWin.getUIContext() // 拿到UIContext
    let promptAction = uiContext.getPromptAction();
    ad.dialogName = name // 目的是将dialog的弹窗名称传递到builder中
    let contentNode = new ComponentContent(uiContext, wrapBuilder(AdBuilder), ad);
    let options: promptAction.BaseDialogOptions = {
      alignment: DialogAlignment.Center,
      autoCancel: false
    };
    this.map.set(name, contentNode) // 将key/value写入到map中
    promptAction.openCustomDialog(contentNode, options);


    // 一般半屏广告 是得用户手动点击才能关闭的 一般不会自动关闭
    // setTimeout(() => {
    //   promptAction.closeCustomDialog(contentNode)
    // }, 2000)


  }

  async closeAd(name: string) {
    if (name) {
      const mainWin = await window.getLastWindow(this.context || getContext())
      let uiContext = mainWin.getUIContext() // 拿到UIContext
      let promptAction = uiContext.getPromptAction();
      promptAction.closeCustomDialog(this.map.get(name))
      // 清理map
      this.map.delete(name) // 删除已经关闭的弹窗
    }
  }
}

export const adManagerFinal = new AdManagerFinal()

6.完整代码-EntryAbility

 async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    // 开启全屏
    const win = windowStage.getMainWindowSync()
    win.setWindowLayoutFullScreen(true)

    // 获取安全区域
    const top = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM)
      .topRect
    AppStorage.setOrCreate<number>('safeTop', px2vp(top.height))
    const bottom = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR)
      .bottomRect
    AppStorage.setOrCreate<number>('safeBottom', px2vp(bottom.height))

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
      getUser.initUser()
      auth.initUser()
    });


    //广告
    // 发起一个向云端获取广告数据的请求
    const userSetting = new UserSetting(this.context)
    const result = await new Promise<AdvertClass>((resolve, reject) => {
      setTimeout(() => {
        resolve({
          showAd: true,
          adTime: 5,
          adImg: $r('[basic].media.start')
        } as AdvertClass)
      }, 1000)
    })
    await userSetting.setUserAd(result) // 写入首选项
    if (result.showAd) {
      const win = await windowStage.createSubWindow("ad_window")
      await win.showWindow()
      win.setUIContent("pages/StartPage")
    }
  }

更多推荐