## 目录 1. [引言一屏多卡的意义](#1-引言一屏多卡的意义) 2. [displayCount 属性详解](#2-displaycount-属性详解) - [2.1 基本概念](#21-基本概念) - [2.2 宽度计算公式](#22-宽度计算公式) - [2.3 不同数值的视觉效果](#23-不同数值的视觉效果) 3. [itemSpace卡片间距控制](#3-itemspace卡片间距控制) 4. [实战8 张电影推荐卡片轮播](#4-实战8-张电影推荐卡片轮播) 5. [代码逐段解析](#5-代码逐段解析) - [5.1 数据模型8 种配色方案](#51-数据模型8-种配色方案) - [5.2 CardView渐变色卡片的叠层构建](#52-cardview渐变色卡片的叠层构建) - [5.3 build()displayCount itemSpace 核心配置](#53-builddisplaycount--itemspace-核心配置) 6. [displayCount 与常规 Swiper 的对比](#6-displaycount-与常规-swiper-的对比) 7. [常见问题与解决方案](#7-常见问题与解决方案) 8. [本系列七篇全览](#8-本系列七篇全览) 9. [总结](#9-总结) --- ## 1. 引言一屏多卡的意义 在移动应用中横向滚动卡片是一种极为常见的内容展示形式 | 应用 | 横向滚动场景 | displayCount 典型值 | |------|------------|-------------------| | **Apple Music** | 朋友正在听推荐列表 | 2.5 | | **Netflix** | 影片横向分类行 | 3.0~3.5 | | **App Store** | 今日推荐卡片 | 2.0~2.5 | | **淘宝** | 商品横向推荐 | 2.5~3.0 | | **Instagram** | 快拍/故事列表 | 3.5~4.0 | 这些场景的共同特征是**不希望用户一次只看一张卡片**。一屏展示多张卡片有三大好处 1. **空间效率**同样的屏幕空间展示更多内容信息密度更高 2. **视觉暗示**右侧露出的半张卡片告诉用户还可以滑 3. **对比浏览**用户可以同时看到相邻的几条内容快速决定是否滑动 HarmonyOS NEXT 的 Swiper 组件通过 displayCount 属性完美支持这种一屏多卡布局。 --- ## 2. displayCount 属性详解 ### 2.1 基本概念 displayCount 控制 Swiper 一屏一页显示多少个子项 ets Swiper() { ... } .displayCount(2.5) // 一屏显示 2.5 张卡片 **整数部分** —— 完整可见的卡片数量 **小数部分** —— 右侧露出的下一张卡片的宽度比例 - displayCount(1) 每次显示 1 张默认标准轮播 - displayCount(2) 每次显示 2 张完整卡片 - displayCount(2.5) 每次显示 2 张完整卡片 右半张预览 - displayCount(3) 每次显示 3 张完整卡片 ### 2.2 宽度计算公式 text 卡片宽度 (Swiper宽度 - (displayCount - 1) × itemSpace) / displayCount **实际计算**假设 Swiper 宽度 360vp | displayCount | itemSpace | 卡片宽度 | 说明 | |-------------|-----------|---------|------| | 1.0 | 0 | 360 | 标准轮播一屏一张 | | 2.0 | 12 | 174 | 两卡并排中间 12px 间距 | | **2.5** | **12** | **~136.8** | **两卡完整 右半张预览** | | 3.0 | 8 | ~114.7 | 三卡并排 | | 3.5 | 8 | ~98.3 | 三卡 半张预览 | **以 displayCount2.5, itemSpace12 为例** text 卡1 (完整) | 12px | 卡2 (完整) | 12px | 卡3 (半张可见) ───── 136.8 ───── ───── 136.8 ───── ── 68.4 ── ────────────────── 总宽 360 ───────────────────── ### 2.3 不同数值的视觉效果 | displayCount | 视觉感受 | 适用场景 | |-------------|---------|---------| | **1.0** | 全屏轮播焦点突出 | 首页大 Banner | | **1.5** | 右侧露出半张有下一页暗示 | 引导页 | | **2.0** | 两卡并排信息均衡 | 对比展示 | | **2.5** | 两卡 半张预览继续滑暗示强烈 | **推荐列表最常用** | | **3.0** | 三卡并排信息密度高 | 视频分类 / 图标网格 | | **3.5** | 密集展示适合小卡片 | 表情 / Emoji 选择器 | **2.5 是使用最广泛的配置**。它兼顾了内容可见性2 张完整卡片和可发现性半张预览是 Apple Music、Netflix 等主流应用的首选值。 --- ## 3. itemSpace卡片间距控制 itemSpace 设置相邻卡片之间的间距 ets Swiper() { ... } .displayCount(2.5) .itemSpace(12) // 卡片间距 12vp **itemSpace 的作用** | itemSpace | 视觉效果 | 适用场景 | |-----------|---------|---------| | 0 | 卡片紧贴无间隙 | 图片无缝拼接 | | 8 | 紧凑略有间隔 | 小卡片网格 | | **12** | **舒适明显分隔** | **通用卡片列表** | | 16 | 宽松呼吸感强 | 高端品牌展示 | | 24 | 非常宽松 | 大卡片精选推荐 | **itemSpace 在 displayCount 计算中的角色** itemSpace 不占用卡片的宽度而是消耗 Swiper 总宽度的一部分。itemSpace 越大每张卡片的可用宽度越小。 --- ## 4. 实战8 张电影推荐卡片轮播 ### 4.1 设计目标 创建一个类似Netflix 推荐行的横向滑动卡片列表一屏显示 2.5 张卡片8 张不同主题的电影卡片自动轮播。 ### 4.2 8 张卡片配色 | # | 电影 | 配色 | 色值 | |---|------|------|------| | 1 | 星际穿越 | 深蓝→蓝 | #0D47A1 → #1976D2 | | 2 | 悲剧之王 | 深紫→紫 | #4A148C → #7B1FA2 | | 3 | 马戏迷城 | 橙→金黄 | #E65100 → #FF8F00 | | 4 | 梵高之眼 | 深绿→绿 | #2E7D32 → #43A047 | | 5 | 波西米亚 | 深红→红 | #B71C1C → #E53935 | | 6 | 中土传奇 | 深棕→棕 | #3E2723 → #6D4C41 | | 7 | 机械纪元 | 深灰→灰 | #37474F → #607D8B | | 8 | 深海探秘 | 墨绿→青 | #004D40 → #00897B | 8 种配色覆盖蓝、紫、橙、绿、红、棕、灰、青色系滑动时色彩变化丰富。 ### 4.3 布局预览 ┌──────────────────────────────────────┐ │ 热门推荐 │ │ displayCount(2.5) 一屏多卡 │ │ │ │ ┌──────┐ ┌──────┐ ┌──── ┐ │ │ │ │ │ │ │ │ │ │ │星际穿│ │悲剧之│ │马戏迷│ ← 半张 │ │ │越 │ │王 │ │城 │ ← 预览 │ │ │科幻· │ │剧情· │ │奇幻· │ │ │ │冒险 │ │励志 │ │家庭 │ │ │ └──────┘ └──────┘ └──── ┘ │ │ ← 12px → ← 12px → │ │ ○ ● ○ ○ ○ ○ ○ ○ │ │ │ │ Swiper displayCount 多卡片布局 │ │ ● displayCount(2.5) │ │ ● itemSpace(12) │ │ ● 右半张预览 → 继续滑动 │ │ ● autoPlay 自动轮播 │ └──────────────────────────────────────┘ --- ## 5. 代码逐段解析 ### 5.1 数据模型8 种配色方案 ets interface CardInfo { emoji: string; // 电影图标 title: string; // 标题 category: string; // 分类 标签 gradientStart: string; // 渐变起点色 gradientEnd: string; // 渐变终点色 } private readonly cards: CardInfo[] [ { emoji: , title: 星际穿越, category: 科幻 · 冒险, gradientStart: #0D47A1, gradientEnd: #1976D2 }, { emoji: , title: 悲剧之王, category: 剧情 · 励志, gradientStart: #4A148C, gradientEnd: #7B1FA2 }, // ... 共 8 张 ]; **配色设计思路** 每张卡片使用 linearGradient 从深色到亮色的垂直渐变模拟电影海报的视觉质感。8 组配色覆盖了完整的色环确保滑动时色彩跳跃明显 蓝 → 紫 → 橙 → 绿 → 红 → 棕 → 灰 → 青 → 蓝循环 ### 5.2 CardView渐变色卡片的叠层构建 ets Builder private CardView(card: CardInfo) { Stack() { // 底层渐变背景 Column() .width(100%).height(100%).borderRadius(16) .linearGradient({ direction: GradientDirection.Bottom, colors: [[card.gradientStart, 0], [card.gradientEnd, 1]], }) // 中层底部半透明遮罩增强文字可读性 Column() .width(100%).height(80) .linearGradient({ direction: GradientDirection.Bottom, colors: [[#00000000, 0], [#00000066, 1]], }) .borderRadius({ bottomLeft: 16, bottomRight: 16 }) .position({ y: 140 }) // 上层图标 Text(card.emoji).fontSize(48).position({ x: 0, y: 20 }).width(100%) // 上层标题 分类底部对齐 Column() { Text(card.title).fontSize(17).fontWeight(FontWeight.Bold).fontColor(#FFFFFF) Text(card.category).fontSize(12).fontColor(#FFFFFF).opacity(0.8) } .position({ x: 14, y: 164 }) } .width(100%).height(100%).clip(true) } **叠层结构** ┌──────────────────────┐ │ │ ← 图标 (position y:20) │ │ │ │ │ │ │ ┌─────────────┐ │ ← 渐变遮罩 (position y:140, height:80) │ │ 星际穿越 │ │ │ │ 科幻 · 冒险 │ │ ← 文字 (position y:164) │ └─────────────┘ │ └──────────────────────┘ ↑ 渐变背景 (铺满) **底部遮罩的作用** 渐变背景上的白色文字在浅色背景下可能不易阅读。底部半透明黑色遮罩从完全透明到 40% 黑色确保了文字区域始终有足够的对比度同时不破坏渐变的美感。 ### 5.3 build()displayCount itemSpace 核心配置 ets build() { Scroll() { Column() { // 标题 Text( 热门推荐).fontSize(22).fontWeight(FontWeight.Bold) Swiper() { ForEach(this.cards, (card: CardInfo, idx: number) { Stack() { this.CardView(card) } .width(100%).height(100%) }, (card: CardInfo, idx: number): string idx.toString()) } .width(100%).height(220) .index(this.currentIndex) .displayCount(2.5) // ← 核心属性一屏显示 2.5 张 .itemSpace(12) // ← 核心属性卡片间距 12vp .autoPlay(true).interval(3000).loop(true).duration(500) .indicator(false) .onChange((index) { this.currentIndex index; }) // 指示器 说明卡片 Stack() { this.DotIndicator() }.margin({ top: 4 }) Stack() { this.FeatureCard() }.width(90%).margin({ top: 14 }) } } .backgroundColor(#F2F3F8) } **displayCount 与卡片子元素的 width 关系** 注意子元素使用了 .width(100%).height(100%)——它们的宽度由 Swiper 根据 displayCount 自动计算分配不需要也不应该手动设置固定宽度。 **Swiper 高度 220vp 的选择依据** 相比标准轮播通常 300vp 以上多卡片布局的卡片更小220vp 高度在手机竖屏下刚好显示 2.5 张卡片 底部信息无需用户滚动页面。 --- ## 6. displayCount 与常规 Swiper 的对比 | 对比维度 | 常规 SwiperdisplayCount1 | 多卡片布局displayCount2.5 | |---------|-----------------------------|------------------------------| | **一屏卡片数** | 1 张 | 2.5 张 | | **信息密度** | 低一张大图 | 高多张卡片 | | **右侧预览** | 无看不到下一张 | 有半张预览暗示可滑 | | **焦点** | 单卡片焦点集中 | 多卡片对比浏览 | | **滑动步长** | 1 张卡片 | 1 张卡片还是在同一个分页体系下 | | **适合场景** | 首页大 Banner | 推荐列表 / 商品展示 | | **卡片宽度** | 100% Swiper 宽度 | 由 displayCount 公式计算 | **滑动行为的一致性** 无论 displayCount 设置为多少Swiper 的滑动步长始终是一张卡片。也就是说从第 0 张滑到第 1 张内容会移动一个卡片的宽度不是半个 Swiper 宽度。 这使得 displayCount 更像是一个显示属性而非行为属性——它只改变一屏能看到多少不改变一次能滑多少。 --- ## 7. 常见问题与解决方案 ### 7.1 卡片显示不全 **现象**displayCount 设置后卡片右侧被裁剪。 **原因**卡片子元素设置了固定宽度覆盖了 Swiper 的自动分配。 **解决**子元素使用 .width(100%)不要设置固定宽度。 ets // ✅ 正确宽度由 Swiper 自动分配 Stack() { this.CardView(card) } .width(100%).height(100%) // ❌ 错误固定宽度会覆盖 Swiper 的计算 Stack() { this.CardView(card) } .width(150).height(220) ### 7.2 displayCount 取整问题 **现象**设置 displayCount(2.5) 但最终显示的不是精确的 2.5 张。 **原因**Swiper 宽度可能不是 itemSpace 和 displayCount 的整数倍Swiper 会在内部做适应性调整最终结果可能与理论值有 ±0.1 的偏差。 **解决方案**这不是 bug是正常的舍入行为。只要视觉上接近 2.5 即可。 ### 7.3 卡片间距不均匀 **现象**相邻卡片间距不一致。 **原因**itemSpace 是在 Swiper 侧设置的但 padding 是在卡片侧设置的两者叠加导致间距翻倍。 **解决**不要在卡片子元素上设置左右 padding/margin间距统一由 itemSpace 控制。 ### 7.4 displayCount 与 loop 配合 **现象**displayCount(2.5) loop(true) 时循环边界出现空白。 **原因**loop 模式下的虚拟列表增加了额外的页面副本displayCount 需要在这些副本之间保持一致的布局。 **解决方案**大多数情况下可以正常工作。如果出现异常确认 ForEach 的 keyGenerator 返回了稳定的唯一值。 ### 7.5 displayCount 在小屏幕上的表现 **现象**在屏幕宽度较小的设备上displayCount 2.5 导致卡片过窄。 **解决方案**使用条件语句适配不同屏幕 ets // 根据屏幕宽度动态调整 aboutToAppear(): void { let w this.getUIContext()?.getWindowWidth() ?? 360; if (w 360) { this.cardCount 2.0; // 小屏 2 张 } else { this.cardCount 2.5; // 正常 2.5 张 } } --- ## 8. 本系列七篇全览 本文是鸿蒙原生 ArkTS 布局实战系列的第六篇不含项目回顾篇。以下是全系列概览 | # | 标题 | 组件 | 核心属性 | 核心知识点 | |---|------|------|---------|-----------| | 1 | Tabs animateTo 切换动画 | Tabs | animateTo | 显式动画编排 | | 2 | Swiper 轮播图 | Swiper | autoPlay, interval | 基础轮播配置 | | 3 | Tabs vertical 侧边栏 | Tabs | vertical, barPosition.Start | 左侧导航布局 | | 4 | Swiper loop 无限循环 | Swiper | loop(true) | 虚拟列表原理 | | 5 | Swiper Curve 缓动曲线 | Swiper | .curve(Curve.xxx) | 动画节奏控制 | | **6** | **Swiper displayCount** | **Swiper** | **displayCount, itemSpace** | **多卡片并排** | | — | 项目回顾 | — | — | 24 个编译错误总结 | **三篇 Swiper 文章的递进关系** 1. **第 2 篇**基础轮播学会 Swiper 的基本使用 2. **第 4 篇**loop理解循环原理 3. **第 5 篇**Curve掌控动画节奏 4. **第 6 篇本篇**displayCount掌握多卡片布局 --- ## 9. 总结 ### 9.1 核心知识点 | 知识点 | 掌握程度 | |--------|---------| | displayCount 属性 | ✅ 控制一屏显示的卡片数量 | | 宽度计算公式 | ✅ (总宽 - (count-1) × itemSpace) / count | | itemSpace 卡片间距 | ✅ 统一间距控制 | | 2.5 的典型配置 | ✅ 两卡完整 半张预览 | | 卡片子元素 width | ✅ 使用 100%不设固定值 | | displayCount vs. 滑动步长 | ✅ 显示 ≠ 行为 | ### 9.2 displayCount 的最佳实践 | 卡片类型 | 推荐 displayCount | 推荐 itemSpace | |---------|------------------|---------------| | 大图海报 | 1.5~2.0 | 12~16 | | 中等卡片 | **2.5** | **12** | | 小卡片/头像 | 3.5~4.5 | 6~8 | | Emoji/图标 | 5.0~6.0 | 4~6 | ### 9.3 扩展方向 1. **displayCount 动态切换**根据屏幕宽度或用户偏好动态调整 count 2. **嵌套 displayCount**外层 Swiper全屏轮播内嵌 displayCount Swiper分类行 3. **3D 堆叠效果**通过自定义动画让多张卡片在滑动时产生3D 层叠效果 4. **drag 手势**关闭 autoPlay让用户完全通过拖拽浏览displayCount 提供预览 ### 9.4 推荐阅读 - [HarmonyOS NEXT 开发文档 — Swiper 组件](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-container-swiper-V5) - [本系列第 2 篇 — Swiper 轮播图基础](./HarmonyOS_Swiper_Blog.md) - [本系列第 4 篇 — Swiper loop 无限循环](./HarmonyOS_Swiper_Loop_Blog.md) --- **本文配套完整代码**entry/src/main/ets/pages/Index.ets209 行已通过编译验证。 **编译命令**hvigorw assembleApp --no-daemon **运行方式**在 DevEco Studio 中打开项目连接鸿蒙 NEXT 模拟器或真机运行。