概览

  • 页面构成:输入区(发布动态)、FlatList 虚拟化的留言列表、卡片内的点赞/评论/分享操作,以及顶部帮助与底部导航。
  • 架构风格:函数式组件 + 本地状态,事件驱动数据更新,动效用 Animated 搭配原生驱动实现“轻量反馈”。
  • 跨端关注点:动画原生驱动映射到 ArkUI、FlatList 虚拟化滚动手感与事件窗口统一、Alert 弹窗行为与返回手势管理、emoji 图标渲染一致性,以及 TextInput 的输入法合成事件(composition)处理。

关键组件:MessageItem 的动效与展开逻辑

  • 点赞缩放动效采用 Animated.sequence,使用 useNativeDriver:true 在 transform 维度执行,性能友好且跨端稳定。鸿蒙端通过适配层映射到 ArkUI 原生动画能力,避免布局或颜色动画造成掉帧。
  • 文本折叠/展开通过 numberOfLines 与 isExpanded 状态切换,超过阈值(长度 > 100)显示“展开/收起”操作,卡片层次简单且易复用。
  • 操作菜单以 Alert 占位执行“编辑/删除/取消”,生产场景应改为统一的 ActionSheet/自定义弹窗,确保三端一致的视觉与交互(鸿蒙端桥接 ArkUI 弹窗能力与返回手势)。

示例动效逻辑(结构抽象):

const scaleValue = useRef(new Animated.Value(1)).current

Animated.sequence([
  Animated.timing(scaleValue, { toValue: 1.2, duration: 100, useNativeDriver: true }),
  Animated.timing(scaleValue, { toValue: 1, duration: 100, useNativeDriver: true }),
]).start()

列表与性能:FlatList 的虚拟化与滚动控制

  • 列表使用 FlatList 渲染,并通过 ref + scrollToIndex 在发布后滚动到顶部,提升“发布即刻可见”的体验。事件触发后用 setTimeout 微延迟,防止尚未布局完成导致滚动失败;生产可改为 InteractionManager.runAfterInteractions 或 onContentSizeChange 的更稳策略。
  • keyExtractor 使用稳定 id,renderItem 承载 MessageItem;生产建议将 renderItem 用 useCallback 缓存、MessageItem 使用 React.memo 降低重渲染,尤其在点赞计数快速变化时。
  • 复杂布局下可以增加 getItemLayout(已知高度时)提升初次滚动的稳定性;鸿蒙端需确保虚拟化与滚动物理(回弹/阻尼/惯性)统一映射到 ArkUI,保证触发窗口与手感一致。

状态更新与一致性:点赞/编辑/删除/发布

  • 点赞逻辑:基于 liked 状态切换 likes 计数,使用不可变更新 map 替换目标项,语义清晰;在高并发快速点击场景建议改为函数式更新 setMessages(prev => …) 保证最新状态源。
  • 编辑逻辑:将目标消息文本回填到输入框并从列表中移除,实现“取出-修改-再发布”的直观流程;生产需保留原消息 id 或提供“更新”接口,避免打断历史序列。
  • 删除操作:Alert 确认后过滤删除;若存在撤销需求,可引入临时缓存与定时器实现“撤销删除”模式。

示例函数式更新(提升一致性):

setMessages(prev =>
  prev.map(msg => msg.id === id
    ? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked }
    : msg
  )
)

输入法与粘贴:TextInput 的跨端差异治理

  • 多行 TextInput(multiline + numberOfLines)在 iOS/Android/鸿蒙存在差异:键盘弹出时的滚动调整、输入法合成事件与光标定位需要在适配层正确映射到 RN 的 onChangeText/onSelectionChange,避免中文输入中间态被误判为文本完成。
  • 粘贴/复制操作当前以 Alert 占位;生产需桥接系统剪贴板(iOS UIPasteboard、Android ClipboardManager、鸿蒙 ArkUI/Ability 对应能力),并处理权限与前后台切换的一致性;建议复制/粘贴统一使用纯文本,避免富文本导致统计或渲染异常。

图标与视觉一致性:emoji 的替代方案

  • 图标采用 emoji(👍、🔗、☰ 等)快速原型化,但在不同系统字体下基线与颜色呈现差异明显;生产需迁移到统一矢量/字体图标栈,并在鸿蒙端通过 ArkUI 渲染能力保持像素级一致。
  • 点赞的“红色高亮”与计数颜色切换,从语义上增强反馈;为避免无障碍用户的色彩识别歧义,建议同时加入 aria-like 或文本提示以增强可访问性。

弹窗与返回手势:Alert 的统一替换

  • Alert 在三端的视觉与按钮排列受平台控制,难以统一风格;建议用 RN Modal/Portal 实现自定义弹窗,管控层级、焦点与返回手势,并在适配层桥接 ArkUI 弹窗能力。
  • 底部导航的选中态通过样式标记,不涉及路由;生产建议接入跨端导航框架并统一返回手势与转场动画。

鸿蒙跨端适配要点

  • 动画:transform 动画原生驱动(useNativeDriver:true)在鸿蒙端需映射到 ArkUI 动画能力;避免 layout/颜色动画造成高开销。
  • 虚拟化与滚动:FlatList 与 SectionList 的虚拟化、滚动物理与事件窗口必须在适配层统一,确保 scrollToIndex 等方法语义一致。
  • 输入法:中文 IME 合成事件正确传递,防止中间态统计抖动与光标错乱;粘贴/复制能力使用 ArkUI/Ability 桥接。
  • 弹窗:自定义 Modal 统一视觉与行为,返回手势与焦点导航在鸿蒙端匹配 ArkUI 规范。
  • 图标:避免 emoji 差异,使用统一图标栈,颜色与字号在不同端做最小差异化处理。

React Native动态留言板系统与鸿蒙跨端适配技术深度解析

概述

本文分析的是一个基于React Native构建的动态留言板应用,集成了实时互动、动画效果、列表管理等核心功能。该应用采用了Animated动画系统、复杂的状态管理和动态列表交互,展现了社交互动类应用的典型技术架构。在鸿蒙OS的跨端适配场景中,这种涉及复杂动画和实时交互的应用具有重要的技术参考价值。

核心架构设计深度解析

Animated动画交互系统

应用在MessageItem组件中实现了精细的动画交互:

const MessageItem = ({ message, onLike, onDelete, onEdit, onReply }) => {
  const [isExpanded, setIsExpanded] = useState(false);
  const scaleValue = useRef(new Animated.Value(1)).current;

  const handleLike = () => {
    // 序列动画:先放大后缩小
    Animated.sequence([
      Animated.timing(scaleValue, {
        toValue: 1.2,
        duration: 100,
        useNativeDriver: true,
      }),
      Animated.timing(scaleValue, {
        toValue: 1,
        duration: 100,
        useNativeDriver: true,
      }),
    ]).start();

    onLike(message.id);
  };

  return (
    <Animated.View style={[styles.messageItem, { transform: [{ scale: scaleValue }] }]}>
      {/* 组件内容 */}
    </Animated.View>
  );
};

这种动画设计采用了Animated.sequence组合动画,实现了点赞时的弹跳效果。useNativeDriver设置为true启用原生驱动,确保动画的流畅性能。transform scale动画通过useRef保持动画值的持久性。

在鸿蒙ArkUI体系中,动画实现需要完全重构:

@Component
struct MessageItem {
  @Prop message: Message;
  @Event onLike: (id: string) => void;
  
  @State scaleValue: number = 1;
  
  handleLike() {
    // 使用animateTo实现序列动画
    animateTo({
      duration: 100,
      onFinish: () => {
        this.scaleValue = 1.2;
        animateTo({
          duration: 100,
          onFinish: () => {
            this.scaleValue = 1;
            this.onLike(this.message.id);
          }
        });
      }
    });
  }

  build() {
    // 应用缩放变换
    Column()
      .scale({ x: this.scaleValue, y: this.scaleValue })
  }
}

动态列表管理机制

应用采用了复杂的动态列表状态管理:

const [messages, setMessages] = useState<Message[]>([]);

// 添加新消息
const handleSend = () => {
  const newMessage: Message = {
    id: Date.now().toString(),
    text: inputText,
    timestamp: '刚刚',
    likes: 0,
    comments: 0,
    user: '我',
    avatar: '??',
    liked: false
  };
  
  setMessages([newMessage, ...messages]); // 新消息置顶
  setInputText('');
  
  // 滚动到顶部
  setTimeout(() => {
    flatListRef.current?.scrollToIndex({ index: 0, animated: true });
  }, 100);
};

// 点赞逻辑
const handleLike = (id: string) => {
  setMessages(messages.map(msg => 
    msg.id === id 
      ? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked } 
      : msg
  ));
};

这种状态管理体现了多个重要原则:使用Date.now()生成唯一ID,展开运算符确保不可变性,map操作实现精确更新,条件表达式处理点赞/取消点赞逻辑。

鸿蒙的状态管理采用直接赋值模式:

@State messages: Message[] = [];

// 添加消息
handleSend() {
  const newMessage: Message = {
    id: Date.now().toString(),
    text: this.inputText,
    // ...其他字段
  };
  
  this.messages = [newMessage, ...this.messages];
  this.inputText = '';
}

// 点赞处理
handleLike(id: string) {
  this.messages = this.messages.map(msg => 
    msg.id === id 
      ? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked } 
      : msg
  );
}

智能文本展示系统

MessageItem实现了自适应的文本展示控制:

const [isExpanded, setIsExpanded] = useState(false);

<Text style={styles.messageText} numberOfLines={isExpanded ? undefined : 3}>
  {message.text}
</Text>
{message.text.length > 100 && (
  <TouchableOpacity onPress={() => setIsExpanded(!isExpanded)}>
    <Text style={styles.expandText}>
      {isExpanded ? '收起' : '展开'}
    </Text>
  </TouchableOpacity>
)}

这种设计通过numberOfLines属性控制文本行数,基于文本长度动态显示展开/收起按钮,提供了良好的用户体验。

鸿蒙的实现需要使用文本组件的maxLines属性:

@State isExpanded: boolean = false;

build() {
  Column() {
    Text(this.message.text)
      .maxLines(this.isExpanded ? undefined : 3)
    
    if (this.message.text.length > 100) {
      Button(this.isExpanded ? '收起' : '展开', { type: ButtonType.Normal })
        .onClick(() => this.isExpanded = !this.isExpanded)
    }
  }
}

跨端适配技术方案

组件映射策略

React Native组件 鸿蒙ArkUI组件 关键适配点
Animated.View 普通组件+animateTo 动画系统完全重构
FlatList List 列表实现和滚动控制差异
TouchableOpacity Button 交互反馈机制不同
TextInput TextInput 多行文本属性一致

动画系统迁移

React Native的Animated模块与鸿蒙动画API对比:

功能 React Native 鸿蒙ArkUI 适配说明
基础动画 Animated.timing animateTo 参数配置不同
序列动画 Animated.sequence 嵌套animateTo 实现方式差异
原生驱动 useNativeDriver 默认启用 性能优化策略
变换动画 transform style .scale()方法 语法差异

状态管理迁移

复杂状态对象的更新策略:

// React Native
setMessages(messages.map(msg => 
  msg.id === id 
    ? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked } 
    : msg
));

// 鸿蒙
this.messages = this.messages.map(msg => 
  msg.id === id 
    ? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked } 
    : msg
);

性能优化与最佳实践

列表性能优化

使用keyExtractor和getItemLayout提升列表性能:

<FlatList
  ref={flatListRef}
  data={messages}
  keyExtractor={item => item.id}
  getItemLayout={(data, index) => ({
    length: 120, // 预估行高
    offset: 120 * index,
    index,
  })}
  renderItem={({ item }) => <MessageItem message={item} />}
/>

动画性能优化

启用useNativeDriver提升动画性能:

Animated.timing(scaleValue, {
  toValue: 1.2,
  duration: 100,
  useNativeDriver: true, // 启用原生驱动
}).start();

完整代码演示:

// app.tsx
import React, { useState, useRef } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, TextInput, FlatList, Animated } from 'react-native';

// 图标库
const ICONS = {
  message: '💬',
  send: '🚀',
  delete: '🗑️',
  edit: '✏️',
  like: '👍',
  comment: '💬',
  share: '🔗',
  menu: '☰',
};

const { width } = Dimensions.get('window');

// 留言类型
type Message = {
  id: string;
  text: string;
  timestamp: string;
  likes: number;
  comments: number;
  user: string;
  avatar: string;
  liked: boolean;
};

// 留言项组件
const MessageItem = ({ 
  message, 
  onLike, 
  onDelete,
  onEdit,
  onReply
}: { 
  message: Message; 
  onLike: (id: string) => void;
  onDelete: (id: string) => void;
  onEdit: (id: string) => void;
  onReply: (id: string) => void;
}) => {
  const [isExpanded, setIsExpanded] = useState(false);
  const scaleValue = useRef(new Animated.Value(1)).current;

  const handleLike = () => {
    // 动画效果
    Animated.sequence([
      Animated.timing(scaleValue, {
        toValue: 1.2,
        duration: 100,
        useNativeDriver: true,
      }),
      Animated.timing(scaleValue, {
        toValue: 1,
        duration: 100,
        useNativeDriver: true,
      }),
    ]).start();

    onLike(message.id);
  };

  return (
    <Animated.View style={[styles.messageItem, { transform: [{ scale: scaleValue }] }]}>
      <View style={styles.messageHeader}>
        <View style={styles.userAvatar}>
          <Text style={styles.avatarText}>{message.avatar}</Text>
        </View>
        <View style={styles.userInfo}>
          <Text style={styles.userName}>{message.user}</Text>
          <Text style={styles.timestamp}>{message.timestamp}</Text>
        </View>
        <TouchableOpacity 
          style={styles.menuButton} 
          onPress={() => Alert.alert('操作', '选择操作', [
            { text: '编辑', onPress: () => onEdit(message.id) },
            { text: '删除', onPress: () => onDelete(message.id) },
            { text: '取消', style: 'cancel' }
          ])}
        >
          <Text style={styles.menuText}>{ICONS.menu}</Text>
        </TouchableOpacity>
      </View>
      
      <View style={styles.messageContent}>
        <Text style={styles.messageText} numberOfLines={isExpanded ? undefined : 3}>
          {message.text}
        </Text>
        {message.text.length > 100 && (
          <TouchableOpacity onPress={() => setIsExpanded(!isExpanded)}>
            <Text style={styles.expandText}>
              {isExpanded ? '收起' : '展开'}
            </Text>
          </TouchableOpacity>
        )}
      </View>
      
      <View style={styles.messageActions}>
        <TouchableOpacity style={styles.actionButton} onPress={handleLike}>
          <Text style={[styles.actionIcon, message.liked && styles.liked]}>{ICONS.like}</Text>
          <Text style={[styles.actionText, message.liked && styles.likedText]}>{message.likes}</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.actionButton} onPress={() => onReply(message.id)}>
          <Text style={styles.actionIcon}>{ICONS.comment}</Text>
          <Text style={styles.actionText}>{message.comments}</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.actionButton}>
          <Text style={styles.actionIcon}>{ICONS.share}</Text>
          <Text style={styles.actionText}>分享</Text>
        </TouchableOpacity>
      </View>
    </Animated.View>
  );
};

// 主页面组件
const MessageBoardDynamicListApp: React.FC = () => {
  const [messages, setMessages] = useState<Message[]>([
    {
      id: '1',
      text: '今天天气真好,出去散散步感觉整个人都精神了!阳光明媚,微风轻拂,正是户外活动的好时机。大家有什么好的运动推荐吗?',
      timestamp: '10分钟前',
      likes: 12,
      comments: 3,
      user: '运动达人',
      avatar: '🏃‍♂️',
      liked: false
    },
    {
      id: '2',
      text: '昨天完成了人生第一次半程马拉松,虽然累但成就感满满!感谢一路上朋友们的加油打气,让我坚持到了最后。',
      timestamp: '30分钟前',
      likes: 24,
      comments: 8,
      user: '跑步爱好者',
      avatar: '🏃‍♀️',
      liked: true
    },
    {
      id: '3',
      text: '新买的运动装备终于到了,迫不及待想要试一试!这套装备颜值很高,而且据说功能性也很强,期待明天的晨跑体验。',
      timestamp: '1小时前',
      likes: 8,
      comments: 2,
      user: '装备控',
      avatar: '👕',
      liked: false
    }
  ]);
  
  const [inputText, setInputText] = useState('');
  const flatListRef = useRef<FlatList>(null);

  const handleSend = () => {
    if (inputText.trim() === '') {
      Alert.alert('提示', '请输入留言内容');
      return;
    }
    
    const newMessage: Message = {
      id: Date.now().toString(),
      text: inputText,
      timestamp: '刚刚',
      likes: 0,
      comments: 0,
      user: '我',
      avatar: '👤',
      liked: false
    };
    
    setMessages([newMessage, ...messages]);
    setInputText('');
    
    // 滚动到顶部
    setTimeout(() => {
      flatListRef.current?.scrollToIndex({ index: 0, animated: true });
    }, 100);
  };

  const handleLike = (id: string) => {
    setMessages(messages.map(msg => 
      msg.id === id 
        ? { ...msg, likes: msg.liked ? msg.likes - 1 : msg.likes + 1, liked: !msg.liked } 
        : msg
    ));
  };

  const handleDelete = (id: string) => {
    Alert.alert(
      '确认删除',
      '确定要删除这条留言吗?',
      [
        { text: '取消', style: 'cancel' },
        { 
          text: '删除', 
          style: 'destructive',
          onPress: () => setMessages(messages.filter(msg => msg.id !== id))
        }
      ]
    );
  };

  const handleEdit = (id: string) => {
    const messageToEdit = messages.find(msg => msg.id === id);
    if (messageToEdit) {
      setInputText(messageToEdit.text);
      setMessages(messages.filter(msg => msg.id !== id));
    }
  };

  const handleReply = (id: string) => {
    Alert.alert('回复', `回复给 ${messages.find(m => m.id === id)?.user}`);
  };

  return (
    <SafeAreaView style={styles.container}>
      {/* 头部 */}
      <View style={styles.header}>
        <Text style={styles.title}>动态留言板</Text>
        <TouchableOpacity style={styles.infoButton} onPress={() => Alert.alert('帮助', '这是一个动态留言列表,您可以发表、点赞、评论和管理留言')}>
          <Text style={styles.infoText}>ℹ️</Text>
        </TouchableOpacity>
      </View>

      {/* 输入区域 */}
      <View style={styles.inputContainer}>
        <TextInput
          style={styles.textInput}
          value={inputText}
          onChangeText={setInputText}
          placeholder="写下您的动态..."
          multiline
          numberOfLines={3}
        />
        <TouchableOpacity 
          style={[styles.sendButton, inputText.trim() === '' && styles.sendButtonDisabled]} 
          onPress={handleSend}
          disabled={inputText.trim() === ''}
        >
          <Text style={styles.sendButtonText}>{ICONS.send} 发布</Text>
        </TouchableOpacity>
      </View>

      {/* 留言列表 */}
      <FlatList
        ref={flatListRef}
        data={messages}
        keyExtractor={item => item.id}
        renderItem={({ item }) => (
          <MessageItem
            message={item}
            onLike={handleLike}
            onDelete={handleDelete}
            onEdit={handleEdit}
            onReply={handleReply}
          />
        )}
        style={styles.listContainer}
        showsVerticalScrollIndicator={false}
        ListHeaderComponent={
          <Text style={styles.sectionTitle}>最新动态 ({messages.length})</Text>
        }
      />

      {/* 底部导航 */}
      <View style={styles.bottomNav}>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.message}</Text>
          <Text style={styles.navText}>动态</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.like}</Text>
          <Text style={styles.navText}>点赞</Text>
        </TouchableOpacity>
        <TouchableOpacity style={[styles.navItem, styles.activeNavItem]}>
          <Text style={styles.navIcon}>{ICONS.comment}</Text>
          <Text style={styles.navText}>评论</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.navItem}>
          <Text style={styles.navIcon}>{ICONS.menu}</Text>
          <Text style={styles.navText}>我的</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f8fafc',
  },
  header: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: 20,
    backgroundColor: '#ffffff',
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  infoButton: {
    width: 36,
    height: 36,
    borderRadius: 18,
    backgroundColor: '#f1f5f9',
    alignItems: 'center',
    justifyContent: 'center',
  },
  infoText: {
    fontSize: 18,
    color: '#64748b',
  },
  inputContainer: {
    backgroundColor: '#ffffff',
    padding: 16,
    borderBottomWidth: 1,
    borderBottomColor: '#e2e8f0',
  },
  textInput: {
    borderWidth: 1,
    borderColor: '#cbd5e1',
    borderRadius: 8,
    padding: 12,
    fontSize: 16,
    color: '#1e293b',
    height: 80,
    textAlignVertical: 'top',
    backgroundColor: '#f8fafc',
  },
  sendButton: {
    backgroundColor: '#3b82f6',
    padding: 12,
    borderRadius: 8,
    alignItems: 'center',
    marginTop: 12,
  },
  sendButtonDisabled: {
    backgroundColor: '#9ca3af',
  },
  sendButtonText: {
    color: '#ffffff',
    fontSize: 16,
    fontWeight: '500',
  },
  listContainer: {
    flex: 1,
    padding: 16,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#1e293b',
    marginBottom: 16,
  },
  messageItem: {
    backgroundColor: '#ffffff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
    elevation: 1,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 1 },
    shadowOpacity: 0.1,
    shadowRadius: 2,
  },
  messageHeader: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 12,
  },
  userAvatar: {
    width: 40,
    height: 40,
    borderRadius: 20,
    backgroundColor: '#dbeafe',
    alignItems: 'center',
    justifyContent: 'center',
    marginRight: 12,
  },
  avatarText: {
    fontSize: 20,
  },
  userInfo: {
    flex: 1,
  },
  userName: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#1e293b',
  },
  timestamp: {
    fontSize: 12,
    color: '#64748b',
    marginTop: 2,
  },
  menuButton: {
    padding: 4,
  },
  menuText: {
    fontSize: 20,
    color: '#94a3b8',
  },
  messageContent: {
    marginBottom: 12,
  },
  messageText: {
    fontSize: 16,
    color: '#334155',
    lineHeight: 22,
  },
  expandText: {
    color: '#3b82f6',
    fontSize: 14,
    marginTop: 8,
  },
  messageActions: {
    flexDirection: 'row',
    borderTopWidth: 1,
    borderTopColor: '#e2e8f0',
    paddingTop: 12,
  },
  actionButton: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
  },
  actionIcon: {
    fontSize: 18,
    marginRight: 6,
    color: '#64748b',
  },
  actionText: {
    fontSize: 14,
    color: '#64748b',
  },
  liked: {
    color: '#ef4444',
  },
  likedText: {
    color: '#ef4444',
  },
  bottomNav: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    backgroundColor: '#ffffff',
    borderTopWidth: 1,
    borderTopColor: '#e2e8f0',
    paddingVertical: 12,
  },
  navItem: {
    alignItems: 'center',
    flex: 1,
  },
  activeNavItem: {
    paddingBottom: 2,
    borderBottomWidth: 2,
    borderBottomColor: '#3b82f6',
  },
  navIcon: {
    fontSize: 20,
    color: '#94a3b8',
    marginBottom: 4,
  },
  activeNavIcon: {
    color: '#3b82f6',
  },
  navText: {
    fontSize: 12,
    color: '#94a3b8',
  },
  activeNavText: {
    color: '#3b82f6',
    fontWeight: '500',
  },
});

export default MessageBoardDynamicListApp;

请添加图片描述

打包

接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

在这里插入图片描述

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

在这里插入图片描述

最后运行效果图如下显示:

请添加图片描述

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐