服务卡片提供了一种界面展示形式,可以将应用的重要信息或操作前置到服务卡片以达到服务直达、减少跳转层级的体验效果。有些类似于创建了一种 “快键方式”,比如下面的卡片流程图:
- 添加基础卡片
右键入口模块按照图示新增卡片
ArkTS卡片创建完成后,工程中会新增如下卡片相关文件:卡片生命周期管理文件(PhoneFormAbility.ets)、卡片页面文件(WidgetCard.ets)和卡片配置文件(form_config.json)
长按应用图标会出现Hello Word 基础卡片,代表卡片添加成功 - 修改卡片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%')}
}
点击卡片唤起特定页
基础实现原理
- 卡片组件点击之后通过postCardAction触发router事件并携带参数
- 在应用的UIAbility中接收router事件,解析参数完成跳转
准备落地页
视图features/my/src/main/ets/views/RecommendView.ets
import { router } from '@kit.ArkUI'
import { MkNavBar } from '@mk/basic'interface HotGoodsType {id: stringname: stringprice: stringpicture: 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@BuildertopBanner() {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()@BuilderswiperContainer() {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) {// 根据设置的页面 拉起对应的 Pagethis.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 abilityhilog.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).topRectAppStorage.setOrCreate<number>('safeTop', px2vp(top.height))const bottom = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR).bottomRectAppStorage.setOrCreate<number>('safeBottom', px2vp(bottom.height))}});}onWindowStageDestroy(): void {// Main window is destroyed, release UI related resourceshilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');}onForeground(): void {// Ability has brought to foregroundhilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');}onBackground(): void {// Ability has back to backgroundhilog.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 卡片可以使用 和应用通信的一个 apipostCardAction(this, {action: 'router', // 通信的方式 是 routerabilityName: 'EntryAbility', // ability 的名字,当前应用写这个即可params: { targetPage: 'pages/RecommendPage' } // 传递的参数});})}.linearGradient({angle: '135',colors: [['#FD3F8F', 0],['#FF773C', 1]]}).height('100%')}
}