鸿蒙5开发宝藏案例分享---快捷触达的骑行体验

简介: 本文详细解析了基于鸿蒙系统实现共享单车“丝滑”骑行体验的代码,涵盖三大核心模块:扫码直达解锁页(Scan Kit)、智能找车与步行导航(Map Kit)、以及实况窗展示骑行状态(Live View Kit)。通过 TypeScript/ArkTS 实现扫码跳转、地图路径规划和实时状态更新等功能,确保从扫码到支付全流程极简、实时、无感。重点包括权限管理、坐标转换、路径绘制及实况窗动态更新等技术细节。

# 鸿蒙宝藏案例详解:共享单车“丝滑”骑行体验的代码实现 🚲💻

大家好!上次分享了鸿蒙那个超棒的共享单车体验案例,很多朋友留言说想看代码细节。没问题!这就带大家深入代码层,看看那些“丝滑”的体验(扫码直达、实时状态窗、路径规划)到底是怎么敲出来的。官方文档有时像藏宝图,代码才是真金白银!

> **核心目标再强调:** 用HarmonyOS的`Scan Kit`(扫码直达)、`Map Kit`(找车导航)、`Live View Kit`(实况窗)三大能力,把扫码->解锁->骑行->还车->支付的流程做到**极简、实时、无感**。

## 🎯 模块一:扫码直达解锁页 (Scan Kit)

**目标:** 用户在任何地方扫码,直接跳转到该单车的解锁确认页,跳过打开App、找入口的步骤。

### 关键代码详解 (TypeScript/ArkTS)

```

// 1. 导入关键模块

import scanBarcode from '@ohos.abilityAccessCtrl'; // Scan Kit核心模块

import { router } from '@kit.ArkUI'; // 页面路由模块

import { BusinessError } from '@kit.BasicServicesKit'; // 错误处理

// 2. 扫码工具类 (ScanUtil.ts)

export class ScanUtil {

 public static scan(obj: Object): void {

   // 3. 配置扫码选项:支持所有类型码(ALL)和一维码(ONE_D_CODE),允许多码识别,允许从相册选图

   let options: scanBarcode.ScanOptions = {

     scanTypes: [scanBarcode.ScanType.ALL, scanBarcode.ScanType.ONE_D_CODE],

     enableMultiMode: true,

     enableAlbum: true

   };

   try {

     // 4. 启动扫码并等待结果 (异步Promise)

     scanBarcode.startScanForResult(getContext(obj), options)

       .then((result: scanBarcode.ScanResult) => {

         console.info('扫码结果:', JSON.stringify(result));

       

         // 5. 关键逻辑:判断扫码类型 (假设CyclingConstants.SCAN_TYPE代表单车码)

         if (result.scanType === CyclingConstants.SCAN_TYPE) {

           // 6. 设置应用状态:等待解锁 (AppStorage是鸿蒙的状态管理)

           AppStorage.setOrCreate(CyclingConstants.CYCLING_STATUS, CyclingStatus.WAITING_UNLOCK);

         

           // 7. 核心跳转!直接路由到解锁确认页 'pages/ConfirmUnlock'

           router.pushUrl({ url: 'pages/ConfirmUnlock' });

           // 通常这里会把扫码得到的数据(如单车ID)通过params传递给ConfirmUnlock页面

         }

       })

       .catch((error: BusinessError) => {

         console.error('扫码出错:', JSON.stringify(error));

         // 处理错误:如提示用户、重试等

       });

   } catch (error) {

     console.error('启动扫码失败:', JSON.stringify(error));

   }

 }

}

```

**代码解析 & 关键点:**

1.  **权限申请 (`module.json5`):** 扫码必须的相机权限!**必须在配置文件声明**:

   ```

   "requestPermissions": [

     {

       "name": "ohos.permission.CAMERA",

       "reason": "用于扫描共享单车二维码", // 给用户看的理由

       "usedScene": {

         "abilities": ["EntryAbility"], // 在哪个Ability申请

         "when": "always" // 使用时机

       }

     }

   ],

   ```

1.  **`ScanOptions` 配置灵活:**

   -   `scanTypes`: 指定识别的码类型,非常灵活。

   -   `enableMultiMode`: 是否一次扫多个码(共享单车通常不需要,关掉更快)。

   -   `enableAlbum`: 是否允许从相册选择二维码图片(重要!用户可能截图扫码)。

1.  **`startScanForResult`:** 这是启动扫码的核心API,返回一个`Promise`。`.then()`里处理成功结果,`.catch()`处理失败。

1.  **结果处理 (`result`):**

   -   `result.scanType`: 识别出的码类型(二维码?条形码?)。

   -   `result.value`: 扫码得到的数据字符串(通常包含单车唯一ID、解锁指令等)。**这个例子简化了,实际业务中这里会解析`result.value`获取单车信息!**

1.  **状态管理 (`AppStorage`):** 鸿蒙提供的应用级状态管理。这里设置`CYCLING_STATUS = WAITING_UNLOCK`,告诉应用“用户扫到码了,等待确认解锁”。这个状态会被解锁页面使用。

1.  **`router.pushUrl`:** **实现“直达”的关键!** 直接路由导航到解锁确认页`pages/ConfirmUnlock`。用户瞬间从扫码界面跳到了解锁按钮面前,省去所有中间步骤。通常会把单车ID等信息通过`params`传递过去:`router.pushUrl({ url: 'pages/ConfirmUnlock', params: { bikeId: parsedBikeId } })`。

**调用时机:** 在你的首页(`Index`)、共享单车功能页(`BikePage`),甚至一个桌面万能卡片(`Card`)的按钮点击事件里,调用`ScanUtil.scan(this)`即可触发扫码。

* * *

## 🗺️ 模块二:智能找车与步行导航 (Map Kit)

**目标:** 在“找车”页面,显示用户位置、车辆位置,并绘制步行路线。

### 关键代码详解 (地图初始化、定位、路径规划与绘制)

```

// 1. 导入关键模块

import { MapComponent, mapCommon, map, navi } from '@kit.MapKit'; // 地图核心

import geoLocationManager from '@ohos.geoLocationManager'; // 定位管理

import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; // 权限申请

import { BusinessError } from '@kit.BasicServicesKit';

// 2. 在找车页面 (FindBikePage.ets)

@Entry

@Component

struct FindBikePage {

 // ... 其他状态变量 ...

 private mapController?: map.MapComponentController; // 地图控制器

 private mapPolyline?: map.MapPolyline; // 用于绘制路线的线对象

 private myPosition: mapCommon.LatLng = { latitude: 0, longitude: 0 }; // 用户位置

 aboutToAppear(): void {

   // 3. 初始化地图回调

   this.callback = async (err, mapController) => {

     if (!err) {

       this.mapController = mapController;

       this.mapController.on('mapLoad', async () => {

         // 4. 检查并申请定位权限

         const hasPerm = await this.checkLocationPermissions();

         if (hasPerm) {

           this.enableMyLocation(); // 开启定位并获取位置

         }

       });

     }

   };

 }

 // 5. 检查定位权限

 private async checkLocationPermissions(): Promise<boolean> {

   const atManager = abilityAccessCtrl.createAtManager();

   try {

     const permissions = [

       'ohos.permission.LOCATION',

       'ohos.permission.APPROXIMATELY_LOCATION'

     ];

     const grantStatus = await atManager.checkAccessToken(

       abilityAccessCtrl.AccessTokenID.BASE,

       permissions

     );

     return grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;

   } catch (error) {

     console.error('检查权限出错', error);

     return false;

   }

 }

 // 6. 申请定位权限

 private requestPermissions(): void {

   const atManager = abilityAccessCtrl.createAtManager();

   atManager.requestPermissionsFromUser(

     getContext(this) as common.UIAbilityContext,

     ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION']

   ).then(() => {

     this.enableMyLocation(); // 权限获取成功,开启定位

   }).catch((err: BusinessError) => {

     console.error('申请权限失败', err.code, err.message);

   });

 }

 // 7. 开启定位并获取当前位置

 private enableMyLocation(): void {

   if (!this.mapController) return;

   // 7.1 设置地图显示我的位置

   this.mapController.setMyLocationEnabled(true);

   this.mapController.setMyLocationControlsEnabled(true); // 显示定位按钮

   // 7.2 配置定位请求参数 (高精度、首次定位)

   let requestInfo: geoLocationManager.CurrentLocationRequest = {

     priority: geoLocationManager.LocationRequestPriority.FIRST_FIX,

     scenario: geoLocationManager.LocationRequestScenario.NAVIGATION,

     maxAccuracy: 50 // 精度要求(米)

   };

   // 7.3 获取当前位置

   geoLocationManager.getCurrentLocation(requestInfo)

     .then(async (location) => {

       console.info('获取到位置:', location.latitude, location.longitude);

       // 7.4 坐标转换 (WGS84 -> 国内常用的GCJ02)

       let mapPosition: mapCommon.LatLng = await map.convertCoordinate(

         mapCommon.CoordinateType.WGS84,

         mapCommon.CoordinateType.GCJ02,

         { latitude: location.latitude, longitude: location.longitude }

       );

       // 7.5 存储用户位置 & 移动地图视角

       this.myPosition = mapPosition;

       AppStorage.setOrCreate('userLat', mapPosition.latitude);

       AppStorage.setOrCreate('userLon', mapPosition.longitude);

       let cameraUpdate = map.newCameraPosition({

         target: mapPosition,

         zoom: 16 // 放大到合适级别

       });

       this.mapController?.animateCamera(cameraUpdate, 1000); // 1秒动画移动到用户位置

     })

     .catch((err: BusinessError) => {

       console.error('获取位置失败', err.code, err.message);

     });

 }

 // 8. 监听地图点击 (用户点选单车位置)

 private setupMapListeners(): void {

   this.mapController?.on('mapClick', async (clickedPosition: mapCommon.LatLng) => {

     // 8.1 清除旧标记和路线

     this.mapController?.clear();

     this.mapPolyline?.remove();

     // 8.2 在点击位置添加一个标记 (Marker)

     this.marker = await MapUtil.addMarker(clickedPosition, this.mapController);

     // 8.3 关键!发起步行路径规划 (从用户位置this.myPosition 到 点击位置clickedPosition)

     const walkingRoutes = await MapUtil.walkingRoutes(clickedPosition, this.myPosition);

     if (walkingRoutes && walkingRoutes.routes.length > 0) {

       // 8.4 绘制规划好的步行路线

       await MapUtil.paintRoute(walkingRoutes, this.mapPolyline, this.mapController);

     }

   });

 }

 build() {

   Column() {

     // 9. 集成地图组件 (核心UI)

     MapComponent({

       mapOptions: { ... }, // 地图初始配置 (中心点、缩放级别等)

       mapCallback: this.callback // 地图加载完成的回调

     })

     .onClick(() => {

       this.setupMapListeners(); // 通常在地图加载后设置监听

     })

     .width('100%')

     .height('100%')

   }

 }

}

// 10. 路径规划工具类 (MapUtil.ts)

export class MapUtil {

 // 10.1 步行路径规划

 public static async walkingRoutes(

   destination: mapCommon.LatLng,

   origin?: mapCommon.LatLng

 ): Promise<navi.RouteResult | undefined> {

   if (!origin) return undefined;

   let params: navi.RouteParams = {

     origins: [origin], // 起点数组 (这里一个)

     destination: destination, // 终点

     type: navi.RouteType.WALKING, // 步行模式

     language: 'zh_CN' // 中文结果

   };

   try {

     const result = await navi.getWalkingRoutes(params); // 调用Map Kit API

     console.info('步行路线规划成功', JSON.stringify(result));

     return result;

   } catch (err) {

     console.error('步行路线规划失败', JSON.stringify(err));

     return undefined;

   }

 }

 // 10.2 绘制路线到地图

 public static async paintRoute(

   routeResult: navi.RouteResult,

   mapPolyline: map.MapPolyline | undefined,

   mapController?: map.MapComponentController

 ) {

   if (!mapController || !routeResult.routes[0]?.overviewPolyline) return;

   // 清除旧线

   mapPolyline?.remove();

   // 配置新线的样式 (蓝色,20像素宽)

   let polylineOption: mapCommon.MapPolylineOptions = {

     points: routeResult.routes[0].overviewPolyline, // 路线坐标点数组

     clickable: true,

     width: 20,

     color: 0xFF2970FF, // ARGB 蓝色

     zIndex: 10

   };

   // 添加折线到地图并保存引用

   mapPolyline = await mapController.addPolyline(polylineOption);

   return mapPolyline;

 }

 // ... (addMarker 方法类似) ...

}

```

**代码解析 & 关键点:**

1.  **权限 (`module.json5`):** 定位权限同样**必须声明**:

   ```

   "requestPermissions": [

     {

       "name": "ohos.permission.LOCATION",

       "reason": "用于查找附近的共享单车和导航"

     },

     {

       "name": "ohos.permission.APPROXIMATELY_LOCATION",

       "reason": "用于更精准的找车定位"

     }

   ],

   ```

1.  **`MapComponent`:** 地图的UI组件。`mapCallback` 在地图**加载完成**后触发,此时才能安全地获取`mapController`进行操作。

1.  **定位流程 (`enableMyLocation`):**

   -   `setMyLocationEnabled(true)`: 让地图显示用户位置蓝点。

   -   `getCurrentLocation`: 获取**一次**精确位置。对于持续追踪,需用`on('locationChange')`监听。

   -   **坐标转换 (`convertCoordinate`):** **非常重要!** 设备GPS返回的是WGS84坐标,国内地图服务(如GCJ02)需要转换才能准确显示。

1.  **路径规划 (`getWalkingRoutes`):**

   -   调用 `navi.getWalkingRoutes(params)` 是核心。传入起点(`origins`)、终点(`destination`)、类型(`WALKING`)。

   -   返回的 `RouteResult` 包含路线信息,其中 `overviewPolyline` 是**一串压缩过的经纬度点**,用于绘制路线。

1.  **绘制路线 (`addPolyline`):**

   -   使用 `mapController.addPolyline(options)` 绘制折线。

   -   `options.points` 传入路线规划得到的坐标点数组 (`overviewPolyline` 需要先解码,示例代码假设`MapUtil.walkingRoutes`内部或返回结果已处理)。

   -   通过 `width`, `color` 等属性定制路线外观。

1.  **交互流程:** 用户点击地图 -> 获取点击点坐标 -> 清除旧数据 -> 添加新Marker -> 规划并绘制到该Marker的步行路线。

* * *

## ✨ 模块三:实况窗展示骑行状态 (Live View Kit)

**目标:** 解锁后,在状态栏(胶囊)、通知中心、锁屏实时显示骑行状态/时长/费用;还车后变待支付;支付后结束。

### 关键代码详解 (创建、更新、销毁实况窗)

```

// 1. 导入关键模块

import liveViewManager, { LiveViewDataBuilder, TextLayoutBuilder, TextCapsuleBuilder, LiveNotification, LiveViewContext } from '@kit.LiveViewKit';

import { BusinessError } from '@kit.BasicServicesKit';

import wantAgent from '@ohos.app.ability.wantAgent'; // 用于定义点击动作

// 2. 实况窗控制类 (LiveViewController.ts)

export class LiveViewController {

 private liveViewData?: liveViewManager.LiveViewData; // 当前实况窗数据

 private liveNotification?: LiveNotification; // 实况窗通知对象

 // 3. 创建并显示实况窗 (在用户点击"解锁"后调用)

 public async startLiveView(context: LiveViewContext): Promise<liveViewManager.LiveViewResult> {

   // 3.1 构建默认的实况窗数据 (骑行中状态)

   this.liveViewData = await this.buildDefaultView(context);

 

   // 3.2 创建LiveNotification对象 (关联环境信息,如业务类型'RENT')

   let env: liveViewManager.LiveViewEnvironment = { id: 0, event: 'RENT' };

   this.liveNotification = LiveNotification.from(context, env);

 

   // 3.3 创建并显示实况窗!

   return await this.liveNotification.create(this.liveViewData);

 }

 // 4. 构建默认骑行中状态的实况窗数据

 private static async buildDefaultView(context: LiveViewContext): Promise<liveViewManager.LiveViewData> {

   // 4.1 构建展开态卡片布局 (锁屏/通知中心看到的卡片)

   const layoutData = new TextLayoutBuilder()

     .setTitle('骑行中') // 卡片标题

     .setContent('已骑行 0 分钟') // 卡片内容 (初始0分钟)

     .setDescPic('bike_icon.png'); // 卡片右侧图标

   // 4.2 构建胶囊态 (状态栏看到的小胶囊)

   const capsule = new TextCapsuleBuilder()

     .setIcon('bike_small.png') // 胶囊图标

     .setBackgroundColor('#FF00FF00') // 胶囊背景色 (绿色)

     .setTitle('骑行中'); // 胶囊文字

   // 4.3 构建点击动作 (点击实况窗跳转回App的骑行页面)

   const wantAgentInfo: wantAgent.WantAgentInfo = {

     wants: [

       {

         bundleName: context.bundleName,

         abilityName: 'EntryAbility',

         parameters: { route: 'pages/RidingPage' } // 跳转到骑行页

       }

     ],

     operationType: wantAgent.OperationType.START_ABILITY,

     requestCode: 0

   };

   const wantAgentObj = await wantAgent.getWantAgent(wantAgentInfo);

   // 4.4 构建完整的LiveViewData

   const liveViewData = new LiveViewDataBuilder()

     .setTitle('骑行中') // 主标题

     .setContentText(['已骑行 0 分钟']) // 内容文本数组 (可多行)

     .setContentColor('#FFFFFFFF') // 内容文字颜色 (白色)

     .setLayoutData(layoutData) // 设置卡片布局

     .setCapsule(capsule) // 设置胶囊样式

     .setWant(wantAgentObj) // 设置点击动作

     // (可选) 配置锁屏沉浸态扩展Ability (见后面)

     .setLiveViewLockScreenAbilityName('LiveViewLockScreenExtAbility')

     .setLiveViewLockScreenPicture('bike_lock_icon.png')

     .build(); // 构建完成

   return liveViewData;

 }

 // 5. 更新实况窗状态 (骑行中 -> 待支付 -> 支付完成)

 public async updateLiveView(status: number, context: LiveViewContext): Promise<liveViewManager.LiveViewResult> {

   if (!this.liveViewData || !this.liveNotification) {

     console.error('实况窗未创建或数据为空');

     return { code: -1 };

   }

   switch (status) {

     case CyclingStatus.RIDING: // 骑行中 (更新计时)

       // ... 更新 this.liveViewData 的计时文本 (e.g., '已骑行 5 分钟') ...

       return await this.liveNotification.update(this.liveViewData);

     

     case CyclingStatus.WAITING_PAYMENT: // 还车成功,待支付

       // 5.1 更新标题、内容、胶囊文字

       this.liveViewData.primary.title = '待支付';

       this.liveViewData.primary.content = [{ text: '骑行结束,点击支付', textColor: '#FFFFFFFF' }];

       this.liveViewData.capsule.title = '待支付';

       // 5.2 更新点击动作 (点击跳转到支付页)

       this.liveViewData.primary.clickAction = await this.buildWantAgent(context, 'pages/PaymentPage');

       // 5.3 更新卡片布局

       this.liveViewData.primary.layoutData = new TextLayoutBuilder()

         .setTitle('待支付')

         .setContent('费用:¥2.50')

         .setDescPic('payment_icon.png');

       return await this.liveNotification.update(this.liveViewData);

     

     case CyclingStatus.PAYMENT_COMPLETED: // 支付完成

       // 5.4 更新为最终状态

       this.liveViewData.primary.title = '支付成功';

       this.liveViewData.primary.content = [{ text: '行程已完成,感谢使用', textColor: '#FFFFFFFF' }];

       this.liveViewData.capsule.title = '完成';

       // 5.5 关键!停止实况窗 (显示最终状态几秒后消失)

       return await this.liveNotification.stop(this.liveViewData);

     

     default:

       return { code: -1 };

   }

 }

 // ... (buildWantAgent 辅助方法) ...

}

// 6. 锁屏沉浸态实况窗扩展Ability (LiveViewLockScreenExtAbility.ets)

import { LiveViewLockScreenExtensionAbility, UIExtensionContentSession } from '@kit.LiveViewKit';

import hilog from '@ohos.hilog';

export default class LiveViewLockScreenExtAbility extends LiveViewLockScreenExtensionAbility {

 onSessionCreate(want: Want, session: UIExtensionContentSession) {

   hilog.info(0x0000, 'LiveViewLock', '锁屏扩展Ability创建会话');

   // 6.1 加载自定义的锁屏实况窗UI页面

   session.loadContent('pages/LiveViewLockScreenPage'); // 这个页面你用ArkUI自己设计!

 }

 // ... (其他生命周期方法 onForeground, onBackground, onDestroy) ...

}

```

**代码解析 & 关键点:**

1.  **`LiveViewDataBuilder`:** 构建实况窗数据的核心工具。它定义了:

   -   **主信息 (`primary`):** 标题、内容文本/颜色、点击动作(`WantAgent`)、卡片布局(`LayoutData`)、锁屏扩展能力名/参数/图片。

   -   **胶囊态 (`capsule`):** 状态栏显示的图标、背景色、文字。

   -   **其他:** 显示时长(`keepTime`)、是否持久化等。

1.  **状态管理:** 实况窗内容不是静态的!`updateLiveView` 方法根据业务状态 (`RIDING`, `WAITING_PAYMENT`, `PAYMENT_COMPLETED`) **动态更新** `liveViewData` 的各个部分,然后调用 `update()` 或 `stop()` 刷新界面。

1.  **`WantAgent`:** **实现点击交互的关键!** 定义了用户点击实况窗(胶囊或卡片)后要执行的动作。最常见的就是跳转回App的特定页面(如骑行页、支付页)。`wantAgent` 模块用于构建这个意图。

1.  **`LiveNotification`:** 负责实况窗的生命周期管理 (`create`, `update`, `stop`)。`.from(context, env)` 将实况窗与特定的业务环境(`env`)关联起来。

1.  **沉浸态锁屏实况窗 (高级):**

   -   在 `LiveViewDataBuilder` 中配置 `setLiveViewLockScreenAbilityName` 和 `setLiveViewLockScreenPicture`。

   -   实现一个继承自 `LiveViewLockScreenExtensionAbility` 的Ability。

   -   在 `onSessionCreate` 方法中,使用 `session.loadContent('你的自定义UI页面路径')` 加载你用ArkUI编写的**自定义锁屏卡片界面**。这让你可以展示比默认模板更丰富的信息(比如地图缩略图、更详细的费用明细)。

   -   **声明扩展Ability (`module.json5`):**

       ```

       "extensionAbilities": [

         {

           "name": "LiveViewLockScreenExtAbility",

           "type": "liveViewLockScreen", // 类型必须为liveViewLockScreen

           "srcEntry": "./ets/entryability/LiveViewLockScreenExtAbility.ets",

           "exported": true // 允许系统访问

         }

       ],

       ```

1.  **服务开通:** 使用实况窗能力**前**,需要在 `AppGallery Connect` 后台为你的应用开通 `Live View Kit` 服务权益。

* * *

## 📌 总结与思考

把这三块核心代码串起来,就构成了那个“丝滑”骑行体验的骨架:

1.  **`ScanUtil.scan()`** 被调用 -> 扫码成功 -> `router.pushUrl` 直达解锁页。

1.  用户点击解锁 -> 调用 **`LiveViewController.startLiveView()`** 创建实况窗 (显示骑行中)。

1.  骑行中

相关文章
|
20天前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
142 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
26天前
|
移动开发 前端开发 Android开发
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
209 12
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
5月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:外卖App
仓颉语言实战分享,教你如何用仓颉开发外卖App界面。内容包括页面布局、导航栏自定义、搜索框实现、列表模块构建等,附完整代码示例。轻松掌握Scroll、List等组件使用技巧,提升HarmonyOS应用开发能力。
|
28天前
|
移动开发 Rust JavaScript
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
436 3
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
4月前
|
安全 JavaScript API
鸿蒙开发核心要素
鸿蒙开发核心要素
|
25天前
|
移动开发 Android开发
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
90 0
|
5月前
|
存储 IDE 定位技术
【HarmonyOS 5】鸿蒙组件&模板服务详解 - 助力高效开发的利器
在移动应用开发领域,效率与质量始终是开发者追求的核心目标。鸿蒙系统作为新兴的操作系统,为开发者提供了丰富且强大的开发资源,其中鸿蒙组件&模板服务更是成为开发者快速构建高质量应用的得力助手。
166 0
HarmonyOS NEXT仓颉开发语言实战案例:电影App
周末好!本文分享使用仓颉语言重构ArkTS实现的电影App案例,对比两者在UI布局、组件写法及语法差异。内容包括页面结构、列表分组、分类切换与电影展示等。通过代码演示仓颉在HarmonyOS开发中的应用。##仓颉##ArkTS##HarmonyOS开发
|
5月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:健身App
本期分享一个健身App首页的布局实现,顶部采用Stack容器实现重叠背景与偏移效果,列表部分使用List结合Scroll实现可滚动内容。代码结构清晰,适合学习HarmonyOS布局技巧。
HarmonyOS NEXT仓颉开发语言实战案例:小而美的旅行App
本文分享了一个旅行App首页的设计与实现,使用List容器搭配Row、Column布局完成个人信息、功能列表及推荐模块的排版,详细展示了HarmonyOS下的界面构建技巧。

热门文章

最新文章