问题背景

在小程序开发中,我们经常会遇到一个看似简单却难以完美解决的问题:UI 稿上一行显示的内容,在某些机型上却出现了换行

最近在开发成员管理功能时,就遇到了这个典型问题:信息展示区域包含"成立日期"、“任期时间”、"换届日期"等字段,UI 稿设计为一行显示,但在小屏手机上出现了换行。

问题现象

UI 稿期望:
┌─────────────────────────────────────┐
│ 成立日期:2024-01-01  任期:5年  换届日期:2029-01-01 │
└─────────────────────────────────────┘

实际效果(小屏机型):
┌─────────────────────────────────────┐
│ 成立日期:2024-01-01  任期:5年     │
│ 换届日期:2029-01-01                │
└─────────────────────────────────────┘

这个问题被激活了 3 次,开发、测试、产品之间进行了多轮沟通。

问题根因

1. 屏幕宽度差异大

小程序运行在各种机型上,屏幕宽度差异很大:

机型 屏幕宽度 (px) rpx 换算
iPhone SE 320 750rpx = 320px
iPhone 12 390 750rpx = 390px
iPhone 14 Pro Max 430 750rpx = 430px

同样的 750rpx 布局,在不同机型上的实际像素差距可达 34%

2. 内容长度固定

日期格式(如 2024-01-01)是固定长度的,无法压缩。当多个日期字段并排时,总宽度很容易超出小屏设备的可用空间。

3. UI 稿的理想化

UI 设计师通常在固定尺寸的画布上设计,很难预见所有机型的实际效果。

解决方案探索

方案一:缩小字体(不推荐)

.date-text {
  font-size: 24rpx; /* 从 28rpx 缩小到 24rpx */
}

问题

  • 可读性下降
  • 只能缓解,无法根治
  • 不同内容长度仍可能换行

方案二:强制不换行 + 溢出处理

.info-row {
  display: flex;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

问题

  • 内容被截断,信息丢失
  • 用户体验不佳

方案三:响应式布局(推荐)

根据屏幕宽度动态调整布局:

<template>
  <view class="info-container">
    <!-- 大屏:一行显示 -->
    <view v-if="isLargeScreen" class="info-row">
      <text>成立日期:{{ establishDate }}</text>
      <text>任期:{{ tenure }}年</text>
      <text>换届日期:{{ changeDate }}</text>
    </view>

    <!-- 小屏:两行显示 -->
    <view v-else class="info-rows">
      <view class="info-row">
        <text>成立日期:{{ establishDate }}</text>
        <text>任期:{{ tenure }}年</text>
      </view>
      <view class="info-row">
        <text>换届日期:{{ changeDate }}</text>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const isLargeScreen = ref(true)

onMounted(() => {
  const { windowWidth } = uni.getSystemInfoSync()
  isLargeScreen.value = windowWidth >= 375
})
</script>

方案四:精简字段(最佳方案)

与产品沟通后,发现:

  • "任期时间"在右上角已经显示过
  • "成立日期"在需求文档中没有定义

最终决定精简字段:

优化前:成立日期 + 任期 + 换届日期 + 状态(4个字段)
优化后:任期开始 + 任期结束 + 状态(3个字段)

这是最根本的解决方案:减少内容量,从源头避免换行问题。

最佳实践总结

1. 设计阶段

  • 多机型预览:设计稿应在多种屏幕宽度下预览
  • 留有余量:一行内容不要占满,预留 10-15% 的空间
  • 考虑极端情况:内容最长时是否仍能一行显示

2. 开发阶段

/* 推荐的响应式写法 */
.info-row {
  display: flex;
  flex-wrap: wrap; /* 允许换行,优雅降级 */
  gap: 16rpx;
}

.info-item {
  flex-shrink: 0; /* 防止内容被压缩 */
}

/* 小屏适配 */
@media screen and (max-width: 375px) {
  .info-row {
    flex-direction: column;
  }
}

3. 沟通协作

  • 质疑不合理的设计:如果一行放不下,及时与设计沟通
  • 提供替代方案:不只是说"做不了",而是提供可行的替代方案
  • 用数据说话:展示不同机型的实际效果截图

代码示例:通用的自适应信息展示组件

<template>
  <view class="adaptive-info">
    <view
      v-for="(item, index) in displayItems"
      :key="index"
      class="info-item"
    >
      <text class="label">{{ item.label }}:</text>
      <text class="value">{{ item.value }}</text>
    </view>
  </view>
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps({
  items: {
    type: Array,
    default: () => []
  },
  maxItemsPerRow: {
    type: Number,
    default: 3
  }
})

// 根据屏幕宽度动态计算每行显示数量
const displayItems = computed(() => {
  const { windowWidth } = uni.getSystemInfoSync()
  let itemsPerRow = props.maxItemsPerRow

  if (windowWidth < 350) {
    itemsPerRow = 1
  } else if (windowWidth < 400) {
    itemsPerRow = 2
  }

  return props.items
})
</script>

<style scoped>
.adaptive-info {
  display: flex;
  flex-wrap: wrap;
  gap: 12rpx 24rpx;
}

.info-item {
  display: flex;
  align-items: center;
  min-width: fit-content;
}

.label {
  color: #666;
  font-size: 26rpx;
}

.value {
  color: #333;
  font-size: 26rpx;
}
</style>

总结

  1. 多机型适配是小程序开发的核心挑战:不能只在一种机型上测试

  2. UI 稿不是圣经:当设计与实现冲突时,需要沟通协商

  3. 减少内容比压缩内容更好:与其让内容挤在一起,不如精简不必要的字段

  4. 优雅降级:允许在小屏上换行,比强制不换行导致内容截断更好

  5. 组件化思维:封装自适应组件,避免每次都处理适配问题


本文源于一个被激活 3 次的问题,希望能帮助其他开发者避免类似的坑。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐