rn_for_openharmony狗狗之家app实战-图片查看实现
案例开源地址:https://atomgit.com/nutpi/rn_openharmony_dogimg
沉浸式的图片体验
图库页面展示的是缩略图,用户点击后需要一个专门的页面来查看大图。这个页面要做到沉浸式,让用户专注于图片本身。
ImageViewPage 就是这样一个页面。黑色背景、全屏图片、底部操作栏,简洁但功能完整。
获取路由参数
页面需要知道要显示哪张图片,这个信息通过路由参数传递:
import {useRoute, useStore} from '../../hooks';
import type {DogImage} from '../../types';
export function ImageViewPage() {
const {params} = useRoute<{image: DogImage}>();
const img = params.image;
useRoute 是自定义 Hook,返回当前路由信息和参数。
泛型 <{image: DogImage}> 指定了参数的类型,这样 TypeScript 就知道 params.image 是什么结构。
从图库页面跳转时传的参数:
navigate('ImageView', {image: img})
这里直接传了整个 image 对象,包含 id、url 等信息。
收藏状态的获取
const {favoriteImages, toggleImage} = useStore();
const isFav = favoriteImages.includes(img.id);
favoriteImages 是收藏的图片 ID 数组。
toggleImage 是切换收藏状态的方法。
isFav 判断当前图片是否已收藏,用于显示不同的图标和文字。
屏幕尺寸的获取
import {Dimensions} from 'react-native';
const {width, height} = Dimensions.get('window');
在组件外部获取屏幕尺寸,避免每次渲染都调用。
这两个值用于计算图片的显示尺寸。
页面结构
return (
<View style={s.container}>
<Header title="图片详情" right={...} />
<View style={s.imgBox}>
<Image source={{uri: img.url}} style={s.img} resizeMode="contain" />
</View>
<View style={s.actions}>
...
</View>
</View>
);
三个部分:
- Header:顶部导航栏
- imgBox:图片展示区域
- actions:底部操作栏
Header 的右侧按钮
<Header title="图片详情" right={
<TouchableOpacity onPress={() => toggleImage(img.id)}>
<Text style={s.fav}>{isFav ? '❤️' : '🤍'}</Text>
</TouchableOpacity>
} />
Header 组件支持 right 属性,可以传入自定义的右侧内容。
这里放了一个收藏按钮,点击切换收藏状态。
图标根据 isFav 显示红心或白心。
图片展示区域
<View style={s.imgBox}>
<Image
source={{uri: img.url}}
style={s.img}
resizeMode="contain"
/>
</View>
resizeMode=“contain” 是关键属性。它让图片保持原始比例,完整显示在容器内,不会被裁剪。
其他可选值:
- cover:填满容器,可能裁剪
- stretch:拉伸填满,会变形
- center:居中显示原始尺寸
对于查看大图的场景,contain 是最合适的。
图片容器样式
imgBox: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
flex: 1 让容器占据除 Header 和底部操作栏之外的所有空间。
justifyContent 和 alignItems 都设为 center,让图片在容器中居中显示。
图片尺寸计算
img: {width, height: height * 0.6},
宽度用屏幕宽度,高度用屏幕高度的 60%。
为什么不用 100%?因为要给 Header 和底部操作栏留空间。
配合 resizeMode="contain",图片会在这个区域内自适应显示。
底部操作栏
<View style={s.actions}>
<TouchableOpacity style={s.action} onPress={() => toggleImage(img.id)}>
<Text style={s.actionIcon}>{isFav ? '❤️' : '🤍'}</Text>
<Text style={s.actionText}>{isFav ? '已收藏' : '收藏'}</Text>
</TouchableOpacity>
<TouchableOpacity style={s.action}>
<Text style={s.actionIcon}>📤</Text>
<Text style={s.actionText}>分享</Text>
</TouchableOpacity>
<TouchableOpacity style={s.action}>
<Text style={s.actionIcon}>💾</Text>
<Text style={s.actionText}>保存</Text>
</TouchableOpacity>
</View>
三个操作按钮:收藏、分享、保存。
每个按钮都是图标 + 文字的组合。
收藏按钮的图标和文字都会根据状态变化。
操作栏样式
actions: {
flexDirection: 'row',
backgroundColor: '#1a1a1a',
paddingVertical: 20,
paddingBottom: 36
},
flexDirection: ‘row’ 让三个按钮横向排列。
backgroundColor: ‘#1a1a1a’ 深灰色背景,比纯黑稍亮,有层次感。
paddingBottom: 36 底部多留一些空间,适配有底部安全区的设备。
单个操作按钮样式
action: {flex: 1, alignItems: 'center'},
actionIcon: {fontSize: 26, marginBottom: 4},
actionText: {fontSize: 12, color: '#fff'},
flex: 1 让三个按钮平分宽度。
alignItems: ‘center’ 让图标和文字居中。
图标用 26 号字,文字用 12 号,形成主次分明的视觉层次。
深色主题的设计
整个页面用深色背景:
container: {flex: 1, backgroundColor: '#000'},
深色背景有几个好处:
- 突出图片:黑色背景让图片更醒目
- 减少干扰:用户注意力集中在图片上
- 护眼:在暗环境下看图片更舒适
这是图片查看类应用的常见设计。
收藏功能的双入口
页面上有两个收藏入口:
- Header 右侧的心形图标
- 底部操作栏的收藏按钮
两个入口调用的是同一个方法 toggleImage(img.id),状态是同步的。
为什么要两个入口?
Header 的按钮方便快速操作,不用滚动到底部。
底部按钮有文字说明,更直观,适合不熟悉图标含义的用户。
分享和保存功能
当前代码里分享和保存按钮只是占位,没有实际功能:
<TouchableOpacity style={s.action}>
<Text style={s.actionIcon}>📤</Text>
<Text style={s.actionText}>分享</Text>
</TouchableOpacity>
实现这些功能需要用到原生模块:
分享:调用系统分享面板,可以分享到微信、微博等。
保存:把图片下载到相册,需要存储权限。
这些功能在鸿蒙系统上的实现方式可能和 Android/iOS 不同,需要单独适配。
useRoute Hook 的实现原理
看看 useRoute 是怎么工作的:
export function useRoute<T = any>() {
const [route, setRoute] = useState(navigator.getRoute());
const [params, setParams] = useState<T>(navigator.getParams<T>());
useEffect(() => {
const unsubscribe = navigator.subscribe((r, p) => {
setRoute(r);
setParams((p || {}) as T);
});
return unsubscribe;
}, []);
return {route, params};
}
初始值从 navigator 获取当前路由和参数。
订阅变化:通过 subscribe 监听路由变化,更新本地状态。
返回值包含路由名和参数,供组件使用。
泛型 T 让调用方可以指定参数类型,获得类型提示。
图片加载的优化
当前实现比较简单,可以优化的点:
加载占位:图片加载前显示一个占位图或 Loading。
const [loaded, setLoaded] = useState(false);
<Image
source={{uri: img.url}}
onLoad={() => setLoaded(true)}
/>
{!loaded && <ActivityIndicator />}
加载失败处理:网络问题导致图片加载失败时显示错误提示。
<Image
source={{uri: img.url}}
onError={() => setError(true)}
/>
手势操作的扩展
图片查看页面通常支持手势操作:
双击放大:双击图片放大到 2 倍,再双击恢复。
捏合缩放:两指捏合调整图片大小。
拖动平移:放大后可以拖动查看不同区域。
这些功能需要用到手势系统,React Native 提供了 PanResponder 和 Animated API。
不过为了保持代码简洁和鸿蒙兼容性,当前版本没有实现这些高级功能。
图片信息展示
可以在页面上显示更多图片信息:
{img.breeds && img.breeds.length > 0 && (
<View style={s.breedInfo}>
<Text style={s.breedName}>{img.breeds[0].name}</Text>
</View>
)}
如果图片关联了品种信息,可以显示品种名称,点击跳转到品种详情。
小结
图片查看页面的实现要点:
- 路由参数:通过 useRoute 获取传递的图片数据
- 沉浸式设计:深色背景突出图片内容
- resizeMode:contain 模式保持图片比例
- 多入口操作:Header 和底部都有收藏按钮
- 响应式布局:用 Dimensions 获取屏幕尺寸
下一篇讲随机狗狗页面,每次刷新都能看到不同的狗狗,是个很有趣的功能。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐




所有评论(0)