ReactNative【实战系列教程】我的小红书 5 -- 文章详情(含轮播图 ImageSlider,点亮红心动画 Heart,嵌套评论等)

最终效果

在这里插入图片描述

安装依赖

npm i dayjs

用于对时间进行格式化

必备组件

轮播图 ImageSlider

https://blog.csdn.net/weixin_41192489/article/details/149224510

点亮红心动画 Heart

components/Heart.tsx

import AntDesign from "@expo/vector-icons/AntDesign";
import React, { useEffect, useRef, useState } from "react";
import { Animated, TouchableOpacity } from "react-native";
type Props = {value: boolean;onValueChanged?: (value: boolean) => void;size?: number;color?: string;
};
// eslint-disable-next-line react/display-name
export default (props: Props) => {const { value, onValueChanged, size = 20, color = "black" } = props;const [showState, setShowState] = useState<boolean>(false);const scale = useRef<Animated.Value>(new Animated.Value(0)).current;const alpha = useRef<Animated.Value>(new Animated.Value(0)).current;useEffect(() => {setShowState(value);}, [value]);const onHeartPress = () => {const newState = !showState;setShowState(newState);onValueChanged?.(newState);if (newState) {alpha.setValue(1);const scaleAnim = Animated.timing(scale, {toValue: 1.8,duration: 300,useNativeDriver: false,});const alphaAnim = Animated.timing(alpha, {toValue: 0,duration: 400,useNativeDriver: false,delay: 200,});Animated.parallel([scaleAnim, alphaAnim]).start();} else {scale.setValue(0);alpha.setValue(0);}};return (<TouchableOpacity onPress={onHeartPress}>{showState ? (<AntDesign name="heart" size={size} color="red" />) : (<AntDesign name="hearto" size={size} color={color} />)}<Animated.Viewstyle={{width: size,height: size,borderRadius: size / 2,borderWidth: size / 20,position: "absolute",borderColor: "#ff2442",transform: [{ scale: scale }],opacity: alpha,}}/></TouchableOpacity>);
};

获取路由参数

import { useLocalSearchParams } from "expo-router";
  const params = useLocalSearchParams();const { id } = params;

此种路由参数为路由添加的查询条件

router.push(`/articleDetail?id=${article.id}`);

代码实现

app/_layout.tsx

注册路由

              <Stack.Screenname="articleDetail"options={{ headerShown: false }}/>

app/articleDetail.tsx

文章详情页

import Heart from "@/components/Heart";
import { ImageSlider } from "@/components/slidePager";
import AntDesign from "@expo/vector-icons/AntDesign";
import Entypo from "@expo/vector-icons/Entypo";
import EvilIcons from "@expo/vector-icons/EvilIcons";
import dayjs from "dayjs";
import { useLocalSearchParams } from "expo-router";
import { useEffect, useState } from "react";
import {Dimensions,Image,ScrollView,StyleSheet,Text,TextInput,TouchableOpacity,View,
} from "react-native";
export default function Index() {const [height, setHeight] = useState<number>(300);const { width: SCREEN_WIDTH } = Dimensions.get("window");const userInfo = {avatar:"https://gips1.baidu.com/it/u=705524004,2611014536&fm=3074&app=3074&f=PNG?w=2048&h=2048",};const detail = {avatarUrl:"https://img2.baidu.com/it/u=902203086,3868774028&fm=253&app=138&f=JPEG?w=500&h=500",userName: "张三",images: ["https://img0.baidu.com/it/u=1168912228,741609775&fm=253&fmt=auto&app=120&f=JPEG?w=1422&h=800","https://gips3.baidu.com/it/u=1014935733,598223672&fm=3074&app=3074&f=PNG?w=1440&h=2560","https://img0.baidu.com/it/u=2779316202,721068756&fm=253&app=120&f=JPEG?w=1422&h=800",],title: "让我抱抱,一起温暖,真的好治愈",desc: "家里鸡蛋不知道怎么吃的,可以去试试这个火腿肠炒蛋,\n我妈说居然这么好吃。\n香辣过瘾真的忒下饭了,做法鸡蛋,你们也去安排起来。",tag: ["每秒都值得记录", "治愈系", "治愈猫咪"],dateTime: "2023-02-27 12:20",location: "广东",favoriteCount: 1098,collectionCount: 208,isFavorite: false,isCollection: false,comments: [{userName: "胖胖胖",avatarUrl:"https://img0.baidu.com/it/u=2875122849,1736556673&fm=253&app=138&f=JPEG?w=500&h=500",message:"果断关注,这个博主做的菜真实色香味俱全。每天跟着学做菜,我要做大厨了,啊哈哈~",dateTime: "2023-02-28 07:23",location: "成都",favoriteCount: 45,isFavorite: false,children: [{userName: "小飞飞爱猫咪",avatarUrl:"https://img2.baidu.com/it/u=1922178401,3519789815&fm=253&app=138&f=JPEG?w=500&h=500",message: "安排起来,这个做法很简单的哦。",dateTime: "2023-02-28 13:23",location: "广东",favoriteCount: 1,isFavorite: false,},],},{userName: "左半",avatarUrl:"https://img2.baidu.com/it/u=3118961545,1128223138&fm=253&app=138&f=JPEG?w=500&h=500",message: "看着真有食欲",dateTime: "2023-02-28 09:09",location: "福建",favoriteCount: 2,isFavorite: false,children: undefined,},],};useEffect(() => {if (!detail?.images) {return;}const firstImg = detail?.images[0];Image.getSize(firstImg, (width: number, height: number) => {const showHeight = (SCREEN_WIDTH * height) / width;setHeight(showHeight);});// eslint-disable-next-line react-hooks/exhaustive-deps}, []);const params = useLocalSearchParams();const { id } = params;// 此处省略访问接口获取文章详情console.log(id);const renderImages = () => {const { images } = detail;if (!images?.length) {return null;}const data: any[] = detail.images.map((i) => {return { img: i };});return (<View style={{ paddingBottom: 30 }}><ImageSliderdata={data}autoPlay={false}closeIconColor="white"caroselImageStyle={{ height }}indicatorContainerStyle={{ bottom: -40 }}activeIndicatorStyle={styles.activeDot}inActiveIndicatorStyle={styles.inActiveDot}/></View>);};const renderInfo = () => {const tags = detail.tag?.map((i) => `# ${i}`).join(" ");return (<><Text style={styles.articleTitleTxt}>{detail.title}</Text><Text style={styles.descTxt}>{detail.desc}</Text><Text style={styles.tagsTxt}>{tags}</Text><Text style={styles.timeAndLocationTxt}>{detail.dateTime} {detail.location}</Text><View style={styles.line} /></>);};const renderComments = () => {const count = detail.comments?.length || 0;const styles = StyleSheet.create({commentsCountTxt: {fontSize: 14,color: "#666",marginTop: 20,marginLeft: 16,},inputLayout: {width: "100%",padding: 16,flexDirection: "row",alignItems: "center",},userAvatarImg: {width: 32,height: 32,borderRadius: 16,resizeMode: "cover",},commentInput: {flex: 1,height: 40,borderRadius: 16,marginLeft: 12,backgroundColor: "#f0f0f0",color: "#333",paddingVertical: 4,paddingHorizontal: 12,},commentsContainer: {paddingHorizontal: 16,paddingTop: 16,paddingBottom: 32,},commentItem: {width: "100%",flexDirection: "row",},cAvatar: {width: 36,height: 36,resizeMode: "cover",borderRadius: 18,},contentLayout: {flex: 1,marginHorizontal: 12,},nameTxt: {fontSize: 12,color: "#999",},messageTxt: {fontSize: 14,color: "#333",marginTop: 6,},timeLocationTxt: {fontSize: 12,color: "#bbb",},countLayout: {alignItems: "center",},fCount: {fontSize: 12,color: "#666",marginTop: 2,},divider: {marginLeft: 50,marginRight: 0,height: StyleSheet.hairlineWidth,backgroundColor: "#eee",marginVertical: 16,},});return (<><Text style={styles.commentsCountTxt}>{count ? `共 ${count} 条评论` : "暂无评论"}</Text><View style={styles.inputLayout}><Imagestyle={styles.userAvatarImg}source={{ uri: userInfo.avatar }}/><TextInputstyle={styles.commentInput}placeholder="说点什么吧,万一火了呢~"placeholderTextColor={"#bbb"}/></View>{!!count && (<View style={styles.commentsContainer}>{detail.comments?.map((i: ArticleComment, index: number) => {return (<View key={`${index}`} style={{}}><View style={styles.commentItem}><Imagestyle={styles.cAvatar}source={{ uri: i.avatarUrl }}/><View style={styles.contentLayout}><Text style={styles.nameTxt}>{i.userName}</Text><Text style={styles.messageTxt}>{i.message}<Text style={styles.timeLocationTxt}>{dayjs(i.dateTime).format("MM-DD")} {i.location}</Text></Text>{!!i.children?.length &&i.children.map((j: ArticleComment, subIndex: number) => {return (<Viewkey={`${index}-${subIndex}`}style={[styles.commentItem,{ marginTop: 12, width: SCREEN_WIDTH - 80 },]}><Imagestyle={[styles.cAvatar,{ width: 32, height: 32 },]}source={{ uri: j.avatarUrl }}/><View style={styles.contentLayout}><Text style={styles.nameTxt}>{j.userName}</Text><Text style={styles.messageTxt}>{j.message}<Text style={styles.timeLocationTxt}>{dayjs(j.dateTime).format("MM-DD")}{" "}{j.location}</Text></Text></View><View style={styles.countLayout}><Heart size={20} value={j.isFavorite} /><Text style={styles.fCount}>{j.favoriteCount}</Text></View></View>);})}</View><View style={styles.countLayout}><Heart size={20} value={i.isFavorite} /><Text style={styles.fCount}>{i.favoriteCount}</Text></View></View><View style={styles.divider} /></View>);})}</View>)}</>);};const renderBottom = () => {return (<View style={styles.bottomLayout}><View style={styles.bottomEditLayout}><AntDesign name="edit" size={20} color="#333" /><TextInputstyle={styles.bottomCommentInput}placeholder="说点什么"placeholderTextColor={"#333"}/></View><Heart size={22} value={detail.isFavorite} color="grey" /><Text style={styles.bottomCount}>{detail.favoriteCount}</Text><AntDesign name="staro" size={22} color="grey" /><Text style={styles.bottomCount}>{detail.collectionCount}</Text><AntDesign name="message1" size={22} color="grey" /><Text style={styles.bottomCount}>{detail.comments?.length || 0}</Text></View>);};return (<View style={styles.page}><View style={styles.topBar}><TouchableOpacityonPress={() => {console.log("返回");}}><Entypo name="chevron-left" size={24} color="black" /></TouchableOpacity><Image style={styles.avatarImg} source={{ uri: detail.avatarUrl }} /><Text style={styles.nameTxt}>{detail.userName}</Text><TouchableOpacityonPress={() => {console.log("关注");}}><Text style={styles.followBtn}>关注</Text></TouchableOpacity><TouchableOpacityonPress={() => {console.log("转发");}}><EvilIconsstyle={styles.shareIcon}name="external-link"size={32}color="black"/></TouchableOpacity></View><ScrollView showsVerticalScrollIndicator={false}>{renderImages()}{renderInfo()}{renderComments()}</ScrollView>{renderBottom()}</View>);
}
const styles = StyleSheet.create({page: {backgroundColor: "white",width: "100%",height: "100%",},bottomLayout: {width: "100%",height: 64,flexDirection: "row",alignItems: "center",paddingHorizontal: 16,borderTopWidth: 1,borderTopColor: "#eee",},bottomEditLayout: {height: 40,flex: 1,backgroundColor: "#f0f0f0",borderRadius: 20,flexDirection: "row",alignItems: "center",paddingHorizontal: 12,marginRight: 12,},bottomCommentInput: {height: "100%",paddingTop: 6,paddingLeft: 6,color: "#333",textAlignVertical: "center",paddingVertical: 0,},bottomCount: {fontSize: 14,color: "grey",marginLeft: 6,marginRight: 8,},shareIcon: {marginBottom: 4,},articleTitleTxt: {fontSize: 18,color: "#333",fontWeight: "bold",paddingHorizontal: 16,},descTxt: {fontSize: 15,color: "#333",marginTop: 6,paddingHorizontal: 16,},tagsTxt: {fontSize: 15,color: "#305090",marginTop: 6,paddingHorizontal: 16,},timeAndLocationTxt: {fontSize: 12,color: "#bbb",marginVertical: 16,marginLeft: 16,},line: {marginHorizontal: 16,height: StyleSheet.hairlineWidth,backgroundColor: "#eee",},topBar: {flexDirection: "row",alignItems: "center",padding: 10,},avatarImg: {width: 30,height: 30,resizeMode: "cover",borderRadius: 50,margin: 10,},nameTxt: {fontSize: 14,color: "#999",marginLeft: 6,flex: 1,},followBtn: {fontSize: 12,color: "red",borderWidth: 1,borderColor: "red",borderRadius: 10,paddingVertical: 4,paddingHorizontal: 12,marginRight: 10,},activeDot: {width: 6,height: 6,backgroundColor: "#ff2442",borderRadius: 3,},inActiveDot: {width: 6,height: 6,backgroundColor: "#c0c0c0",borderRadius: 3,},
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/88662.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/88662.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

哔哩哔哩第三方TV-BBLL最新版

—————【下 载 地 址】——————— 【​本章下载一】&#xff1a;https://pan.xunlei.com/s/VOUtUcaymd9rpgurgDKS9pswA1?pwdp76n# 【​本章下载二】&#xff1a;https://pan.xunlei.com/s/VOUtUcaymd9rpgurgDKS9pswA1?pwdp76n# 【百款黑科技】&#xff1a;https://uc…

用YOLOv5系列教程(1)-用YOLOv5轻松实现设备状态智能监控!工业级教程来了

用YOLOv5轻松实现设备状态智能监控&#xff01;工业级教程来了设备运维革命&#xff1a;15分钟教会你的摄像头看懂指示灯状态工业现场各种设备状态指示灯是工程师的"眼睛"——红灯报警、绿灯运行、黄灯待机。但人工巡检耗时费力&#xff0c;关键故障容易漏检&#xf…

笔记-分布式计算基础

Distributed Computing 划分数据并行&#xff08;DataParallelism&#xff09;将数据分为n份&#xff0c;发送到n个GPU上&#xff0c;每个GPU上都存在一个完整的大模型 缺点&#xff1a; 模型太大Pipeline Parallelism&#xff08;串行的&#xff09;将模型做split,每个GPU负责…

Android Studio 2024,小白入门喂饭级教程

一、下载Android Studio 1、从安卓官网安卓官网下载Android Studio。 ​ ​ 二、安装Android Studio 1、双击android-studio-2024.3.2.15-windows.exe。 ​ ​​ ​ ​ ​ ​ 三、新建工程 1、双击桌面图标​ 打开Android Studio。 ​ 选“Empty Views Activity…

AI智能体 | 使用Coze制作一键生成单词洗脑循环视频,一天批量生成100条视频不是梦!(附保姆级教程)

目录 一、整体工作流设计 二、制作工作流 2.1 开始节点 2.2 大模型-单词 2.3 大模型_图像生成 2.4 输出 2.5 图像生成_1 2.6 输出_2 2.7 画板_2 2.8 文本处理 2.9 输出_3 2.10 speech_synthesis_1x5 2.11 get_audio_duration_1 2.12 代码 2.13 get_audio_durati…

质量属性场景(Quality Attribute Scenario)深度解析

本质定义:质量属性场景(Quality Attribute Scenario)是精确描述软件系统质量要求的结构化方法,通过标准化的场景模板将抽象的质量属性转化为可测量、可验证的具体行为描述,为架构设计提供客观评估基准。 一、质量属性场景核心结构 1. 六元组标准模板 #mermaid-svg-AGbvPVRu…

Java_Springboot技术框架讲解部分(一)

首先讲解一下&#xff1a;Java技术栈中&#xff0c;目前Spring Boot在国内的关注趋势也日渐超过Spring。Spring Boot是Spring家族中的一个全新的框架&#xff0c;它用来简化Spring应用程序的创建和开发过程。采用Spring Boot可以非常容易和快速的构建基于Spring框架的应用程序&…

从OpenMV到执行器:当PID算法开始“调教”舵机

如果到现在还不会驱动舵机——朋友&#xff0c;电赛的元器件清单每年都在对你“明示”&#xff0c;二维云台都快成祖传考题了&#xff01;补课&#xff1f;现在&#xff01;立刻&#xff01;&#xff08;当然&#xff0c;如果你脸皮够厚&#xff0c;也可以私信骚扰作者&#xf…

xml映射文件的方式操作mybatis

映射文件 在Java spring中使用mybatis有两种方式&#xff0c;一种是注释的方式&#xff0c;一种是xml映射文件的方式。在简单的功能需求可以使用注释&#xff0c;方便简洁。而在大的功能逻辑上&#xff0c;更推荐使用xml映射文件&#xff0c;方便管理且结构清晰。 首先xml文件结…

Carla自动驾驶仿真_快速安装与运行Carla

大家好&#xff0c;我是橙子&#xff0c;今天给大家介绍Carla的基础安装和使用 目录 1.Carla介绍 2.Carla的安装与使用 3.Carla0.9.15安装包下载&#xff1a; ​编辑 4.运行Demo 5.运行一个简单场景&#xff1a; 6.相关资源 1.Carla介绍 Carla 是一个开源的自动驾驶仿…

远程登录docker执行shell报错input is not a terminal问题

背景 最近要远程去k8s docker里面获取信息&#xff0c;于是&#xff0c;写了一个如下的命令&#xff0c;执行完之后&#xff0c;报错了。 ssh 192.168.100.2 sudo crictl exec -it xxx.docker /usr/bin/lscpu --online --extended错误信息如下&#xff1a; time“2025-07-11T21…

使用FastAdmin框架开发二

继上一篇使用FastAdmin框架开发-CSDN博客教程 部署完项目后我们就可以边开发边调试了 在开发前我们可以先做一些基本设置 安装成功后会生成一个项目后台的地址http://域名/VrHGtzlfMB.php/index/login&#xff0c;后台入口文件是随机生成的&#xff0c;当然我们也可以根据我…

【DB2】load报错SQL3501W、SQL3109N、SQL2036N

最近老遇到迁移测试LOAD时报错&#xff0c;如图所示但是换成import又可以看描述是说load的内容将不会进入备份暂挂状态balbala… 下面的错误是说ixf文件无效 这里一直以为是SQL3501W的问题&#xff0c;去各种研究load参数和db2set里面的load参数&#xff0c;各种调整都不行 又以…

YOLO家族内战!v5/v8/v10谁才是你的真命天子?(附保姆级选择指南)

在目标检测领域&#xff0c;YOLO系列始终是工业部署与学术研究的焦点。从风靡全网的YOLOv5&#xff0c;到全面升级的YOLOv8&#xff0c;再到突破性能瓶颈的YOLOv10——每一次迭代都带来全新可能。作为开发者&#xff0c;究竟该选哪一代&#xff1f;本文用千字长文对比表格为你彻…

Claude Code是什么?国内如何使用到Claude Code?附国内最新使用教程

2025年已过大半&#xff0c;相信你也听说过 Claude——它是近年最受关注的 AI 模型之一&#xff0c;而 Claude Code 则是它面向编程场景的特别版本&#xff0c;专为代码理解、生成与重构而生&#xff0c;尤其擅长处理大项目、长上下文&#xff0c;和开发者对话更自然。 但对于一…

双轮驱动:政策激励与外部制约下的国产服务器市场演进

2025年6月&#xff0c;科智咨询正式发布《中国国产服务器市场研究报告&#xff08;2025&#xff09;》&#xff0c;报告从国产服务器产业概述、政策环境分析、市场现状与竞争格局、面临挑战与市场机遇等维度&#xff0c;深入剖析国产服务器市场现状及未来发展趋势。2022年10月&…

【工具变量】全国省市区县土地出让结果公告数据(2000-2024年)

土地出让结果公告数据是指政府主管部门在完成国有土地使用权出让后&#xff0c;依法依规对外公开的结果信息&#xff0c;涵盖土地出让时间、出让方式、竞得人、成交价、用地性质、面积等关键信息。土地出让数据是研究中国土地市场供需变化、城市发展轨迹以及地方财政收入结构的…

前端面试专栏-算法篇:22.树结构(二叉树、B树、红黑树)

&#x1f525; 欢迎来到前端面试通关指南专栏&#xff01;从js精讲到框架到实战&#xff0c;渐进系统化学习&#xff0c;坚持解锁新技能&#xff0c;祝你轻松拿下心仪offer。 前端面试通关指南专栏主页 前端面试专栏规划详情 树结构&#xff08;二叉树、B树、红黑树&#xff09…

爬虫-数据解析

1.解析概述特性re (正则表达式)bs4 (BeautifulSoup)xpath (lxml)pyquery本质文本模式匹配HTML/XML 解析器 (DOM树操作)XML路径语言 (节点导航)jQuery 式 CSS 选择器 (封装lxml)学习曲线陡峭中等中等简单 (熟悉jQuery/CSS)灵活性极高 (处理任意文本)高 (容错好&#xff0c;DOM操…

MySQL8.0基于GTID的组复制分布式集群的环境部署

前言&#xff1a; 需要清楚知道&#xff1a;MySQL 复制组能够以一种自动优先选择的单主模式运行&#xff0c;在某个时间只有一个服务器接受更新 。但是对于更高优先级的用户&#xff0c;组能够以多主模式部署&#xff0c;所有的服务器都能够接受更新&#xff0c;即使它们是同时…