本文介绍了在React Native中开发鸿蒙(HarmonyOS)组件的方法,主要包括三个关键步骤:1) 学习鸿蒙开发基础,包括DevEco Studio、HarmonyOS SDK和Java/Ko
本文介绍了在React Native中开发鸿蒙(HarmonyOS)组件的方法,主要包括三个关键步骤:1) 学习鸿蒙开发基础,包括DevEco Studio、HarmonyOS SDK和Java/Kotlin语言;2) 提供三种集成方案:使用WebView加载网页版鸿蒙应用、通过Native Modules桥接原生代码、利用Deep Linking/Intent传递数据;3) 职业发展规划建议。此
在React Native中开发鸿组件(这里指的是鸿蒙(HarmonyOS)组件),你需要了解鸿蒙开发的基础以及如何在React Native项目中集成鸿蒙应用。鸿蒙OS是由华为开发的一个分布式操作系统,主要用于其智能设备,如手机、平板、智能手表等。
- 了解鸿蒙开发基础
首先,你需要熟悉鸿蒙OS的开发环境设置和基本开发流程。这包括:
- 开发工具:使用DevEco Studio作为开发IDE。
- SDK:下载并安装HarmonyOS SDK。
- 语言与框架:主要使用Java/Kotlin进行应用开发,但也可以通过C/C++扩展功能。
- 在React Native中集成鸿蒙应用
React Native本身主要用于Harmony和Harmony平台的开发,但你可以通过以下几种方式将鸿蒙应用集成到React Native项目中:
A. 使用WebView
一种简单的方法是使用WebView来加载鸿蒙应用的网页版或通过一个WebView桥接本地代码与鸿蒙应用。
-
在React Native中添加WebView:
npm install react-native-webview -
使用WebView加载鸿蒙应用的URL:
import React from 'react'; import { WebView } from 'react-native-webview'; const HarmonyApp = () => { return ( <WebView source={{ uri: 'https://your-harmony-app-url.com' }} style={{ flex: 1 }} /> ); }; export default HarmonyApp;
B. 使用Native Modules
创建一个Native Module来桥接React Native和鸿蒙原生应用。
-
在DevEco Studio中创建一个鸿蒙应用。
-
开发Native Module:创建一个Java/Kotlin模块,在其中实现与鸿蒙应用交互的逻辑。
-
在React Native中调用Native Module:使用
react-native-bridge或其他桥接库来调用鸿蒙原生模块。例如,使用
react-native-bridge:npm install react-native-bridge然后在JavaScript中调用:
import { NativeModules } from 'react-native'; const { HarmonyModule } = NativeModules;
C. 使用Deep Linking或Intent传递数据
如果你的鸿蒙应用支持Deep Linking或Intent传递数据,你可以在React Native中处理这些链接或Intent,并据此与鸿蒙应用交互。
- 职业发展规划和开发代码详情
对于职业发展规划,你可以考虑以下步骤:
- 学习鸿蒙开发:深入学习鸿蒙OS的APIs和开发工具。
- 实践项目:在项目中实践鸿蒙应用的开发与集成。
- 优化集成方案:不断优化React Native与鸿蒙应用的集成方案,提高用户体验和性能。
- 持续学习:关注鸿蒙OS的最新动态和更新,持续学习新技术和新特性。
- 分享和交流:参与开源项目,分享你的经验,与其他开发者交流。
通过这些步骤,你可以有效地在React Native项目中开发并集成鸿蒙组件,同时规划你的职业发展路径。
开发一个名为“植物养护智能助手”的React Native应用,涉及到多个技术层面,包括前端UI设计、后端数据处理以及可能的硬件接口(例如传感器数据采集)。下面,我将提供一个基本的开发指南和代码示例,帮助你开始这个项目。
- 环境准备
首先,确保你的开发环境已经安装了Node.js和React Native。你可以通过以下步骤安装React Native环境:
安装Node.js (如果尚未安装)
安装React Native CLI
npm install -g react-native-cli
创建一个新的React Native项目
react-native init PlantCareAssistant
进入项目目录
cd PlantCareAssistant
- 安装依赖
为了简化开发,可以使用一些现成的库来帮助处理植物养护相关的功能,例如使用react-native-vector-icons来添加图标、react-native-camera来处理图像识别等。
安装图标库
npm install react-native-vector-icons
安装相机库(可选,根据需求)
npm install react-native-camera
- 项目结构规划
创建一个基本的项目结构,例如:
PlantCareAssistant/
|-- Harmony/
|-- Harmony/
|-- src/
|-- components/
|-- screens/
|-- HomeScreen.js
|-- CareTipsScreen.js
|-- App.js
|-- package.json
- 开发界面组件
HomeScreen.js
import React from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome'; // 假设使用FontAwesome图标库
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<Text style={styles.title}>植物养护智能助手</Text>
<Button title="查看养护建议" onPress={() => navigation.navigate('CareTips')} />
<Icon name="leaf" size={50} color="green" /> {/* 显示一个叶子图标 */}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontSize: 24,
marginBottom: 20,
},
});
export default HomeScreen;
CareTipsScreen.js
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome'; // 使用相同图标库
const CareTipsScreen = () => {
return (
<View style={styles.container}>
<Text style={styles.title}>养护建议</Text>
<Text>请每天给植物浇水。</Text> {/* 示例文本 */}
<Icon name="info-circle" size={30} color="blue" /> {/* 信息图标 */}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontSize: 24,
marginBottom: 20,
},
});
export default CareTipsScreen;
- 导航设置(使用React Navigation)
安装React Navigation:
npm install @react-navigation/native @react-navigation/stack react-native-screens react-native-safe-area-context @react-navigation/native-stack @react-navigation/stack @react-navigation/bottom-tabs @react-navigation/material-top-tabs @react-navigation/material-bottom-tabs @react-navigation/drawer @react-navigation/elements @react-navigation/routers @react-navigation/web react-native-tab-view react-native-pager-view react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context react-native-svg expo @expo/vector-icons expo-linear-gradient expo-constants expo-linking --save` 确保所有依赖都正确安装。`npm install`。`npx pod-install
真实演示案例代码:
// app.tsx
import React, { useState } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, FlatList } from 'react-native';
// 图标库
const ICONS = {
sun: '☀️',
cloud: '☁️',
rain: '🌧️',
snow: '❄️',
storm: '⛈️',
wind: '💨',
humidity: '💧',
temperature: '🌡️',
};
const { width } = Dimensions.get('window');
// 天气预报类型
type WeatherForecast = {
id: string;
date: string;
dayOfWeek: string;
highTemp: number;
lowTemp: number;
condition: '晴' | '多云' | '雨' | '雪' | '阴' | '雷雨';
precipitation: number; // 百分比
humidity: number; // 百分比
windSpeed: number; // km/h
};
// 生活指数类型
type LifeIndex = {
id: string;
name: string;
value: string;
description: string;
icon: string;
};
// 空气质量类型
type AirQuality = {
id: string;
location: string;
aqi: number;
level: string;
pm25: number;
pm10: number;
o3: number;
};
// 主页面组件
const WeatherApp: React.FC = () => {
const [currentWeather] = useState({
location: '北京市',
temperature: 24,
condition: '多云',
feelsLike: 26,
high: 28,
low: 22,
humidity: 65,
windSpeed: 12,
uvIndex: 5,
visibility: 10,
});
const [forecast] = useState<WeatherForecast[]>([
{ id: '1', date: '今天', dayOfWeek: '周二', highTemp: 28, lowTemp: 22, condition: '多云', precipitation: 20, humidity: 65, windSpeed: 12 },
{ id: '2', date: '明天', dayOfWeek: '周三', highTemp: 30, lowTemp: 24, condition: '晴', precipitation: 0, humidity: 55, windSpeed: 10 },
{ id: '3', date: '周四', dayOfWeek: '周四', highTemp: 26, lowTemp: 20, condition: '雨', precipitation: 80, humidity: 75, windSpeed: 15 },
{ id: '4', date: '周五', dayOfWeek: '周五', highTemp: 24, lowTemp: 18, condition: '阴', precipitation: 30, humidity: 70, windSpeed: 8 },
{ id: '5', date: '周六', dayOfWeek: '周六', highTemp: 27, lowTemp: 21, condition: '晴', precipitation: 10, humidity: 60, windSpeed: 11 },
]);
const [lifeIndexes] = useState<LifeIndex[]>([
{ id: '1', name: '紫外线', value: '强', description: '外出需涂抹防晒霜', icon: '☀️' },
{ id: '2', name: '穿衣', value: '热', description: '适合穿短袖短裤', icon: '👕' },
{ id: '3', name: '洗车', value: '适宜', description: '适合洗车的好时机', icon: '🚗' },
{ id: '4', name: '感冒', value: '少发', description: '感冒机率较低', icon: '🤧' },
{ id: '5', name: '运动', value: '较适宜', description: '适合户外活动', icon: '🏃♂️' },
]);
const [airQuality] = useState<AirQuality>({
id: '1',
location: '北京市',
aqi: 85,
level: '良',
pm25: 45,
pm10: 78,
o3: 120,
});
const [hourlyForecast] = useState([
{ hour: '现在', temp: 24, condition: '多云' },
{ hour: '13:00', temp: 25, condition: '多云' },
{ hour: '14:00', temp: 26, condition: '多云' },
{ hour: '15:00', temp: 27, condition: '晴' },
{ hour: '16:00', temp: 27, condition: '晴' },
{ hour: '17:00', temp: 26, condition: '晴' },
{ hour: '18:00', temp: 25, condition: '多云' },
]);
const getConditionIcon = (condition: string) => {
switch (condition) {
case '晴':
return ICONS.sun;
case '多云':
return ICONS.cloud;
case '雨':
return ICONS.rain;
case '雪':
return ICONS.snow;
case '雷雨':
return ICONS.storm;
default:
return ICONS.cloud;
}
};
const getAQIColor = (aqi: number) => {
if (aqi <= 50) return '#00E400'; // 优
if (aqi <= 100) return '#FFFF00'; // 良
if (aqi <= 150) return '#FF7E00'; // 轻度污染
if (aqi <= 200) return '#FF0000'; // 中度污染
if (aqi <= 300) return '#99004C'; // 重度污染
return '#7E0023'; // 严重污染
};
const renderHourlyForecast = ({ item }: { item: typeof hourlyForecast[0] }) => (
<View style={styles.hourlyItem}>
<Text style={styles.hourlyTime}>{item.hour}</Text>
<Text style={styles.hourlyIcon}>{getConditionIcon(item.condition)}</Text>
<Text style={styles.hourlyTemp}>{item.temp}°</Text>
</View>
);
const renderDailyForecast = ({ item }: { item: WeatherForecast }) => (
<View style={styles.dailyItem}>
<Text style={styles.dailyDate}>{item.dayOfWeek}</Text>
<Text style={styles.dailyIcon}>{getConditionIcon(item.condition)}</Text>
<Text style={styles.dailyPrecipitation}>{item.precipitation}%</Text>
<Text style={styles.dailyHigh}>{item.highTemp}°</Text>
<Text style={styles.dailyLow}>{item.lowTemp}°</Text>
</View>
);
return (
<SafeAreaView style={styles.container}>
{/* 头部 */}
<View style={styles.header}>
<TouchableOpacity style={styles.locationButton}>
<Text style={styles.locationText}>{currentWeather.location}</Text>
<Text style={styles.locationIcon}>📍</Text>
</TouchableOpacity>
<View style={styles.headerActions}>
<TouchableOpacity style={styles.searchButton}>
<Text style={styles.searchIcon}>🔍</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.menuButton}>
<Text style={styles.menuIcon}>☰</Text>
</TouchableOpacity>
</View>
</View>
<ScrollView style={styles.content}>
{/* 当前天气卡片 */}
<View style={styles.currentWeatherCard}>
<View style={styles.currentWeatherHeader}>
<Text style={styles.currentDate}>{new Date().toLocaleDateString('zh-CN', { weekday: 'long', month: 'long', day: 'numeric' })}</Text>
<Text style={styles.currentCondition}>{currentWeather.condition}</Text>
</View>
<View style={styles.temperatureContainer}>
<Text style={styles.mainTemperature}>{currentWeather.temperature}°</Text>
<Text style={styles.feelsLike}>体感 {currentWeather.feelsLike}°</Text>
</div>
<View style={styles.weatherDetails}>
<View style={styles.detailItem}>
<Text style={styles.detailIcon}>{ICONS.humidity}</Text>
<Text style={styles.detailValue}>{currentWeather.humidity}%</Text>
<Text style={styles.detailLabel}>湿度</Text>
</View>
<View style={styles.detailItem}>
<Text style={styles.detailIcon}>{ICONS.wind}</Text>
<Text style={styles.detailValue}>{currentWeather.windSpeed}km/h</Text>
<Text style={styles.detailLabel}>风速</Text>
</View>
<View style={styles.detailItem}>
<Text style={styles.detailIcon}>{ICONS.temperature}</Text>
<Text style={styles.detailValue}>↑{currentWeather.high}° ↓{currentWeather.low}°</Text>
<Text style={styles.detailLabel}>高低温</Text>
</div>
</View>
</View>
{/* 逐时预报 */}
<Text style={styles.sectionTitle}>逐时预报</Text>
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.hourlyContainer}>
<FlatList
data={hourlyForecast}
renderItem={renderHourlyForecast}
keyExtractor={item => item.hour}
horizontal
showsHorizontalScrollIndicator={false}
/>
</ScrollView>
{/* 未来几天预报 */}
<Text style={styles.sectionTitle}>未来几天</Text>
<FlatList
data={forecast}
renderItem={renderDailyForecast}
keyExtractor={item => item.id}
horizontal
showsHorizontalScrollIndicator={false}
style={styles.dailyContainer}
/>
{/* 空气质量 */}
<View style={styles.airQualityCard}>
<Text style={styles.airQualityTitle}>空气质量</Text>
<View style={styles.aqiContainer}>
<View style={[styles.aqiCircle, { backgroundColor: getAQIColor(airQuality.aqi) }]}>
<Text style={styles.aqiValue}>{airQuality.aqi}</Text>
</div>
<Text style={styles.aqiLevel}>{airQuality.level}</Text>
</View>
<View style={styles.airDetails}>
<View style={styles.airDetailItem}>
<Text style={styles.airDetailLabel}>PM2.5</Text>
<Text style={styles.airDetailValue}>{airQuality.pm25}</Text>
</View>
<View style={styles.airDetailItem}>
<Text style={styles.airDetailLabel}>PM10</Text>
<Text style={styles.airDetailValue}>{airQuality.pm10}</Text>
</View>
<View style={styles.airDetailItem}>
<Text style={styles.airDetailLabel}>O₃</Text>
<Text style={styles.airDetailValue}>{airQuality.o3}</Text>
</View>
</div>
</View>
{/* 生活指数 */}
<Text style={styles.sectionTitle}>生活指数</Text>
<View style={styles.lifeIndexContainer}>
{lifeIndexes.map(index => (
<View key={index.id} style={styles.lifeIndexItem}>
<Text style={styles.lifeIndexIcon}>{index.icon}</Text>
<Text style={styles.lifeIndexName}>{index.name}</Text>
<Text style={styles.lifeIndexValue}>{index.value}</Text>
<Text style={styles.lifeIndexDescription}>{index.description}</Text>
</View>
))}
</div>
{/* 天气预警 */}
<View style={styles.warningCard}>
<Text style={styles.warningTitle}>天气预警</Text>
<Text style={styles.warningText}>⚠️ 预计明日下午有雷阵雨,请注意防范</Text>
<TouchableOpacity style={styles.warningButton}>
<Text style={styles.warningButtonText}>查看详情</Text>
</TouchableOpacity>
</div>
</ScrollView>
{/* 底部导航 */}
<View style={styles.bottomNav}>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>{ICONS.temperature}</Text>
<Text style={[styles.navText, { color: '#3b82f6' }]}>天气</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>📊</Text>
<Text style={styles.navText}>预报</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>🌊</Text>
<Text style={styles.navText}>降水</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.navItem}>
<Text style={styles.navIcon}>🔍</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',
},
locationButton: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#f1f5f9',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
},
locationText: {
fontSize: 14,
color: '#1e293b',
marginRight: 6,
},
locationIcon: {
fontSize: 16,
color: '#64748b',
},
headerActions: {
flexDirection: 'row',
alignItems: 'center',
},
searchButton: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: '#f1f5f9',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
searchIcon: {
fontSize: 18,
color: '#64748b',
},
menuButton: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: '#f1f5f9',
alignItems: 'center',
justifyContent: 'center',
},
menuIcon: {
fontSize: 18,
color: '#64748b',
},
content: {
flex: 1,
padding: 16,
},
currentWeatherCard: {
backgroundColor: '#3b82f6',
borderRadius: 16,
padding: 24,
marginBottom: 16,
},
currentWeatherHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
},
currentDate: {
fontSize: 16,
color: '#e0f2fe',
},
currentCondition: {
fontSize: 16,
color: '#e0f2fe',
},
temperatureContainer: {
alignItems: 'center',
marginBottom: 20,
},
mainTemperature: {
fontSize: 64,
fontWeight: 'bold',
color: '#ffffff',
},
feelsLike: {
fontSize: 16,
color: '#bae6fd',
},
weatherDetails: {
flexDirection: 'row',
justifyContent: 'space-around',
},
detailItem: {
alignItems: 'center',
},
detailIcon: {
fontSize: 20,
marginBottom: 4,
},
detailValue: {
fontSize: 14,
color: '#ffffff',
fontWeight: '500',
marginBottom: 2,
},
detailLabel: {
fontSize: 12,
color: '#bae6fd',
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1e293b',
marginVertical: 12,
},
hourlyContainer: {
marginBottom: 16,
},
hourlyItem: {
alignItems: 'center',
marginRight: 20,
backgroundColor: '#ffffff',
borderRadius: 12,
paddingVertical: 12,
paddingHorizontal: 16,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
hourlyTime: {
fontSize: 12,
color: '#64748b',
marginBottom: 6,
},
hourlyIcon: {
fontSize: 24,
marginBottom: 6,
},
hourlyTemp: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
},
dailyContainer: {
marginBottom: 16,
},
dailyItem: {
flexDirection: 'column',
alignItems: 'center',
marginRight: 20,
backgroundColor: '#ffffff',
borderRadius: 12,
paddingVertical: 16,
paddingHorizontal: 12,
minWidth: 60,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
dailyDate: {
fontSize: 12,
color: '#64748b',
marginBottom: 8,
},
dailyIcon: {
fontSize: 24,
marginBottom: 8,
},
dailyPrecipitation: {
fontSize: 12,
color: '#64748b',
marginBottom: 8,
},
dailyHigh: {
fontSize: 14,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 4,
},
dailyLow: {
fontSize: 12,
color: '#64748b',
},
airQualityCard: {
backgroundColor: '#ffffff',
borderRadius: 16,
padding: 20,
marginBottom: 16,
},
airQualityTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 16,
},
aqiContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 16,
},
aqiCircle: {
width: 60,
height: 60,
borderRadius: 30,
alignItems: 'center',
justifyContent: 'center',
marginRight: 16,
},
aqiValue: {
fontSize: 20,
fontWeight: 'bold',
color: '#ffffff',
},
aqiLevel: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
},
airDetails: {
flexDirection: 'row',
justifyContent: 'space-around',
},
airDetailItem: {
alignItems: 'center',
},
airDetailLabel: {
fontSize: 12,
color: '#64748b',
marginBottom: 4,
},
airDetailValue: {
fontSize: 14,
fontWeight: 'bold',
color: '#1e293b',
},
lifeIndexContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
marginBottom: 16,
},
lifeIndexItem: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 12,
width: (width - 48) / 2 - 8,
marginBottom: 12,
marginRight: 12,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
lifeIndexIcon: {
fontSize: 24,
marginBottom: 8,
},
lifeIndexName: {
fontSize: 14,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 4,
},
lifeIndexValue: {
fontSize: 12,
color: '#3b82f6',
fontWeight: '500',
marginBottom: 4,
},
lifeIndexDescription: {
fontSize: 12,
color: '#64748b',
},
warningCard: {
backgroundColor: '#fef3c7',
borderRadius: 12,
padding: 16,
marginBottom: 16,
},
warningTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#92400e',
marginBottom: 8,
},
warningText: {
fontSize: 14,
color: '#92400e',
marginBottom: 12,
lineHeight: 18,
},
warningButton: {
backgroundColor: '#f59e0b',
borderRadius: 8,
padding: 10,
alignItems: 'center',
},
warningButtonText: {
color: '#ffffff',
fontWeight: 'bold',
},
bottomNav: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#ffffff',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingVertical: 12,
},
navItem: {
alignItems: 'center',
flex: 1,
},
navIcon: {
fontSize: 20,
color: '#94a3b8',
marginBottom: 4,
},
navText: {
fontSize: 12,
color: '#94a3b8',
},
});
export default WeatherApp;

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

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

最后运行效果图如下显示:
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐

所有评论(0)