HarmonyOS -服务卡片

服务卡片提供了一种界面展示形式,可以将应用的重要信息或操作前置到服务卡片以达到服务直达、减少跳转层级的体验效果。有些类似于创建了一种 “快键方式”,比如下面的卡片流程图:在这里插入图片描述

  1. 添加基础卡片
    右键入口模块按照图示新增卡片
    在这里插入图片描述在这里插入图片描述在这里插入图片描述ArkTS卡片创建完成后,工程中会新增如下卡片相关文件:卡片生命周期管理文件(PhoneFormAbility.ets)、卡片页面文件(WidgetCard.ets)和卡片配置文件(form_config.json)在这里插入图片描述
    长按应用图标会出现Hello Word 基础卡片,代表卡片添加成功在这里插入图片描述
  2. 修改卡片UI
    卡片的UI可以像一个一般组件一样进行基础布局,很方便就可以做定制修改,打开WidgetCard.ets文件进行修改即可
/*
 1. The title.
 */
readonly TITLE: string = '我是一个卡片';

在这里插入图片描述
3.定制样式
卡片的样式定制和普通组件的写法没有任何区别,按照实际需要进行UI改写即可
在这里插入图片描述

@Entry()
@Component
struct WidgetCard {
  fileNameList: string[] = new Array(2).fill('')

  build() {
    Column() {
      Row() {
        Text('今日推荐')
          .fontColor('#fff')
          .fontSize(16)
      }
      .height(40)
      .width('100%')
      .justifyContent(FlexAlign.Center)

      Row() {
        ForEach(
          this.fileNameList,
          (url: string) => {
            Row() {
              Image('')
                .borderRadius(12)
                .width(50)
                .aspectRatio(1)
            }
            .backgroundColor('#eee')
            .borderRadius(12)
          }
        )
      }
      .justifyContent(FlexAlign.SpaceBetween)
      .width('100%')
      .layoutWeight(1)
      .padding({
        left: 20,
        right: 20
      })
      .backgroundColor('#fff')
      .borderRadius({
        topRight: 16,
        topLeft: 16
      })
      .onClick(() => {

      })
    }
    .linearGradient({
      angle: '135',
      colors: [
        ['#FD3F8F', '0%'],
        ['#FF773C', '100%']
      ]
    })
    .height('100%')
  }
}

点击卡片唤起特定页

在这里插入图片描述
基础实现原理

  1. 卡片组件点击之后通过postCardAction触发router事件并携带参数
  2. 在应用的UIAbility中接收router事件,解析参数完成跳转在这里插入图片描述
    准备落地页
    视图features/my/src/main/ets/views/RecommendView.ets
import { router } from '@kit.ArkUI'
import { MkNavBar } from '@mk/basic'

interface HotGoodsType {
  id: string
  name: string
  price: string
  picture: string
}

@Entry
@Component
export struct RecommendView {
  build() {
    Column() {
      Column() {
        this.topBanner()
        this.swiperContainer()
      }
      .height('80%')
      .justifyContent(FlexAlign.Start)
      .linearGradient({
        angle: 180,
        colors: [
          ['#51E1F8', 0],
          ['#F5F4F9', 1]
        ]
      })

      Row() {
        Row() {
          Text('换一批看看')
            .fontSize(16)
            .fontColor('#fff')
        }
        .width('100%')
        .height(46)
        .justifyContent(FlexAlign.Center)
        .backgroundColor('#00C6C6')
        .onClick(() => {

        })
      }
      .width('100%')
      .padding({
        left: 16,
        right: 16
      })
      .layoutWeight(1)
      .backgroundColor('#F5F4F9')
    }
  }

  // 顶部banner
  @Builder
  topBanner() {
    Stack({ alignContent: Alignment.TopStart }) {
      Image($r("app.media.ic_public_hot_banner_bg"))
        .width('100%')
      MkNavBar({
        bg: Color.Transparent,
        leftClickHandler: () => {
          router.replaceUrl({
            url: 'pages/Index'
          })
        }
      })
    }
  }

  // 轮播图
  swiperController: SwiperController = new SwiperController()

  @Builder
  swiperContainer() {
    Column() {
      Swiper(this.swiperController) {
        ForEach(
          new Array<HotGoodsType>(3).fill({
            id: '1001',
            name: 'PLAZA STYLE estaa 珍珠款毛绒绒保暖套装【含…',
            price: '289',
            picture: 'http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-05/6fdcac19-dd44-442c-9212-f7ec3cf3ed18.jpg'
          }),
          (item: HotGoodsType) => {
            Column() {
              Image(item.picture)
                .width('100%')

              Text(item.name)
                .margin({
                  top: 10
                })
                .fontSize(14)
                .fontColor('#434343')

              Text(`${item.price}`)
                .margin({
                  top: 10
                })
                .fontSize(20)
                .fontColor('#191919')
                .fontWeight(700)
            }
            .padding(30)
            .backgroundColor('#fff')
          }
        )
      }
      .width(270)
      .borderRadius(16)
      .indicator(
        new DotIndicator()
          .itemWidth(6)
          .itemHeight(6)
          .selectedItemWidth(6)
          .selectedItemHeight(6)
          .color('#E9E8EC')
          .selectedColor('#191919')
      )
    }
    .margin({
      top: -60
    })
  }
}

页面products/phone/src/main/ets/pages/RecommendPage

import { RecommendView } from '@mk/my'

@Entry
@Component
struct RecommendPage {
  build() {
    Column() {
      RecommendView()
    }
    .width('100%')
    .height('100%')
  }
}

跳转打开落地页

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { promptAction, window } from '@kit.ArkUI';
import { Log } from '@abner/log';
import { deviceInfo } from '@kit.BasicServicesKit';
import { auth } from '@mk/basic';

export default class EntryAbility extends UIAbility {
  // 保存 传递的信息
  private selectPage: string = ''
  // 保存当前的  WindowStage 对象
  private currentWindowStage: window.WindowStage | null = null;

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
    
    if (want.parameters !== undefined) {
      let params: Record<string, string> = JSON.parse(JSON.stringify(want.parameters));
      this.selectPage = params.targetPage;
      Log.info(this.selectPage)
    }

    Log.info('onCreate-run')
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // hilog.info(DOMAIN_NUMBER, TAG, `onNewWant Want: ${JSON.stringify(want)}`);
    // Log.info('onNewWant-run')
    if (want.parameters?.params !== undefined) {
      let params: Record<string, string> = JSON.parse(JSON.stringify(want.parameters));
      this.selectPage = params.targetPage;
    }
    // 如果 window 对象存在, 人为的调用 onWindowStageCreate 传入 window 对象
    if (this.currentWindowStage !== null) {
      // 根据设置的页面 拉起对应的 Page
      this.onWindowStageCreate(this.currentWindowStage);
    }
  }

  onDestroy(): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    // const pageName = this.selectPage != '' ? this.selectPage : 'pages/Index';

    // 保存 window 对象 后续 通过该对象 拉起指定的页面
    if (this.currentWindowStage === null) {
      // 第一次启动 保存
      this.currentWindowStage = windowStage
    }

    windowStage.loadContent(this.selectPage || '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.');

      // 清空 传递进来的 页面名称,避免重复打开即可
      this.selectPage = ''

      // 2in1设备,不需要全屏
      if (deviceInfo.deviceType != '2in1') {
        // 开启全屏
        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))
      }
    });
  }

  onWindowStageDestroy(): void {
    // Main window is destroyed, release UI related resources
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
    // Ability has brought to foreground
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
  }

  onBackground(): void {
    // Ability has back to background
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
  }
}

products\phone\src\main\ets\widget\pages\WidgetCard.ets

@Entry
@Component
struct WidgetCard {
  /*
   * The title.
   */
  readonly TITLE: string = '我是一个卡片';
  /*
   * The action type.
   */
  readonly ACTION_TYPE: string = 'router';
  /*
   * The ability name.
   */
  readonly ABILITY_NAME: string = 'EntryAbility';
  /*
   * The message.
   */
  readonly MESSAGE: string = 'add detail';
  /*
   * The width percentage setting.
   */
  readonly FULL_WIDTH_PERCENT: string = '100%';
  /*
   * The height percentage setting.
   */
  readonly FULL_HEIGHT_PERCENT: string = '100%';
  fileNameList: string[] = new Array(2).fill('')

  build() {
    Column() {
      Row() {
        Text('今日推荐')
          .fontColor('#fff')
          .fontSize(16)
      }
      .height(40)
      .width('100%')
      .justifyContent(FlexAlign.Center)

      Row() {
        ForEach(
          this.fileNameList,
          (url: string) => {
            Row() {
              Image('')
                .borderRadius(12)
                .width(50)
                .aspectRatio(1)
            }
            .backgroundColor('#eee')
            .borderRadius(12)
          }
        )
      }
      .justifyContent(FlexAlign.SpaceBetween)
      .width('100%')
      .layoutWeight(1)
      .padding({
        left: 20,
        right: 20
      })
      .backgroundColor('#fff')
      .borderRadius({
        topRight: 16,
        topLeft: 16
      })
      .onClick(() => {
        // postCardAction 卡片可以使用 和应用通信的一个 api
        postCardAction(this, {
          action: 'router', // 通信的方式 是 router
          abilityName: 'EntryAbility', // ability 的名字,当前应用写这个即可
          params: { targetPage: 'pages/RecommendPage' } // 传递的参数
        });
      })
    }
    .linearGradient({
      angle: '135',
      colors: [
        ['#FD3F8F', 0],
        ['#FF773C', 1]
      ]
    })
    .height('100%')
  }
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值