From 52ac43bdb28980c799bb86f1b0d37de8e5879fe5 Mon Sep 17 00:00:00 2001 From: wangchunyan Date: Wed, 5 Nov 2025 22:07:42 +0800 Subject: [PATCH] ai finish --- .../LargeListExamples/CompleteVideoExample.js | 364 ++++++++++++++++++ .../LargeListExamples/SimpleVideoExample.js | 260 +++++++++++++ .../LargeListExamples/VideoBannerExample.js | 203 ++++++++++ .../LargeListExamples/VideoListExample.js | 192 +++++++++ Examples/LargeListExamples/index.js | 4 + 5 files changed, 1023 insertions(+) create mode 100644 Examples/LargeListExamples/CompleteVideoExample.js create mode 100644 Examples/LargeListExamples/SimpleVideoExample.js create mode 100644 Examples/LargeListExamples/VideoBannerExample.js create mode 100644 Examples/LargeListExamples/VideoListExample.js diff --git a/Examples/LargeListExamples/CompleteVideoExample.js b/Examples/LargeListExamples/CompleteVideoExample.js new file mode 100644 index 00000000..bf7ffa21 --- /dev/null +++ b/Examples/LargeListExamples/CompleteVideoExample.js @@ -0,0 +1,364 @@ +import React from "react"; +import { StyleSheet, Text, View, Image, FlatList, TouchableOpacity, Dimensions } from "react-native"; +import { LargeList } from "../../src"; +import { MediaWrapper } from "../../src"; +import Video from "react-native-video"; + +const { width } = Dimensions.get("window"); + +export class CompleteVideoExample extends React.Component { + static navigationOptions = { + title: "Complete Video Example", + }; + + state = { + likedItems: {}, + comments: {}, + playingVideoId: null, + }; + + render() { + return ( + 0} + heightForIndexPath={this._heightForIndexPath} + renderIndexPath={this._renderIndexPath} + contentStyle={styles.listContent} + /> + ); + } + + _getData = () => { + return [ + { + items: [ + // Banner at the top + { type: "banner", id: "banner-1", images: this._getBannerImages() }, + // Video items + { + type: "video", + id: "video-1", + url: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", + title: "Big Buck Bunny", + user: "Nature Channel", + avatar: "https://ui-avatars.com/api/?name=Nature+Channel&background=random" + }, + { + type: "video", + id: "video-2", + url: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4", + title: "Elephants Dream", + user: "Wildlife World", + avatar: "https://ui-avatars.com/api/?name=Wildlife+World&background=random" + }, + { + type: "video", + id: "video-3", + url: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4", + title: "For Bigger Blazes", + user: "Fire Safety", + avatar: "https://ui-avatars.com/api/?name=Fire+Safety&background=random" + }, + { + type: "video", + id: "video-4", + url: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4", + title: "For Bigger Escapes", + user: "Adventure Club", + avatar: "https://ui-avatars.com/api/?name=Adventure+Club&background=random" + }, + ], + }, + ]; + }; + + _getBannerImages = () => { + return [ + { id: "banner-img-1", url: "https://images.unsplash.com/photo-1501426026826-31c667bdf23d?w=300", title: "Nature" }, + { id: "banner-img-2", url: "https://images.unsplash.com/photo-1518717758536-85ae29035b6d?w=300", title: "Wildlife" }, + { id: "banner-img-3", url: "https://images.unsplash.com/photo-1500049517218-60249511432c?w=300", title: "Adventure" }, + { id: "banner-img-4", url: "https://images.unsplash.com/photo-1530281700549-e82e7bf110d6?w=300", title: "Mountains" }, + ]; + }; + + _heightForIndexPath = ({ section, row }) => { + const item = this._getData()[section].items[row]; + if (item.type === "banner") { + return 220; + } else if (item.type === "video") { + return 520; + } + return 100; + }; + + _renderIndexPath = ({ section, row }, mediaWrapperParam) => { + const item = this._getData()[section].items[row]; + + if (item.type === "banner") { + return this._renderBanner(item); + } else if (item.type === "video") { + return this._renderVideoItem(item, mediaWrapperParam); + } + + return null; + }; + + _renderBanner = (bannerItem) => { + return ( + + Featured + ( + console.log("Banner pressed:", item.title)}> + + {item.title} + + )} + keyExtractor={(item) => item.id} + contentContainerStyle={styles.bannerList} + /> + + ); + }; + + _renderVideoItem = (videoItem, mediaWrapperParam) => { + const isLiked = this.state.likedItems[videoItem.id] || false; + const commentCount = this.state.comments[videoItem.id] || 0; + const isPlaying = this.state.playingVideoId === videoItem.id; + + return ( + + {/* User Info */} + + + {videoItem.user} + + Follow + + + + {/* Video */} + ( + + + + )} + loadEndFunc="onLoad" + > + + + {/* Video Info */} + + {videoItem.title} + + + {/* Interaction Bar */} + + this._toggleLike(videoItem.id)} + > + + + {isLiked ? "Liked" : "Like"} + + + + this._addComment(videoItem.id)} + > + + + Comment ({commentCount}) + + + + console.log("Share")} + > + + Share + + + + ); + }; + + _toggleLike = (videoId) => { + this.setState((prevState) => ({ + likedItems: { + ...prevState.likedItems, + [videoId]: !prevState.likedItems[videoId], + }, + })); + }; + + _addComment = (videoId) => { + this.setState((prevState) => ({ + comments: { + ...prevState.comments, + [videoId]: (prevState.comments[videoId] || 0) + 1, + }, + })); + }; +} + +const styles = StyleSheet.create({ + listContent: { + backgroundColor: "#f5f5f5", + }, + bannerContainer: { + paddingVertical: 16, + backgroundColor: "white", + }, + bannerTitle: { + fontSize: 20, + fontWeight: "bold", + marginLeft: 16, + marginBottom: 12, + }, + bannerList: { + paddingHorizontal: 16, + }, + bannerItem: { + marginRight: 12, + width: width - 100, + height: 180, + borderRadius: 12, + overflow: "hidden", + }, + bannerImage: { + width: "100%", + height: "100%", + }, + bannerImageTitle: { + position: "absolute", + bottom: 12, + left: 12, + color: "white", + fontSize: 18, + fontWeight: "bold", + textShadowColor: "rgba(0, 0, 0, 0.5)", + textShadowOffset: { width: 1, height: 1 }, + textShadowRadius: 3, + }, + videoContainer: { + backgroundColor: "white", + marginBottom: 12, + padding: 12, + }, + userInfo: { + flexDirection: "row", + alignItems: "center", + marginBottom: 12, + }, + avatar: { + width: 40, + height: 40, + borderRadius: 20, + marginRight: 12, + }, + userName: { + fontSize: 16, + fontWeight: "bold", + flex: 1, + }, + followButton: { + backgroundColor: "#007AFF", + paddingHorizontal: 16, + paddingVertical: 8, + borderRadius: 20, + }, + followButtonText: { + color: "white", + fontSize: 14, + fontWeight: "bold", + }, + mediaWrapper: { + width: "100%", + height: 320, + backgroundColor: "#000", + borderRadius: 12, + overflow: "hidden", + }, + video: { + flex: 1, + }, + loadingContainer: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + loadingGif: { + width: 64, + height: 64, + }, + videoInfo: { + marginVertical: 12, + }, + videoTitle: { + fontSize: 16, + lineHeight: 22, + }, + interactionBar: { + flexDirection: "row", + }, + interactionButton: { + flexDirection: "row", + alignItems: "center", + marginRight: 32, + }, + interactionIcon: { + width: 24, + height: 24, + marginRight: 8, + }, + likedIcon: { + tintColor: "red", + }, + interactionText: { + fontSize: 14, + color: "#333", + }, + likedText: { + color: "red", + }, +}); diff --git a/Examples/LargeListExamples/SimpleVideoExample.js b/Examples/LargeListExamples/SimpleVideoExample.js new file mode 100644 index 00000000..7cbe1077 --- /dev/null +++ b/Examples/LargeListExamples/SimpleVideoExample.js @@ -0,0 +1,260 @@ +import React from "react"; +import { StyleSheet, Text, View, Image, FlatList, TouchableOpacity } from "react-native"; +import { LargeList } from "../../src"; +import { MediaWrapper } from "../../src"; + +export class SimpleVideoExample extends React.Component { + static navigationOptions = { + title: "Simple Video Example", + }; + + state = { + likedItems: {}, + comments: {}, + }; + + render() { + return ( + 0} + heightForIndexPath={this._heightForIndexPath} + renderIndexPath={this._renderIndexPath} + /> + ); + } + + _getData = () => { + return [ + { + items: [ + // Banner + { type: "banner", id: 1, images: this._getBannerImages() }, + // Video items with mock video placeholders + { + type: "video", + id: 1, + title: "Beautiful Nature", + thumbnail: "https://images.unsplash.com/photo-1501426026826-31c667bdf23d?w=600", + user: "Nature Lover" + }, + { + type: "video", + id: 2, + title: "Ocean Waves", + thumbnail: "https://images.unsplash.com/photo-1518717758536-85ae29035b6d?w=600", + user: "Beach Life" + }, + { + type: "video", + id: 3, + title: "Mountain Adventure", + thumbnail: "https://images.unsplash.com/photo-1500049517218-60249511432c?w=600", + user: "Adventure Club" + }, + ], + }, + ]; + }; + + _getBannerImages = () => { + return [ + { id: 1, url: "https://images.unsplash.com/photo-1501426026826-31c667bdf23d?w=300" }, + { id: 2, url: "https://images.unsplash.com/photo-1518717758536-85ae29035b6d?w=300" }, + { id: 3, url: "https://images.unsplash.com/photo-1500049517218-60249511432c?w=300" }, + ]; + }; + + _heightForIndexPath = ({ section, row }) => { + const item = this._getData()[section].items[row]; + if (item.type === "banner") { + return 200; + } else if (item.type === "video") { + return 450; + } + return 100; + }; + + _renderIndexPath = ({ section, row }, mediaWrapperParam) => { + const item = this._getData()[section].items[row]; + + if (item.type === "banner") { + return this._renderBanner(item); + } else if (item.type === "video") { + return this._renderVideoItem(item, mediaWrapperParam); + } + + return null; + }; + + _renderBanner = (bannerItem) => { + return ( + + ( + + )} + keyExtractor={(item) => item.id.toString()} + /> + + ); + }; + + _renderVideoItem = (videoItem, mediaWrapperParam) => { + const isLiked = this.state.likedItems[videoItem.id] || false; + const commentCount = this.state.comments[videoItem.id] || 0; + + return ( + + {videoItem.user} + + ( + + Loading Video... + + )} + loadEndFunc="onLoad" + > + + + + ▶️ + + + + + {videoItem.title} + + + this._toggleLike(videoItem.id)} + > + + {isLiked ? "❤️ Liked" : "🤍 Like"} + + + + this._addComment(videoItem.id)} + > + + 💬 Comment ({commentCount}) + + + + console.log("Share")} + > + 📤 Share + + + + ); + }; + + _toggleLike = (id) => { + this.setState((prevState) => ({ + likedItems: { + ...prevState.likedItems, + [id]: !prevState.likedItems[id], + }, + })); + }; + + _addComment = (id) => { + this.setState((prevState) => ({ + comments: { + ...prevState.comments, + [id]: (prevState.comments[id] || 0) + 1, + }, + })); + }; +} + +const styles = StyleSheet.create({ + bannerContainer: { + height: 200, + width: "100%", + }, + bannerImage: { + width: 300, + height: 200, + marginHorizontal: 8, + }, + videoItem: { + padding: 12, + backgroundColor: "white", + marginBottom: 8, + }, + userName: { + fontSize: 14, + fontWeight: "bold", + marginBottom: 8, + }, + mediaWrapper: { + width: "100%", + height: 300, + backgroundColor: "#000", + }, + videoPlaceholder: { + flex: 1, + position: "relative", + }, + videoThumbnail: { + flex: 1, + }, + playButton: { + position: "absolute", + top: "50%", + left: "50%", + transform: [{ translateX: -30 }, { translateY: -30 }], + width: 60, + height: 60, + borderRadius: 30, + backgroundColor: "rgba(255, 255, 255, 0.7)", + justifyContent: "center", + alignItems: "center", + }, + playButtonText: { + fontSize: 30, + }, + loadingContainer: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + videoTitle: { + fontSize: 16, + marginTop: 12, + }, + interactionBar: { + flexDirection: "row", + marginTop: 12, + }, + interactionButton: { + marginRight: 24, + }, + interactionText: { + fontSize: 14, + }, + likedText: { + color: "red", + }, +}); diff --git a/Examples/LargeListExamples/VideoBannerExample.js b/Examples/LargeListExamples/VideoBannerExample.js new file mode 100644 index 00000000..efbec739 --- /dev/null +++ b/Examples/LargeListExamples/VideoBannerExample.js @@ -0,0 +1,203 @@ +import React from "react"; +import { StyleSheet, Text, View, Image, FlatList, TouchableOpacity } from "react-native"; +import { LargeList } from "../../src"; +import { MediaWrapper } from "../../src"; +import Video from "react-native-video"; + +export class VideoBannerExample extends React.Component { + static navigationOptions = { + title: "VideoBannerExample", + }; + + state = { + likedItems: {}, + comments: {}, + }; + + render() { + return ( + 0} + heightForIndexPath={this._heightForIndexPath} + renderIndexPath={this._renderIndexPath} + /> + ); + } + + _getData = () => { + return [ + { + items: [ + // Banner item at the first position + { type: "banner", data: this._getBannerData() }, + // Video items + { type: "video", id: 1, url: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", title: "Big Buck Bunny" }, + { type: "video", id: 2, url: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4", title: "Elephants Dream" }, + { type: "video", id: 3, url: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4", title: "For Bigger Blazes" }, + ], + }, + ]; + }; + + _getBannerData = () => { + return [ + { id: 1, image: "https://images.unsplash.com/photo-1542744173-8e7e53415bb0?w=300" }, + { id: 2, image: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=300" }, + { id: 3, image: "https://images.unsplash.com/photo-1551963831-b3b1ca40c98e?w=300" }, + ]; + }; + + _heightForIndexPath = ({ section, row }) => { + const item = this._getData()[section].items[row]; + if (item.type === "banner") { + return 200; + } else if (item.type === "video") { + return 400; + } + return 100; + }; + + _renderIndexPath = ({ section, row }, mediaWrapperParam) => { + const item = this._getData()[section].items[row]; + + if (item.type === "banner") { + return this._renderBanner(item.data); + } else if (item.type === "video") { + return this._renderVideoItem(item, mediaWrapperParam); + } + + return null; + }; + + _renderBanner = (bannerData) => { + return ( + + ( + + )} + keyExtractor={(item) => item.id.toString()} + /> + + ); + }; + + _renderVideoItem = (item, mediaWrapperParam) => { + const isLiked = this.state.likedItems[item.id] || false; + const commentCount = this.state.comments[item.id] || 0; + + return ( + + ( + + Loading... + + )} + loadEndFunc="onLoad" + > + + {item.title} + + this._toggleLike(item.id)} + > + + {isLiked ? "❤️ Liked" : "🤍 Like"} + + + this._addComment(item.id)} + > + + 💬 Comment ({commentCount}) + + + + + ); + }; + + _toggleLike = (id) => { + this.setState((prevState) => ({ + likedItems: { + ...prevState.likedItems, + [id]: !prevState.likedItems[id], + }, + })); + }; + + _addComment = (id) => { + this.setState((prevState) => ({ + comments: { + ...prevState.comments, + [id]: (prevState.comments[id] || 0) + 1, + }, + })); + }; +} + +const styles = StyleSheet.create({ + bannerContainer: { + height: 200, + width: "100%", + }, + bannerImage: { + width: 300, + height: 200, + marginHorizontal: 8, + }, + videoItem: { + padding: 16, + }, + mediaWrapper: { + width: "100%", + height: 300, + backgroundColor: "#000", + }, + video: { + flex: 1, + }, + loadingContainer: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + videoTitle: { + fontSize: 18, + fontWeight: "bold", + marginTop: 12, + }, + interactionBar: { + flexDirection: "row", + marginTop: 12, + }, + interactionButton: { + marginRight: 24, + }, + interactionText: { + fontSize: 16, + }, + likedText: { + color: "red", + }, +}); diff --git a/Examples/LargeListExamples/VideoListExample.js b/Examples/LargeListExamples/VideoListExample.js new file mode 100644 index 00000000..d745ef16 --- /dev/null +++ b/Examples/LargeListExamples/VideoListExample.js @@ -0,0 +1,192 @@ +/* + * @Author: 石破天惊 + * @email: shanshang130@gmail.com + * @Date: 2021-07-29 17:30:00 + * @LastEditTime: 2021-07-29 17:30:00 + * @LastEditors: 石破天惊 + * @Description: 视频列表示例,包含横向滚动banner、视频播放、点赞和评论功能 + */ + +import React from "react"; +import { FlatList, Image, StyleSheet, Text, TouchableOpacity, View, Dimensions } from "react-native"; +import { LargeList } from "../../src"; +import Video from "react-native-video"; + +const { width } = Dimensions.get("window"); +const bannerWidth = width; +const bannerHeight = 200; +const videoHeight = 250; + +// 模拟数据 +const banners = [ + { id: 1, image: "https://images.unsplash.com/photo-1624916888351-a18f955a1b93?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=200&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYyNzQ0NTE1Nw&ixlib=rb-1.2.1&q=80&w=300" }, + { id: 2, image: "https://images.unsplash.com/photo-1626743702655-543c7789cd4d?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=200&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYyNzQ0NTI4MQ&ixlib=rb-1.2.1&q=80&w=300" }, + { id: 3, image: "https://images.unsplash.com/flagged/photo-1625836014110-880aae477f2b?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=200&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTYyNzQ0NTI5NQ&ixlib=rb-1.2.1&q=80&w=300" }, +]; + +const videoData = [ + { id: 1, videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", title: "Big Buck Bunny", likes: 1234, comments: 567 }, + { id: 2, videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4", title: "Elephants Dream", likes: 5678, comments: 901 }, + { id: 3, videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4", title: "For Bigger Blazes", likes: 9012, comments: 345 }, + { id: 4, videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4", title: "For Bigger Escapes", likes: 3456, comments: 789 }, + { id: 5, videoUrl: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4", title: "For Bigger Fun", likes: 7890, comments: 123 }, +]; + +export class VideoListExample extends React.Component { + static navigationOptions = { + title: "VideoListExample", + }; + + state = { + data: [ + { items: ["banner"] }, // 第一行是banner + ...videoData.map(item => ({ items: [item] })), // 后面是视频列表 + ], + likes: {}, + comments: {}, + }; + + // 渲染横向滚动banner + _renderBanner = () => { + return ( + + ( + + )} + /> + + ); + }; + + // 渲染视频项 + _renderVideoItem = ({ section, row }, mediaWrapperParam) => { + const item = this.state.data[section].items[row]; + if (section === 0) { + // 第一行是banner + return this._renderBanner(); + } + + const isLiked = this.state.likes[item.id] || false; + const likeCount = this.state.likes[item.id] ? item.likes + 1 : item.likes; + const commentCount = this.state.comments[item.id] || item.comments; + + return ( + + {item.title} + + ); + }; + + // 切换点赞状态 + _toggleLike = (id) => { + this.setState(prevState => ({ + likes: { + ...prevState.likes, + [id]: !prevState.likes[id], + }, + })); + }; + + // 显示评论 + _showComments = (id) => { + // 这里可以实现评论弹窗或跳转到评论页面 + console.log("Show comments for video", id); + }; + + // 计算行高 + _heightForIndexPath = ({ section, row }) => { + if (section === 0) { + // 第一行是banner + return bannerHeight; + } + // 视频行高 + return videoHeight + 80; + }; + + render() { + return ( + 0} + heightForIndexPath={this._heightForIndexPath} + renderIndexPath={this._renderVideoItem} + /> + ); + } +} + +const styles = StyleSheet.create({ + bannerContainer: { + width: width, + height: bannerHeight, + }, + bannerImage: { + width: bannerWidth, + height: bannerHeight, + }, + videoContainer: { + width: width, + padding: 16, + backgroundColor: "white", + }, + videoTitle: { + fontSize: 18, + fontWeight: "bold", + marginBottom: 8, + }, + video: { + width: width - 32, + height: videoHeight, + backgroundColor: "#000", + borderRadius: 8, + }, + interactionContainer: { + flexDirection: "row", + marginTop: 12, + }, + interactionButton: { + marginRight: 32, + }, + interactionText: { + fontSize: 16, + color: "#666", + }, + likedText: { + color: "red", + }, +}); diff --git a/Examples/LargeListExamples/index.js b/Examples/LargeListExamples/index.js index a94d91cd..7c6111b0 100644 --- a/Examples/LargeListExamples/index.js +++ b/Examples/LargeListExamples/index.js @@ -18,3 +18,7 @@ export * from "./ChatExample"; export * from "./FlatListExample"; export * from "./LargeListExamples"; export * from "./BigMediaExample"; +export * from "./VideoBannerExample"; +export * from "./CompleteVideoExample"; +export * from "./SimpleVideoExample"; +export * from "./VideoListExample";