news 2026/6/9 23:43:54

RN for OpenHarmony 小工具 App 实战:屏幕尺子实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RN for OpenHarmony 小工具 App 实战:屏幕尺子实现

这一篇实现一个「屏幕尺子」小工具:在手机屏幕上绘制 cm / inch 两条刻度尺,并展示当前设备屏幕宽度(px)与“换算后的大致厘米数”。

需要先说明一个现实限制:

  • 屏幕尺子的精度取决于设备的 PPI(像素密度)与系统缩放
  • 代码里用的是近似换算(pxPerCm = 37.8),因此适合“粗略参考”,不适合精密测量。

本文所有代码片段均来自仓库真实文件:

  • src/pages/Ruler.tsx
  • src/tools/index.ts
  • src/screens/ToolScreen.tsx

你要求“每一段代码后面跟着的文字解释多一些”,本文会按“代码块 -> 紧跟解释”的方式组织。


01. 这个小工具的设计目标

屏幕尺子是典型的“看起来简单,细节很多”的小工具。目标不是做实验室级精度,而是:

  • 可用:能滑动、能看清刻度
  • 直观:整数刻度有数字,半刻度/四分之一刻度有不同长度
  • 自解释:明确告诉用户精度可能不准

同时,它非常适合作为 RN UI 练习:

  • 刻度生成(循环渲染)
  • ScrollView 横向滚动
  • px 与 cm/inch 的比例换算
  • 小而美的入场动画

02. 工具箱接入:id=54 映射到 Ruler

2.1 工具列表注册

文件:src/tools/index.ts

{id:54,name:'尺子',description:'屏幕尺子工具',icon:'📏',component:'Ruler'},

解释:

  • component: 'Ruler'是 ToolScreen 查表渲染的 key
  • 📏图标在工具网格里容易识别
  • description简洁明确:这是一个屏幕尺子工具

2.2 ToolScreen 映射

文件:src/screens/ToolScreen.tsx

Ruler: Pages.Ruler,

解释:

  • 工具箱会根据配置中的字符串RulercomponentMap找到Pages.Ruler
  • 这种“配置驱动”的结构意味着:文章和代码的对应关系非常稳定

03. 屏幕像素信息:Dimensions.get(‘window’)

文件:src/pages/Ruler.tsx

import { View, Text, StyleSheet, ScrollView, Dimensions, Animated } from 'react-native'; const { width: screenWidth } = Dimensions.get('window');

解释:

  • Dimensions.get('window')返回当前窗口的尺寸信息(像素单位)
  • 这里我们只取width,用于:
    • “屏幕宽度 px”展示
    • 估算“屏幕宽度约等于多少 cm”

为什么用window而不是screen

  • window更接近应用可用区域
  • screen可能包含状态栏等不可用区域(不同平台行为有差异)

对于“尺子”这种工具,精度本来就不是严格保证,所以取 window 是更合理的工程选择。


04. 核心参数:cmCount 与 pxPerCm

const cmCount = 20; const pxPerCm = 37.8;

解释:

4.1cmCount的意义

cmCount=20代表我们要画 0~20cm 的刻度。

  • 每 1cm 需要 10 个小刻度(0.1cm)
  • 因此后续会生成cmCount * 10 + 1个 tick

为什么要+1?因为 0 也要画出来。

4.2pxPerCm = 37.8从哪来?

这个数字来自一个常见近似:

  • 96 dpi 的换算:1 inch = 96 px
  • 1 inch = 2.54 cm
  • 所以 1 cm ≈ 96 / 2.54 ≈ 37.8 px

它不是“设备真实 PPI”,但作为“参考级工具”足够。

如果你想做得更准,必须知道设备真实 dpi / ppi。


05. 入场动画:fade + slide(并行动画)

const fadeAnim = useRef(new Animated.Value(0)).current; const slideAnim = useRef(new Animated.Value(-20)).current; useEffect(() => { Animated.parallel([ Animated.timing(fadeAnim, { toValue: 1, duration: 500, useNativeDriver: true }), Animated.spring(slideAnim, { toValue: 0, friction: 6, useNativeDriver: true }), ]).start(); }, []);

解释:

  • fadeAnim控制 header 的透明度:0 -> 1
  • slideAnim控制 header 的 Y 位移:-20 -> 0(从上往下滑入)
  • Animated.parallel让两个动画同时执行

为什么这里用timing + spring的组合?

  • 淡入用timing就够了,稳定、可控
  • 位移用spring会更自然,有一点“落下来的惯性”

为什么只动画 header?

  • 主尺子是 ScrollView 内容,动画它会影响滚动体验
  • header 动一下就足够让页面“有启动感”,而不会干扰主要功能

06. cm 刻度渲染:把 1cm 切成 10 个 tick

{Array.from({ length: cmCount * 10 + 1 }).map((_, i) => { const isCm = i % 10 === 0; const isHalfCm = i % 5 === 0 && !isCm; return ( <View key={i} style={styles.tickContainer}> <View style={[styles.tick, isCm && styles.tickCm, isHalfCm && styles.tickHalf]} /> {isCm && <Text style={styles.tickLabel}>{i / 10}</Text>} </View> ); })}

解释(这是这页最核心的“刻度生成”):

6.1 为什么是cmCount * 10 + 1

因为我们把 1cm 切成 10 个小刻度(每格 0.1cm)。

  • 20cm 就是 200 个小刻度
  • +1是为了把 0cm 的刻度画出来

6.2isCmisHalfCm的判定思路

  • isCm:每 10 个 tick 一个整厘米
  • isHalfCm:每 5 个 tick 一个 0.5cm(但要排除整厘米)

这样你会得到三种高度:

  • 0.1cm:短线
  • 0.5cm:中线
  • 1cm:长线 + 数字

6.3 为什么数字只在整厘米显示?

如果每个小刻度都显示数字,UI 会极度拥挤。

整厘米显示数字是尺子类工具的通用设计:

  • 信息密度合理
  • 用户读数不会困惑

07. inch 刻度渲染:1 inch 切成 8 份(1/8)

{Array.from({ length: 8 * 8 + 1 }).map((_, i) => { const isInch = i % 8 === 0; const isHalf = i % 4 === 0 && !isInch; const isQuarter = i % 2 === 0 && !isHalf && !isInch; return ( <View key={i} style={[styles.tickContainer, { width: pxPerCm * 2.54 / 8 }]}> <View style={[styles.tick, isInch && styles.tickCm, isHalf && styles.tickHalf, isQuarter && styles.tickQuarter]} /> {isInch && <Text style={styles.tickLabel}>{i / 8}</Text>} </View> ); })}

解释:

7.1 为什么 1 inch 切成 8 份?

常见尺子会使用 1/8 inch 或 1/16 inch 作为最小刻度。

这里选择 1/8 是一个折中:

  • 刻度足够细
  • 但不会多到难以辨认

7.2 tick 宽度为何是pxPerCm * 2.54 / 8

  • 1 inch = 2.54 cm
  • 1/8 inch = 2.54/8 cm
  • 再乘以pxPerCm就得到每个 tick 的宽度(px)

这样 inch 刻度就会与 cm 刻度在同一套 px 换算体系下保持一致。

7.3 1/2 与 1/4 刻度高度区分

  • isInch:整英寸(最高)
  • isHalf:半英寸(次高)
  • isQuarter:四分之一英寸(中等)
  • 其他:八分之一(最短)

这也是典型的英制尺子视觉层次。


08. 尺子横向滚动:为什么用 ScrollView horizontal?

<ScrollView horizontal style={styles.rulerContainer} showsHorizontalScrollIndicator={false}> <View style={styles.ruler}> {/* ticks */} </View> </ScrollView>

解释:

  • 刻度尺天然是横向延伸的
  • horizontal可以让用户像使用真实尺子一样拖动查看
  • showsHorizontalScrollIndicator={false}隐藏滚动条,让视觉更像“尺子”而不是“列表”

这里有个设计细节:

  • 内层用View来承载 ticks,而不是直接在 ScrollView 里 map
  • 这样刻度背景(黄色/蓝色)可以作为一整条条带显示

09. 屏幕信息展示:px 与 cm 的换算

<Text style={styles.infoValue}>{screenWidth.toFixed(0)} px</Text> <Text style={styles.infoValue}>{(screenWidth / pxPerCm).toFixed(1)} cm</Text>

解释:

  • screenWidth是 px
  • screenWidth / pxPerCm得到“估算厘米”

为什么只保留 1 位小数?

  • 因为pxPerCm本身就是近似值
  • 显示过多小数会给用户一种“很精确”的错觉

这类工具更应该强调“参考”,而不是制造误导。


10. 关键样式:刻度宽度与刻度高度

来自src/pages/Ruler.tsx

tickContainer: { width: 3.78, alignItems: 'center' }, tick: { width: 1, height: 10, backgroundColor: '#333' }, tickCm: { height: 30, width: 2 }, tickHalf: { height: 20 }, tickQuarter: { height: 15 },

解释:

10.1 为什么tickContainer.width = 3.78

它对应“0.1cm”的宽度:

  • 1cm ≈ 37.8px
  • 0.1cm ≈ 3.78px

所以 cm 尺子每个小刻度正好占用 3.78px。

10.2 不同刻度高度的意义

如果所有刻度高度一样,用户会难以快速定位整数刻度。

高度分层会形成“视觉导航”:

  • 找到长线 -> 读数字
  • 在长线之间用中线/短线估算

这就是尺子类 UI 的核心可读性来源。


11. 小结

这个「屏幕尺子」工具的实现重点在于:

  • 通过pxPerCm把“物理长度”映射到“屏幕像素”
  • cm / inch 两套刻度各自按常见规则分桶渲染
  • 用 ScrollView horizontal 提供自然的尺子滑动体验
  • 明确提示“精度因设备而异”,避免误用

如果你后续想做得更精确,关键不在 UI,而在拿到真实 PPI并计算pxPerCm


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 21:31:41

GLM-4.6V-Flash-WEB健康监测:可穿戴设备图像数据分析

GLM-4.6V-Flash-WEB健康监测&#xff1a;可穿戴设备图像数据分析 1. 技术背景与应用场景 随着可穿戴设备在医疗健康领域的广泛应用&#xff0c;实时、精准的生理数据监测成为智能健康管理的重要组成部分。传统传感器主要依赖心率、血氧、体温等数值型信号进行分析&#xff0c…

作者头像 李华
网站建设 2026/6/10 0:33:31

通义千问2.5-7B降本部署案例:4GB量化镜像节省GPU成本60%

通义千问2.5-7B降本部署案例&#xff1a;4GB量化镜像节省GPU成本60% 1. 引言 随着大模型在企业级应用中的广泛落地&#xff0c;如何在保障推理性能的同时有效控制部署成本&#xff0c;成为工程团队关注的核心问题。通义千问2.5-7B-Instruct作为阿里云于2024年9月发布的中等体…

作者头像 李华
网站建设 2026/6/10 13:45:42

FST ITN-ZH与Python集成:API调用与二次开发指南

FST ITN-ZH与Python集成&#xff1a;API调用与二次开发指南 1. 引言 1.1 场景背景 在自然语言处理&#xff08;NLP&#xff09;的实际工程落地中&#xff0c;中文逆文本标准化&#xff08;Inverse Text Normalization, ITN&#xff09;是一项关键的预处理任务。它负责将口语…

作者头像 李华
网站建设 2026/6/10 13:21:51

OpenDataLab MinerU性能优化教程:低算力设备也能跑多模态模型

OpenDataLab MinerU性能优化教程&#xff1a;低算力设备也能跑多模态模型 1. 引言 随着多模态大模型在文档理解、图像解析和信息提取等场景中的广泛应用&#xff0c;越来越多开发者希望在本地或边缘设备上部署具备视觉理解能力的AI模型。然而&#xff0c;主流多模态模型往往参…

作者头像 李华
网站建设 2026/6/10 12:38:32

Emotion2Vec+ Large实时流处理?WebSocket集成方案构想

Emotion2Vec Large实时流处理&#xff1f;WebSocket集成方案构想 1. 背景与需求分析 1.1 现有系统能力回顾 Emotion2Vec Large 是由阿里达摩院在 ModelScope 平台上发布的语音情感识别大模型&#xff0c;具备高精度、多语种支持和强大的泛化能力。当前基于该模型构建的 WebU…

作者头像 李华
网站建设 2026/6/10 12:39:47

垂直领域模型优势:DeepSeek-R1在专业场景下的表现深度评测

垂直领域模型优势&#xff1a;DeepSeek-R1在专业场景下的表现深度评测 1. 引言 随着大语言模型在通用场景中的能力趋于饱和&#xff0c;行业对垂直领域专用模型的需求日益增长。如何在保证推理质量的同时降低部署成本、提升任务适配性&#xff0c;成为工程落地的关键挑战。De…

作者头像 李华