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

📌 开发环境声明:本文基于 React Native 0.72.90 版本进行开发适配


在这里插入图片描述

🚀 一、开篇引言

瀑布流布局是现代移动应用中常见的展示方式,广泛应用于图片社交、电商商品展示、新闻资讯等场景。react-native-waterfall-flow 是 React Native 社区中轻量级的瀑布流组件,支持多列布局、下拉刷新、上拉加载等核心功能。本文将带你深入了解如何在 HarmonyOS 平台上集成和使用这个实用的布局组件。

1.1 你将学到什么?

  • ✅ waterfall-flow 的核心概念与工作原理
  • ✅ HarmonyOS 平台的完整集成流程
  • ✅ 瀑布流布局与数据渲染
  • ✅ API 的深度解析
  • ✅ 实际应用场景的最佳实践

1.2 适用人群

  • 正在进行 React Native 鸿蒙化迁移的开发者
  • 需要实现瀑布流布局的开发者
  • 对跨平台列表组件开发感兴趣的技术爱好者

1.3 为什么选择 waterfall-flow?

特点 说明
轻量级 专注瀑布流布局,无冗余功能
跨平台一致 iOS、Android、HarmonyOS 表现一致
简单易用 API 类似 FlatList,学习成本低
功能实用 支持多列、刷新、加载更多
性能优秀 虚拟化列表,大数据量表现良好

📦 二、库概览

2.1 基本信息

项目 内容
库名称 react-native-waterfall-flow
版本信息 1.1.5 (RN 0.72/0.77)
官方仓库 https://github.com/axerjs/react-native-waterfall-flow
开源协议 MIT

2.2 版本兼容性

三方库版本 支持RN版本 是否支持Autolink
1.1.5 0.72/0.77 Yes

2.3 核心能力矩阵

能力项 描述 HarmonyOS 支持
多列布局 numColumns 属性 ✅ 完全支持
列表渲染 renderItem 方法 ✅ 完全支持
下拉刷新 onRefresh 属性 ✅ 完全支持
上拉加载 onEndReached 属性 ✅ 完全支持
滚动控制 scrollTo 系列方法 ✅ 完全支持
头部/尾部组件 ListHeader/Footer ✅ 完全支持

2.4 技术架构图

原生平台层

布局计算层

React Native 应用层

WaterfallFlow Component

renderItem

data

numColumns

Column Layout Engine

高度计算

位置分配

动态重排

Android
ScrollView

iOS
UIScrollView

HarmonyOS
List

2.5 典型应用场景

场景 描述 示例
图片社交 不规则图片展示 📷 小红书、Pinterest
电商商品 商品列表展示 🛒 淘宝、京东商品列表
新闻资讯 图文混排资讯 📰 新闻客户端、博客
素材资源 设计素材展示 🎨 图标库、壁纸应用

🔧 三、环境准备

3.1 安装依赖

在项目根目录执行以下命令:

npm install react-native-waterfall-flow@1.1.5

或使用 yarn:

yarn add react-native-waterfall-flow@1.1.5

3.2 验证安装

安装完成后,检查 package.json 文件中是否包含以下依赖:

{
  "dependencies": {
    "react-native-waterfall-flow": "1.1.5"
  }
}

3.3 添加类型声明

由于该库没有内置 TypeScript 类型定义,需要手动添加类型声明文件。

src/types 目录下创建 react-native-waterfall-flow.d.ts 文件:

declare module 'react-native-waterfall-flow' {
  import { ComponentType, ReactElement, Component } from 'react';
  import { ViewStyle } from 'react-native';

  export interface WaterfallFlowProps<T = any> {
    data: T[];
    renderItem: (info: { item: T; index: number; column: number }) => ReactElement | null;
    numColumns?: number;
    ListHeaderComponent?: ComponentType | ReactElement | null;
    ListFooterComponent?: ComponentType | ReactElement | null;
    ListEmptyComponent?: ComponentType | ReactElement | null;
    onEndReached?: () => void;
    onRefresh?: () => void;
    refreshing?: boolean;
    style?: ViewStyle;
    contentContainerStyle?: ViewStyle;
    refreshControl?: ReactElement;
    keyExtractor?: (item: T, index: number) => string;
    onEndReachedThreshold?: number;
  }

  export default class WaterfallFlow<T = any> extends Component<WaterfallFlowProps<T>> {
    scrollToEnd(params?: { animated?: boolean }): void;
    scrollToIndex(params: { index: number; animated?: boolean; viewPosition?: number }): void;
    scrollToItem(params: { item: any; animated?: boolean; viewPosition?: number }): void;
    scrollToOffset(params: { offset: number; animated?: boolean }): void;
  }
}

使用时需要正确定义数据类型和 ref:

import WaterfallFlow from 'react-native-waterfall-flow';

interface DataItem {
  id: string;
  title: string;
  height: number;
}

const waterfallRef = useRef<WaterfallFlow<DataItem>>(null);

const renderItem = ({ item, index }: { item: DataItem; index: number }) => (
  <View style={{ height: item.height }}>
    <Text>{item.title}</Text>
  </View>
);

⚙️ 四、原生配置

由于该库支持 Autolink,在 RN 0.72 版本下无需手动配置原生依赖。如果你的项目已接入 Autolink 框架,安装依赖后重新构建即可。

4.1 同步依赖

在 DevEco Studio 中点击右上角的 sync 按钮,或在命令行执行:

cd harmony/entry
ohpm install

📖 五、API 详解

5.1 核心属性

renderItem - 列表项渲染函数

用于渲染列表中的每一项,是瀑布流组件的核心属性。

方法签名:

renderItem: (info: { item: any, index: number, column: number }) => JSX.Element | null

参数说明:

参数名 类型 说明
item any 当前项的数据
index number 当前项在数据源中的索引
column number 当前项所在的列索引(从0开始)

示例:

const renderItem = ({ item, index, column }) => {
  return (
    <View style={[styles.item, { height: item.height }]}>
      <Text>{item.title}</Text>
      <Text>第 {column + 1} 列</Text>
    </View>
  );
};

data - 数据源

瀑布流的数据源,是一个数组,数组中的每个元素会作为 renderItem 的 item 参数传入。

类型: any[]

示例:

const data = [
  { id: '1', title: 'Item 1', height: 150 },
  { id: '2', title: 'Item 2', height: 200 },
  { id: '3', title: 'Item 3', height: 180 },
];

numColumns - 列数

指定瀑布流的列数,默认为 2 列。

类型: number

默认值: 2

示例:

<WaterfallFlow
  data={data}
  renderItem={renderItem}
  numColumns={3}
/>

ListHeaderComponent - 头部组件

在列表顶部渲染的组件,可以是 React 组件或渲染函数。

类型: React.ComponentType | React.ReactElement | null

示例:

// 方式一:直接传入组件
<WaterfallFlow
  ListHeaderComponent={<View><Text>列表头部</Text></View>}
/>

// 方式二:传入渲染函数
<WaterfallFlow
  ListHeaderComponent={() => <View><Text>列表头部</Text></View>}
/>

ListFooterComponent - 尾部组件

在列表底部渲染的组件,常用于显示加载状态。

类型: React.ComponentType | React.ReactElement | null

示例:

<WaterfallFlow
  ListFooterComponent={
    <View style={styles.footer}>
      <ActivityIndicator />
      <Text>加载中...</Text>
    </View>
  }
/>

ListEmptyComponent - 空列表组件

当 data 为空数组时显示的组件。

类型: React.ComponentType | React.ReactElement | null

示例:

<WaterfallFlow
  data={[]}
  ListEmptyComponent={
    <View style={styles.empty}>
      <Text>暂无数据</Text>
    </View>
  }
/>

5.2 交互属性

onEndReached - 滚动到底部回调

当列表滚动到底部时触发,常用于实现"加载更多"功能。

类型: () => void

示例:

const [loading, setLoading] = useState(false);

const onEndReached = async () => {
  if (loading) return;
  setLoading(true);
  const newData = await fetchMoreData();
  setData([...data, ...newData]);
  setLoading(false);
};

<WaterfallFlow
  data={data}
  renderItem={renderItem}
  onEndReached={onEndReached}
/>

onRefresh - 下拉刷新回调

下拉刷新时触发的回调函数,需要配合 refreshing 属性使用。

类型: () => void

示例:

const [refreshing, setRefreshing] = useState(false);

const onRefresh = async () => {
  setRefreshing(true);
  const newData = await fetchNewData();
  setData(newData);
  setRefreshing(false);
};

<WaterfallFlow
  data={data}
  renderItem={renderItem}
  onRefresh={onRefresh}
  refreshing={refreshing}
/>

refreshing - 刷新状态

控制是否显示刷新指示器。

类型: boolean

默认值: false


5.3 样式属性

style - 外层容器样式

瀑布流外层容器的样式,默认有 { flex: 1 } 样式。

类型: ViewStyle

示例:

<WaterfallFlow
  style={{ backgroundColor: '#f5f5f5' }}
/>

contentContainerStyle - 内容容器样式

瀑布流内容容器的样式,用于设置内边距等。

类型: ViewStyle

示例:

<WaterfallFlow
  contentContainerStyle={{ padding: 10 }}
/>

5.4 静态方法

scrollToEnd - 滚动到底部

滚动到列表底部。

方法签名:

scrollToEnd(params?: { animated?: boolean }): void

参数说明:

参数名 类型 必填 说明
animated boolean 是否使用动画,默认 true

示例:

const waterfallRef = useRef(null);

// 滚动到底部
waterfallRef.current?.scrollToEnd({ animated: true });

<WaterfallFlow ref={waterfallRef} />

scrollToIndex - 滚动到指定索引

将指定位置的元素滚动到可视区域。

方法签名:

scrollToIndex(params: { 
  index: number, 
  animated?: boolean,
  viewPosition?: number 
}): void

参数说明:

参数名 类型 必填 说明
index number 要滚动到的元素索引
animated boolean 是否使用动画,默认 true
viewPosition number 元素在屏幕中的位置:0顶部/0.5居中/1底部

示例:

// 滚动到第 10 个元素,并显示在屏幕中央
waterfallRef.current?.scrollToIndex({
  index: 10,
  animated: true,
  viewPosition: 0.5
});

scrollToOffset - 滚动到指定偏移

滚动到指定的偏移位置(像素)。

方法签名:

scrollToOffset(params: { offset: number, animated?: boolean }): void

参数说明:

参数名 类型 必填 说明
offset number 滚动的偏移量(像素)
animated boolean 是否使用动画,默认 true

示例:

// 滚动到顶部
waterfallRef.current?.scrollToOffset({ offset: 0 });

🎯 六、属性详解

属性名 描述 类型 必填 默认值 HarmonyOS 支持
renderItem 列表项渲染函数 function -
data 数据源 array -
numColumns 列数 number 2
ListHeaderComponent 头部组件 component null
ListFooterComponent 尾部组件 component null
ListEmptyComponent 空列表组件 component null
onEndReached 滚动到底部回调 function -
onRefresh 下拉刷新回调 function -
refreshing 刷新状态 boolean false
style 外层容器样式 object {}
contentContainerStyle 内容容器样式 object {}

💡 七、使用示例

7.1 基础瀑布流

最简单的瀑布流实现,展示两列数据。

适用场景: 快速实现基础的瀑布流布局。

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import WaterfallFlow from 'react-native-waterfall-flow';

interface DataItem {
  id: string;
  title: string;
  height: number;
}

const App = () => {
  const data: DataItem[] = [
    { id: '1', title: 'Item 1', height: 120 },
    { id: '2', title: 'Item 2', height: 180 },
    { id: '3', title: 'Item 3', height: 150 },
    { id: '4', title: 'Item 4', height: 200 },
    { id: '5', title: 'Item 5', height: 140 },
    { id: '6', title: 'Item 6', height: 170 },
  ];

  const renderItem = ({ item }: { item: DataItem }) => (
    <View style={[styles.item, { height: item.height }]}>
      <Text style={styles.text}>{item.title}</Text>
    </View>
  );

  return (
    <View style={styles.container}>
      <WaterfallFlow
        data={data}
        renderItem={renderItem}
        numColumns={2}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#f5f5f5' },
  item: {
    margin: 5,
    borderRadius: 8,
    backgroundColor: '#fff',
    justifyContent: 'center',
    alignItems: 'center',
  },
  text: { fontSize: 16, color: '#333' },
});

代码解析:

  • 定义 DataItem 接口描述数据类型
  • data 数组中每个对象包含 idtitleheight 属性
  • renderItem 使用类型注解 { item: DataItem } 避免隐式 any
  • numColumns={2} 设置为两列瀑布流

7.2 带下拉刷新和加载更多

完整的瀑布流交互实现,支持下拉刷新和上拉加载。

适用场景: 需要数据动态更新的场景,如图片社交、商品列表。

import React, { useState, useCallback } from 'react';
import {
  View,
  Text,
  StyleSheet,
  RefreshControl,
  ActivityIndicator,
} from 'react-native';
import WaterfallFlow from 'react-native-waterfall-flow';

interface DataItem {
  id: string;
  title: string;
  height: number;
}

function generateData(count: number, startId: number = 0): DataItem[] {
  return Array.from({ length: count }, (_, i) => ({
    id: `${startId + i + 1}`,
    title: `Item ${startId + i + 1}`,
    height: 100 + Math.random() * 150,
  }));
}

const App = () => {
  const [data, setData] = useState<DataItem[]>(generateData(10));
  const [refreshing, setRefreshing] = useState(false);
  const [loading, setLoading] = useState(false);

  const onRefresh = useCallback(() => {
    setRefreshing(true);
    setTimeout(() => {
      setData(generateData(10));
      setRefreshing(false);
    }, 1500);
  }, []);

  const onEndReached = useCallback(() => {
    if (loading) return;
    setLoading(true);
    setTimeout(() => {
      setData(prev => [...prev, ...generateData(5, prev.length)]);
      setLoading(false);
    }, 1000);
  }, [loading]);

  const renderItem = ({ item }: { item: DataItem }) => (
    <View style={[styles.item, { height: item.height }]}>
      <Text style={styles.text}>{item.title}</Text>
    </View>
  );

  const ListFooterComponent = () => (
    <View style={styles.footer}>
      {loading && <ActivityIndicator color="#00d4ff" />}
      <Text style={styles.footerText}>
        {loading ? '加载中...' : '上拉加载更多'}
      </Text>
    </View>
  );

  return (
    <View style={styles.container}>
      <WaterfallFlow
        data={data}
        renderItem={renderItem}
        numColumns={2}
        onRefresh={onRefresh}
        refreshing={refreshing}
        onEndReached={onEndReached}
        ListFooterComponent={ListFooterComponent}
        contentContainerStyle={styles.content}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#f5f5f5' },
  content: { padding: 10 },
  item: {
    margin: 5,
    borderRadius: 8,
    backgroundColor: '#fff',
    justifyContent: 'center',
    alignItems: 'center',
  },
  text: { fontSize: 16, color: '#333' },
  footer: { padding: 16, alignItems: 'center' },
  footerText: { color: '#888', fontSize: 14 },
});

代码解析:

  • 定义 DataItem 接口和 generateData 函数类型
  • onRefresh 实现下拉刷新,重置数据
  • onEndReached 实现上拉加载,追加新数据
  • loading 状态防止重复加载
  • ListFooterComponent 显示加载状态提示

7.3 图片瀑布流

模拟图片社交应用的瀑布流展示,带有图片加载效果。

适用场景: 图片社交、图库应用、壁纸应用。

import React, { useState } from 'react';
import {
  View,
  Text,
  StyleSheet,
  Image,
  TouchableOpacity,
} from 'react-native';
import WaterfallFlow from 'react-native-waterfall-flow';

interface ImageItem {
  id: string;
  url: string;
  width: number;
  height: number;
  likes: number;
}

const IMAGE_DATA: ImageItem[] = [
  {
    id: '1',
    url: 'https://picsum.photos/200/300?random=1',
    width: 200,
    height: 300,
    likes: 128,
  },
  {
    id: '2',
    url: 'https://picsum.photos/200/200?random=2',
    width: 200,
    height: 200,
    likes: 256,
  },
];

const App = () => {
  const [data, setData] = useState<ImageItem[]>(IMAGE_DATA);

  const renderItem = ({ item, index }: { item: ImageItem; index: number }) => {
    const imageHeight = (item.height / item.width) * 160;

    return (
      <TouchableOpacity
        style={[styles.card, { height: imageHeight + 60 }]}
        activeOpacity={0.9}
      >
        <Image
          source={{ uri: item.url }}
          style={[styles.image, { height: imageHeight }]}
          resizeMode="cover"
        />
        <View style={styles.cardFooter}>
          <Text style={styles.likes}>❤️ {item.likes}</Text>
        </View>
      </TouchableOpacity>
    );
  };

  return (
    <View style={styles.container}>
      <WaterfallFlow
        data={data}
        renderItem={renderItem}
        numColumns={2}
        contentContainerStyle={styles.content}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#1a1a2e' },
  content: { padding: 8 },
  card: {
    margin: 4,
    borderRadius: 12,
    backgroundColor: '#16213e',
    overflow: 'hidden',
  },
  image: { width: '100%' },
  cardFooter: {
    padding: 10,
    flexDirection: 'row',
    justifyContent: 'flex-end',
  },
  likes: { color: '#fff', fontSize: 14 },
});

代码解析:

  • 定义 ImageItem 接口描述图片数据类型
  • 根据图片宽高比动态计算显示高度
  • 使用 TouchableOpacity 实现点击交互
  • 卡片包含图片和点赞数信息

❓ 八、常见问题

8.1 常见问题解答

Q1: 瀑布流高度不一致怎么办?

A: 瀑布流组件会自动根据内容高度进行布局,确保每个 item 的高度是根据内容动态计算的。

Q2: 如何实现三列或更多列?

A: 通过 numColumns 属性设置列数,如 numColumns={3}

Q3: 下拉刷新不生效?

A: 确保同时设置了 onRefreshrefreshing 两个属性。

Q4: 如何滚动到指定位置?

A: 使用 ref 获取组件实例,调用 scrollToIndexscrollToOffset 方法。

Q5: 性能优化建议?

A:

  • 避免在 renderItem 中进行复杂计算
  • 使用 keyExtractor 优化列表更新
  • 合理使用 onEndReachedThreshold 控制加载时机

8.2 最佳实践

  1. 唯一标识:确保 data 中每项都有唯一的 id
  2. 防抖加载:在 onEndReached 中添加 loading 状态防止重复加载
  3. 合理分页:每次加载适量数据,避免一次性加载过多
  4. 图片优化:使用合适的图片尺寸,避免加载过大的图片

💻 九、完整示例代码

import React, { useState, useRef, useCallback } from 'react';
import {
  View,
  Text,
  StyleSheet,
  SafeAreaView,
  TouchableOpacity,
  RefreshControl,
  ActivityIndicator,
  Dimensions,
} from 'react-native';
import WaterfallFlow from 'react-native-waterfall-flow';

const { width: SCREEN_WIDTH } = Dimensions.get('window');
const CARD_WIDTH = (SCREEN_WIDTH - 40) / 2;

const COLORS = [
  '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4',
  '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F',
];

function generateData(count: number, startId: number = 0) {
  return Array.from({ length: count }, (_, i) => ({
    id: `${startId + i + 1}`,
    title: `卡片 ${startId + i + 1}`,
    description: `这是第 ${startId + i + 1} 个卡片的内容描述`,
    height: 120 + Math.random() * 100,
    color: COLORS[Math.floor(Math.random() * COLORS.length)],
    likes: Math.floor(Math.random() * 1000),
  }));
}

export default function App() {
  const [data, setData] = useState(generateData(12));
  const [refreshing, setRefreshing] = useState(false);
  const [loading, setLoading] = useState(false);
  const waterfallRef = useRef(null);

  const onRefresh = useCallback(() => {
    setRefreshing(true);
    setTimeout(() => {
      setData(generateData(12));
      setRefreshing(false);
    }, 1500);
  }, []);

  const onEndReached = useCallback(() => {
    if (loading) return;
    setLoading(true);
    setTimeout(() => {
      setData(prev => [...prev, ...generateData(6, prev.length)]);
      setLoading(false);
    }, 1000);
  }, [loading]);

  const scrollToTop = () => {
    waterfallRef.current?.scrollToOffset({ offset: 0, animated: true });
  };

  const scrollToEnd = () => {
    waterfallRef.current?.scrollToEnd({ animated: true });
  };

  const renderItem = ({ item, index }) => (
    <TouchableOpacity
      style={[styles.card, { height: item.height }]}
      activeOpacity={0.9}
    >
      <View style={[styles.cardHeader, { backgroundColor: item.color }]}>
        <Text style={styles.cardIndex}>#{index + 1}</Text>
      </View>
      <View style={styles.cardContent}>
        <Text style={styles.cardTitle}>{item.title}</Text>
        <Text style={styles.cardDesc} numberOfLines={2}>
          {item.description}
        </Text>
      </View>
      <View style={styles.cardFooter}>
        <Text style={styles.likes}>❤️ {item.likes}</Text>
      </View>
    </TouchableOpacity>
  );

  const ListHeaderComponent = () => (
    <View style={styles.header}>
      <Text style={styles.headerTitle}>瀑布流展示</Text>
      <Text style={styles.headerSubtitle}>{data.length} 个项目</Text>
    </View>
  );

  const ListFooterComponent = () => (
    <View style={styles.footer}>
      {loading ? (
        <View style={styles.loadingContainer}>
          <ActivityIndicator color="#00d4ff" />
          <Text style={styles.loadingText}>加载中...</Text>
        </View>
      ) : (
        <Text style={styles.footerText}>上拉加载更多</Text>
      )}
    </View>
  );

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.titleBar}>
        <Text style={styles.title}>瀑布流布局</Text>
      </View>

      <WaterfallFlow
        ref={waterfallRef}
        data={data}
        renderItem={renderItem}
        numColumns={2}
        ListHeaderComponent={ListHeaderComponent}
        ListFooterComponent={ListFooterComponent}
        onEndReached={onEndReached}
        onRefresh={onRefresh}
        refreshing={refreshing}
        contentContainerStyle={styles.content}
        refreshControl={
          <RefreshControl
            refreshing={refreshing}
            onRefresh={onRefresh}
            colors={['#00d4ff']}
            tintColor="#00d4ff"
          />
        }
      />

      <View style={styles.bottomBar}>
        <TouchableOpacity style={styles.actionButton} onPress={scrollToTop}>
          <Text style={styles.actionButtonText}>回到顶部</Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.actionButton} onPress={scrollToEnd}>
          <Text style={styles.actionButtonText}>滚动到底部</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#1a1a2e',
  },
  titleBar: {
    padding: 16,
    backgroundColor: '#16213e',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#fff',
    textAlign: 'center',
  },
  content: {
    paddingHorizontal: 8,
    paddingBottom: 16,
  },
  header: {
    padding: 16,
    alignItems: 'center',
  },
  headerTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#fff',
  },
  headerSubtitle: {
    fontSize: 14,
    color: '#888',
    marginTop: 4,
  },
  card: {
    margin: 4,
    borderRadius: 12,
    backgroundColor: '#16213e',
    overflow: 'hidden',
  },
  cardHeader: {
    height: 40,
    justifyContent: 'center',
    alignItems: 'center',
  },
  cardIndex: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#fff',
  },
  cardContent: {
    padding: 12,
    flex: 1,
  },
  cardTitle: {
    fontSize: 16,
    fontWeight: '600',
    color: '#fff',
    marginBottom: 4,
  },
  cardDesc: {
    fontSize: 12,
    color: '#888',
    lineHeight: 18,
  },
  cardFooter: {
    padding: 10,
    borderTopWidth: 1,
    borderTopColor: '#2a2a4a',
    flexDirection: 'row',
    justifyContent: 'flex-end',
  },
  likes: {
    fontSize: 12,
    color: '#00d4ff',
  },
  footer: {
    padding: 16,
    alignItems: 'center',
  },
  loadingContainer: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  loadingText: {
    marginLeft: 8,
    color: '#888',
  },
  footerText: {
    color: '#666',
    fontSize: 14,
  },
  bottomBar: {
    flexDirection: 'row',
    padding: 12,
    backgroundColor: '#16213e',
    gap: 12,
  },
  actionButton: {
    flex: 1,
    backgroundColor: '#00d4ff',
    paddingVertical: 12,
    borderRadius: 8,
    alignItems: 'center',
  },
  actionButtonText: {
    fontSize: 14,
    fontWeight: '600',
    color: '#1a1a2e',
  },
});

🔗 十、相关资源

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐