HarmonyOS 鸿蒙Next 限启动页、权限使用说明、权限管理、嵌套滚动Demo请求

发布于 1周前 作者 sinazl 来自 鸿蒙OS

HarmonyOS 鸿蒙Next 限启动页、权限使用说明、权限管理、嵌套滚动Demo请求

麻烦提供限启动页,权限使用说明、权限管理、嵌套滚动这几个demo。

2 回复

嵌套滚动demo:

@Entry
@Component
struct StickyNestedScroll {
  @Styles
  listCard() {
    .backgroundColor(Color.White)
    .height(72)
    .width("100%")
    .borderRadius(12)
  }
  @State textOffset: number = 0
  @State textMargin: number = 0
  scroller: Scroller = new Scroller()
  @State swiperOffset: number = 0
  @State top: number = 100
  @State visible: boolean = false
  @State visible1: Visibility = Visibility.Visible
  @State number1: number = 0
  @State XiDing: boolean = false
  private swiperController: SwiperController = new SwiperController()
  private data: MyDataSource = new MyDataSource([])
  private data1: MyDataSource = new MyDataSource([])
  private scrollerForScroll: Scroller = new Scroller()
  @Builder
  SwiperItem(item: string) {
    Column(){
      Text(item.toString())
        .width('100%')
        .height("100%")
        .backgroundColor(0xAFEEEE)
        .textAlign(TextAlign.Center)
        .fontSize(30)
    }.width('100%')
    .height(80)
    .margin({ left: 5, right: 5 })
    .backgroundColor(Color.White)
  }
  @Builder
  SwiperItem1(item: string) {
    Column(){
      Text(item.toString())
        .width('100%')
        .height("100%")
        .backgroundColor('#fff')
        .textAlign(TextAlign.Center)
        .fontSize(30)
    }.width('100%')
    .height(50)
    .margin({ left: 5, right: 5 })
    .backgroundColor(Color.White)
  }
  build() {
    Column() {
      TextInput({ placeholder: 'input your word...' })
        .width('95%')
        .height(40)
        .margin(10)
      // swiper组件在屏幕中完全不可见时的逻辑
      if (this.visible) {
        Swiper(this.swiperController) {
          LazyForEach(this.data, (item: string) => {
            this.SwiperItem1(item)
          }, (item: string) => item)
        }
        .displayCount(5)
        .indicator(false)
        .autoPlay(false)
        .cachedCount(2)
        .index(1)
        .itemSpace(0)
        .curve(Curve.Linear)
      }
      Scroll(this.scrollerForScroll) {
        Column() {
          Swiper(this.swiperController) {
            LazyForEach(this.data, (item: string) => {
              this.SwiperItem(item)
            }, (item: string) => item)
          }
          .margin({top: this.swiperOffset})
          .displayCount(5)
          .indicator(false)
          .autoPlay(false)
          .cachedCount(2)
          .index(1)
          .itemSpace(0)
          .curve(Curve.Linear)
          Row({ space: 20 }) {
            Text('春节不打烊(高度50)')
          }
          .visibility(this.visible1)
          .width('100%')
          .height(50)
          .justifyContent(FlexAlign.Center)
          .backgroundColor(Color.Orange)
          .border({radius: { topLeft: 20, topRight: 20 }})
          Row({ space: 20 }) {
            Text('男童')
            Text('女童')
          }
          .width('100%')
          .height(50)
          .justifyContent(FlexAlign.Center)
          .backgroundColor(Color.Gray)
          Row({ space: 20 }) {
            Text('¥100以下')
            Text('¥100-¥150')
            Text('¥150以上')
          }
          .width('100%')
          .height(50)
          .justifyContent(FlexAlign.Center)
          Stack({ alignContent: Alignment.Top }) {
            List({ scroller: this.scroller, space: 10 }) {
              LazyForEach(this.data1, (item: string, index: number) => {
                ListItem() {
                  Text("item" + item)
                    .fontSize(16)
                }.listCard()
                .onAppear(() => {
                  console.log('新数据', index)
                })
              }, (item: string) => item)
            }.width("100%")
            .backgroundColor(Color.Blue)
            .edgeEffect(EdgeEffect.None)
            .margin({ top: this.top })
            .onReachStart(() => {
              console.log('start')
              if (this.swiperOffset === -180) {
                this.XiDing = false
              }
            })
            // 黄色区域滑到一半,手指松开后黄色区域全部弹出
            .onScrollStop(() => {
              if (this.swiperOffset  > -230 && this.swiperOffset && this.XiDing === true) {
                this.swiperOffset = -180
              }
            })
            .onScrollFrameBegin((offset: number, state: ScrollState) => {
              // console.log(`${offset}`)
              if (this.swiperOffset < -80) {
                this.visible = true
                this.visible1 = Visibility.None
              }
              if (this.swiperOffset > -80 && offset < 0) {
                this.visible = false
                this.visible1 = Visibility.Visible
              }
              if (this.swiperOffset - offset >= 0) {
                this.swiperOffset = 0
              } else {
                this.swiperOffset = this.swiperOffset - offset
              }
              // 第一次回滑正常后 this.XiDing 的值变成了false,会导致第二次及以后的回滑效果异常
              if (this.swiperOffset < -280) {
                this.swiperOffset = -280
                this.XiDing = true
              }
              // 这里不设 this.swiperOffset < 0 的话,初次进入页面下拉就会出现页面布局异常
              if (this.swiperOffset > -180 && offset < 0 && this.XiDing === true) {
                // console.log('不准动!!!')
                this.swiperOffset = -180
              }
              if (this.swiperOffset != -280 && this.swiperOffset != 0 && this.swiperOffset != -180) {
                return {
                  offsetRemain: 0
                }
              }
              return {
                offsetRemain: offset
              }
            })
            // 上滑需要回滑的部分
            Column() {
              Row({ space: 20 }) {
                Text('销量')
                Text('价格')
                Text('筛选')
              }
              .width('100%')
              .height(50)
              .justifyContent(FlexAlign.Center)
              .backgroundColor(Color.Yellow)
              Row({ space: 20 }) {
                // Text('筛选')
                // Text('春节不打烊')
                Text('swiper:' + `${this.swiperOffset}`)
                Text('XiDing:' + `${this.XiDing}`)
              }
              .backgroundColor(Color.Red)
              .width('100%')
              .height(50)
              .justifyContent(FlexAlign.Center)
            }
            .offset({
              y: this.textOffset
            })
            .margin({top: this.textMargin})
          }.width("100%").height('100%')
        }
      }
    }
    .backgroundColor(Color.Pink)
  }
  aboutToAppear() {
    let list: number[] = []
    for (let i = 1; i <= 10; i++) {
      list.push(i);
    }
    this.data = new MyDataSource(list)
    let list1: number[] = []
    for (let i = 0; i <= 30; i++) {
      list1.push(i);
    }
    this.data1 = new MyDataSource(list1)
  }
}
class MyDataSource implements IDataSource {
  private list: number[] = []
  constructor(list: number[]) {
    this.list = list
  }
  totalCount(): number {
    return this.list.length
  }
  getData(index: number): number {
    return this.list[index]
  }
  registerDataChangeListener(listener: DataChangeListener): void {
  }
  unregisterDataChangeListener() {
  }
}

在对应模块中的oh-package.json5中添加依赖,如:entry/oh-package.json5

"dependencies": {
    "@hms-paf/ui-widget-base": "14.0.1-103",
    "@hms-paf/ui-widget-consent": "14.0.1-103",
    "@hms-paf/ui-widget-permission": "14.0.1-103",
    "@hms-security/consent": "6.12.0-313",
    "@network/grs": "^7.0.6-300"
}

在项目的入口文件中,进行初始化,如:EntryAbility.ets

// 导入Paf UiWidget包
import { PafUiWidget } from '@hms-paf/ui-widget-base';
// 导入GRS包,用于初始化GRS
import { GrsFileUtil } from '@network/grs';
// 导入consent相关包
import { AgreementCallBack, PafAgreement, PafClientInfo } from '@hms-paf/ui-widget-consent';
// 业务自己适配的接口
import { AgreementConfig } from '../Component/AgreementConfig';1

在项目的入口文件中,进行初始化,如:EntryAbility.ets

async onWindowStageCreate(windowStage: window.WindowStage) {
    // Paf UiWidget 初始化
    PafUiWidget.init(windowStage)
    // Grs初始化
    GrsFileUtil.copyGrsConfigFile(this.context, false);
	// 业务自己适配的接口初始化paf
  AgreementConfig.context = this.context;
    AgreementConfig.windowStage = windowStage;
    let agreementCallBack: AgreementCallBack = {
      loadWelcomePage: async () => {
        windowStage.loadContent('pages/permissionNormal');
      },
      loadMainPage: async () => {
        windowStage.loadContent('pages/index');
      },
      signCallBack: new AgreementConfig.SignCallBack(),
      revokeCallBack: new AgreementConfig.RevokeCallBack(),
      queryRecordsCallBack: new AgreementConfig.QueryCallBack(),
    }
    let clientInfoIn: PafClientInfo = await AgreementConfig.clientInfo();
    let popupPermission: boolean = false;
    // 开关,控制协议签署方式(目前switchToNew 属性测试代码,先用init,initAsync等正式版本后再使用)
    let switchToNew: boolean = AppStorage.get<boolean>('Paf.UseNewConsentWay') || false;
    if (switchToNew) {
      popupPermission = await PafAgreement.initAsync(this.context, clientInfoIn, AgreementConfig.userAccount(),
        AgreementConfig.getAgreementTypes(), agreementCallBack);
    } else {
      // clientInfoIn仅在初始化使用一次
      popupPermission = PafAgreement.init(this.context, clientInfoIn, AgreementConfig.userAccount(),
        AgreementConfig.getAgreementTypes(), agreementCallBack);
    }
    if (popupPermission) {
      // 拉起权限启动页
    } else {
      // 应用业务首页
    }
  }
// 监听语言变化
  onConfigurationUpdate(newConfig: Configuration) {
    PafUiWidget.configurationUpdate(newConfig)
  }

权限启动页示例 - 字符串拼接

import I18n from '@ohos.i18n';
import router from '@ohos.router';
import { PafSpan, PafText } from '@hms-paf/ui-widget-base';
import {
  PafPermission,
  PafPermissionItem,
  PafPopPermissionCancel,
  PafPopPermissionDesc
} from '@hms-paf/ui-widget-permission';
class DeclareDemo {
  format: string | Resource = ''
  args: PafSpan[] = []
}
@Entry
@Component
struct Permission {
  @State basicTitle: string = ''
  @State fullTitle: string = ''
  @State declares: Array<PafText> = []
  @State dialogDeclare: Array<PafText> = []
  declareDemo1: DeclareDemo = {
    format: `本应用需%1$s,调用%2$s、%3$s权限,获取XX信息,以为您提供XX服务。我们仅在您使用具体功能业务时,才会触发上述行为收集使用相关的个人信息。详情请参阅%4$s、%5$s。`,
    args: [
      new PafSpan($r('app.string.paf_text_arg1')).emphasis(),
      new PafSpan('XX').emphasis(),
      new PafSpan('XX').emphasis(),
      new PafSpan($r('app.string.paf_text_arg4')).hyperlink(() => {
        router.pushUrl({ url: 'pages/privacyStatement' });
      }),
      new PafSpan('权限使用说明').hyperlink(() => {
        this.permissionDescDlg.open()
      })
    ]
  }
  declareDemo2: DeclareDemo = {
    format: `请您仔细阅读上述声明,点击“同意”,即表示您知悉并同意我们向您提供本应用服务。`,
    args: []
  }
  dialogDeclareDemo1: DeclareDemo = {
    format: `全量模式下,本应用需%1$s,调用%2$s、%3$s权限,获取XX信息,以为您提供XX服务。我们仅在您使用具体功能业务时,才会触发上述行为收集使用相关的个人信息。详情请参阅%4$s。`,
    args: [
      new PafSpan($r('app.string.paf_text_arg1')).emphasis(),
      new PafSpan('XX').emphasis(),
      new PafSpan('XX').emphasis(),
      new PafSpan($r('app.string.paf_text_arg4')).hyperlink(() => {
        router.pushUrl({ url: 'pages/privacyStatement' });
      })
    ]
  }
  dialogDeclareDemo2: DeclareDemo = {
    format: `请您仔细阅读上述声明,点击“同意基本(全量)模式”,即表示您知悉并同意我们向您提供本应用服务。`,
    args: []
  }
  permissionDescDlg: CustomDialogController = new CustomDialogController({
    builder: PafPopPermissionDesc({
      title: $r('app.string.paf_permission_desc_title'),
      desc: $r('app.string.paf_permission_desc_desc'),
      items: [new PafPermissionItem("权限名称", "包括xx(子权限),用于XXXX"),
        new PafPermissionItem("权限名称2", "用于XXXX"),
        new PafPermissionItem("权限名称3", "用于XXXX"),
        new PafPermissionItem("权限名称4", "用于XXXX"),
      ]
    }),
    alignment: DialogAlignment.Bottom,
    autoCancel: true,
    customStyle: true
  })
  permissionCancelDlg: CustomDialogController = new CustomDialogController({
    builder: PafPopPermissionCancel({
      basicTitle: this.basicTitle,
      fullTitle: this.fullTitle,
      fullDesc: this.dialogDeclare,
      fullService: () => {
        console.log(`PafAgreement popPermissionCancel fullService`);
        router.replaceUrl({ url: 'pages/index' });
      }
    }),
    alignment: DialogAlignment.Bottom,
    autoCancel: true,
    customStyle: true
  })
  aboutToAppear() {
    this.basicTitle = "您可以选择“同意基本模式”以使用华为XX基本服务(播放本地歌曲),该服务需联网,调用存储权限。"
    this.fullTitle = "您可以选择“同意全量模式”以使用更多功能(如音乐推荐、歌单等)。"
    this.declares = [
      new PafText().format(this.declareDemo1.format, this.declareDemo1.args),
      new PafText().format(this.declareDemo2.format, this.declareDemo2.args)
    ]
    this.dialogDeclare = [
      new PafText().format(this.dialogDeclareDemo1.format, this.dialogDeclareDemo1.args),
      new PafText().format(this.dialogDeclareDemo2.format, this.dialogDeclareDemo2.args)
    ]
  }
  cancel() {
    this.permissionCancelDlg.open()
  }
  confirm() {
    router.replaceUrl({ url: 'pages/index' });
  }
  @Builder
  payloadBuilder() {
    PafPermission({
      appImg: $r('app.media.icon'),
      appName: $r('app.string.app_name'),
      appDesc: "声明文本使用PafText().format,支持字符串和资源字符串",
      permissionDeclare: $declares,
      confirm: () => {
        this.confirm()
      },
      cancel: () => {
        this.cancel()
      },
      //仅处理影响账号登陆的准备工作
      beforeSign:() => {
      }
    })
  }
  build() {
    Row() {
      this.payloadBuilder()
    }
    .height('100%')
  }
}

隐私声明页示例

/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 */
import { PafPrivacyStatement } from '@hms-paf/ui-widget-permission';
import { GlobalVariable } from '../Component/GlobalVariable';
import { PafWebViewController } from '@hms-paf/ui-widget-base';
@Entry
@Component
struct Index {
  // 使用 PafPrivacyStatement 组件,需要在@Entry页面中定义 PafWebViewController ,并重写onBackPress,以达到逐个返回网页的效果,而不是一下全部返回多层网页
  @Provide('Paf.WebViewController') controller: PafWebViewController = new PafWebViewController()
  onBackPress(): void | boolean {
    return this.controller.onBackPress()
  }
  @Builder
  payloadBuilder() {
    PafPrivacyStatement({
      // 协议类型id,从协议配置导出来的兜底文件中agreement_config.json获取  agrType 字段
      agreementType: 11,
    })
  }
  build() {
    Row() {
        this.payloadBuilder()
    }
  }
}

权限使用说明弹窗示例

/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 */
import { PafPermissionItem, PafPopPermissionDesc } from '@hms-paf/ui-widget-permission';
@Entry
@Component
struct PermissionDescDlg {
  //创建弹出框,引入PafPopPermissionDesc组件
  permissionDescDlg: CustomDialogController = new CustomDialogController({
    builder: PafPopPermissionDesc({
      title: $r('app.string.paf_permission_desc_title'),
      desc: $r('app.string.paf_permission_desc_desc'),
      confirm: () => {
      },
      customColor: Color.Blue,
      items: [new PafPermissionItem("权限名称", "包括xx(子权限),用于XXXX"),
        new PafPermissionItem("权限名称2", "用于XXXX"),
        new PafPermissionItem("权限名称3", "用于XXXX"),
        new PafPermissionItem("权限名称4", "用于XXXX")
      ]
    }),
    alignment: DialogAlignment.Bottom,
    autoCancel: true,
    customStyle: true
  })
  build() {
    Row() {
    }
    .height('100%')
    .width('100%')
    .onAppear(() => {
      this.permissionDescDlg.open()
    })
  }
}

挽留弹窗示例 - 字符串拼接

/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 */
import router from '@ohos.router';
import I18n from '@ohos.i18n';
import { PafSpan, PafText } from '@hms-paf/ui-widget-base';
import { PafPopPermissionCancel } from '@hms-paf/ui-widget-permission';
@Entry
@Component
struct PermissionCancelDlg {
  basicTitle: string = "如不同意上述隐私条款,可以电视”仅使用基础服务“,您将只能使用本地歌曲播放功能。"
  fullTitle: string = "如需体验更多功能(如音乐推荐、歌单等),可电视”使用完整服务“。"
  // 监听系统语言切换,重新获取隐私申明文档
  @StorageProp('Paf.language') @Watch('getDeclare') private language: string = I18n.System.getSystemLanguage()
  @State dialogDeclare: Array<PafText> = []
  permissionCancelDlg: CustomDialogController = new CustomDialogController({
    builder: PafPopPermissionCancel({
      basicTitle: this.basicTitle,
      fullTitle: this.fullTitle,
      fullDesc: $dialogDeclare,
      customColor: '',
      alwaysDarkMode: false
    }),
    alignment: DialogAlignment.Bottom,
    autoCancel: true,
    customStyle: true
  })
  getDeclare() {
    let format1: string | Resource = $r('app.string.paf_text_format')
    let args1: PafSpan[] = [
      new PafSpan($r('app.string.paf_text_arg1')).emphasis(),
      new PafSpan($r('app.string.paf_text_arg4')).hyperlink(() => {
        router.pushUrl({ url: 'pages/privacyStatement' });
      }),
      new PafSpan($r('app.string.paf_text_arg5')).hyperlink(() => {
      })
    ]
    this.dialogDeclare = [new PafText().format(format1, args1)]
    let format2: string | Resource = $r('app.string.paf_text_agree')
    let args2: PafSpan[] = [
      new PafSpan(`${this.language}`).normal()
    ]
    this.dialogDeclare.push(new PafText().format(format2, args2))
  }
  aboutToAppear() {
    this.getDeclare()
  }
  build() {
    Row() {
    }
    .width('100%')
    .height('100%')
    .onAppear(() => {
      this.permissionCancelDlg.open()
    })
  }
}

更多关于HarmonyOS 鸿蒙Next 限启动页、权限使用说明、权限管理、嵌套滚动Demo请求的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


关于HarmonyOS 鸿蒙Next的启动页、权限使用说明、权限管理及嵌套滚动Demo的请求,以下是专业回复:

启动页

HarmonyOS鸿蒙Next的启动页通常用于展示应用Logo或欢迎界面。开发者可在应用中设置启动页,确保用户在打开应用时获得良好的第一印象。

权限使用说明

应用需明确所需权限,并判断这些权限是否涉及用户敏感信息。对于敏感信息权限,需通过用户授权方式获得,并在应用运行时动态请求。开发者应遵循权限申请最小化原则,提供清晰的权限使用理由。

权限管理

HarmonyOS基于TokenID管理应用权限,确保应用访问系统资源时遵循预先设定的规则。开发者可利用此机制实现应用权限的精细化管理。

嵌套滚动Demo

对于嵌套滚动,开发者需对接ArkUI框架的NestedScrollMode枚举类型。通过设置nestedScroll接口的scrollForward和scrollBackward属性,可实现Web组件与上下层组件的嵌套滚动关系。具体Demo可参考官方文档或开发者社区提供的示例代码。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部