前言
官方文档: react native
windows环境搭建 没有Node和yarn的需要安装
一、安装JDK 注意必须选择JDK的版本必须是1.8,并且是x64的版本
https://www.oracle.com/java/technologies/downloads/#java8-windows
二、配置JDK的环境变量 此电脑的属性里面选择高级系统设置,选择环境变量;
系统环境变量里面:
1 2 3 4 5 6 7 8 // 新建JAVA_HOME环境(下为默认地址),注意是JDK不是JRE的目录; C:\Program Files\Java\Jdk1.8.0_xxxx(版本号) // 新建CLASSPATH环境(如下) .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; // 在Path中添加下面两个路径 %JAVA_HOME%\bin %JAVA_HOME%\jre\bin // java -version 查看版本信息
三、Android Studio 安装
https://developer.android.google.cn/studio/
四、配置android sdk环境 1 2 3 4 5 6 7 8 // ANDROID_HOME环境(注意路径不能有中文) C:\Users\你的用户名\AppData\Local\Android\Sdk // PATH里面添加 %ANDROID_HOME%\platform-tools %ANDROID_HOME%\emulator %ANDROID_HOME%\tools %ANDROID_HOME%\tools\bin // 软件创建真机模拟器要选择Q开头的(Q API Level 29 image)
注意:一些问题
在启动安卓模拟器会出现Unable to locate adb报错?
解决:打开Android Studio软件上方file - Project Structure 里面设置Project SDK开始为No SDK,改为Add Android SDK…选择你的android sdk的目录即可(修改默认地址的也是自己的SDK目录)
android sdk不是默认安装C盘,启动不了模拟器?
首先确保Tools->SDK manager->Appearance&Behavior->System Settings->Android SDK的一些选项打上了对勾
解决:将C盘.android目录下的avd复制到你的android sdk的目录下,
1 2 // 新建ANDROID_SDK_HOME环境 // 路径为你复制后的avd路径
参考:https://blog.csdn.net/qq_48435252/article/details/114446668
启动模拟器后出现弹框提示?
解决:可能是你不是默认的android sdk地址,点击虚拟机右侧引导栏最下方的···按钮后-模拟器手机右下方;Settings设置,右下图的地址关闭按钮选择目录(为你的android sdk地址目录-platform-tools-adb.exe)
参考:https://blog.csdn.net/qq_45951779/article/details/119742277
项目 1 2 3 4 5 6 // 新建项目 npx react-native init AwesomeProject // 打开模拟器或者真机 // 真机需要打开发者模式,开发选项中选择usb线调试(模拟器和真机二选一) // 启动命令 yarn android
查看连接的设备 1 2 3 4 adb devices 显示 emulator-5554 offline 解决:模拟器选择Wipe Data (擦除数据即可) 启动模拟器界面的右侧下拉三角里包含此项
组件介绍 1 2 3 4 // ScrollView组件,在超出页面会上下滚动条,一般在最外面包裹 // FlatList循环 // 文本字符串必须在<Text></Text>组件中 // ScrollView和FlatList不能同时存在,否者报错
网络 1 2 3 4 5 6 7 8 9 10 11 12 13 // 原生的fetch(读取数据需要转换成json格式) function getMoviesFromApiAsync() { return fetch( 'xxx' ) .then((response) => response.json()) .then((responseJson) => { return responseJson.movies; }) .catch((error) => { console.error(error); }); }
React Navigation路由 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // 安装navigatin yarn add @react-navigation/native yarn add react-native-screens react-native-safe-area-context // 解决屏幕适配 yarn add @react-navigation/native-stack // 安卓配置 // 编辑android/app/src/main/java/你的包名/MainActivity.java // 顶部引用 import android.os.Bundle // 增加到MainActivity Class内部 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(null); } // iOS安装包后需要跑这个 npx pod-install ios
用法
说明
navigation.navigate(‘Details’)
Details是另一页面的name如果已经在Details页面了,则不能再次跳转
navigation.push(‘Details’)
已经在Details页面了,还可以继续跳转Details
navigation.goBack()
返回上一页
navigation.popToTop()
返回到一个 Stack 的第一个页面
用法
说明
navigation.navigate('Details', { id: 12, otherParam: '还可以同时传递其他参数'});
传id和其他参数到Details页面
navigation.setParams({ id: 2})
通过代码,手动设置参数
initialParams={{ id: 99 }}
初始参数,需要配置到中
navigation.navigate({ name: 'Home', params: { post: postText }, merge: true})
传递参数到上一个页面接收:React.useEffect(() => { if (route.params?.post) {}}, [route.params?.post])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 // 基本路由 import * as React from 'react'; import { View, Text, Button } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; // 接受navigation用来跳转 function Home({ navigation }) { return ( <View> <Text>这里是首页</Text> <Button title="跳转到详情页" onPress={() => { navigation.navigate("Details", { id: 12, }); }}/> </View> ); } // 需要接收route function Details({ route }) { // 接收id const { id } = route.params; return ( <View> <Text>这里是详情页</Text> <Text>你传过来的id是: {JSON.stringify(id)}</Text> </View> ); } const Stack = createNativeStackNavigator(); function App() { return ( // 导航器容器 <NavigationContainer> {/* 堆栈导航器 */} <Stack.Navigator> {/* 堆栈导航屏幕 */} <Stack.Screen name="Home" component={Home} options={{ title: '首页' }}/> <Stack.Screen name="Details" component={Details} /> </Stack.Navigator> </NavigationContainer> ); } export default App;
项目配置
用法
说明
options={({ route }) => ({ title: route.params.title })}
在stack中配置,会取得上一页传递过来的title参数
navigation.setOptions({ title: ‘标题更新了!’ })}
通过代码,手动设置title
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 // 组件里面跳转设置参数 <Button title="跳转到详情页" onPress={() => navigation.navigate('Details', { title: '我自定义的标题' }) } /> // 自定义组件,设置Header Bar function LogoTitle() { return ( <Image style={{ width: 150, height: 38 }} source={{ uri: 'xxx' }} /> ); } // 导出App function App() { return ( <NavigationContainer> <Stack.Navigator screenOptions={{ headerStyle: { backgroundColor: '#f4511e', }, headerTintColor: '#fff', headerTitleStyle: { fontWeight: 'bold', }, }}> <Stack.Screen name="Home" component={HomeScreen} options={{ headerTitle: props => <LogoTitle {...props} /> }} /> <Stack.Screen name="Details" component={DetailsScreen} // 接收route,只能放在这里 options={({ route }) => ({ title: route.params.title })} /> </Stack.Navigator> </NavigationContainer> ); } // js修改标题 <Button title="修改标题" onPress={() => navigation.setOptions({ title: '标题更新了!' })} />
顶部按钮 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import * as React from 'react'; import { Text, Button } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; function HomeScreen({ navigation }) { const [count, setCount] = React.useState(0); React.useLayoutEffect(() => { navigation.setOptions({ headerRight: () => ( <Button onPress={() => setCount(c => c + 1)} title="增加计数" /> ), }); }, [navigation]); return <Text style={{ fontSize: 24 }}>计数: {count}</Text>; } const Stack = createNativeStackNavigator(); function App() { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} options={{ title: '首页' }} /> </Stack.Navigator> </NavigationContainer> ); } export default App;
底部 Tab 栏 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 // 安装 yarn add @react-navigation/bottom-tabs // 安装图标 // 地址:https://oblador.github.io/react-native-vector-icons/ yarn add react-native-vector-icons // 安卓配置android/app/build.gradle // 增加以下内容到最底部 apply from: "../../node_modules/react-native-vector-icons/fonts.gradle" // iOS配置-修改/ios/Podfile // 增加以下内容到最底部 pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons' // cd ios 进入ios目录,执行 pod update // 修改 index.js // ... import Ionicons from 'react-native-vector-icons/Ionicons'; AppRegistry.registerComponent(appName, () => App); Ionicons.loadFont(); // Tab 里都有多个页面 import * as React from 'react'; import { Text, View, Button } from 'react-native'; // 引用图标库 import Ionicons from 'react-native-vector-icons/Ionicons'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; // 引用Tab import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { NavigationContainer } from '@react-navigation/native'; // 详情页 function DetailsScreen() { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Text>这里是详情页!</Text> </View> ); } // 主页 function HomeScreen({ navigation }) { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Text>这里是首页!</Text> <Button title="跳转到详情页" onPress={() => navigation.navigate('Details')} /> </View> ); } // 设置页 function SettingsScreen({ navigation }) { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Text>这里是设置页!</Text> <Button title="跳转到详情页" onPress={() => navigation.navigate('Details')} /> </View> ); } // 首页里面的跳转 const HomeStack = createNativeStackNavigator(); function HomeStackScreen() { return ( <HomeStack.Navigator> <HomeStack.Screen name="Home" component={HomeScreen} /> <HomeStack.Screen name="Details" component={DetailsScreen} /> </HomeStack.Navigator> ); } // 设置页里面的跳转 const SettingsStack = createNativeStackNavigator(); function SettingsStackScreen() { return ( <SettingsStack.Navigator> <SettingsStack.Screen name="Settings" component={SettingsScreen} /> <SettingsStack.Screen name="Details" component={DetailsScreen} /> </SettingsStack.Navigator> ); } // Tab跳转 const Tab = createBottomTabNavigator(); export default function App() { return ( <NavigationContainer> <Tab.Navigator screenOptions={({ route }) => ({ headerShown: false, tabBarIcon: ({ focused, color, size }) => { let iconName; if (route.name === 'HomeStack') { iconName = focused ? 'home' : 'home-outline'; } else if (route.name === 'SettingsStack') { iconName = focused ? 'settings' : 'settings-outline'; } // 返回Ionicons图标 return <Ionicons name={iconName} size={size} color={color} />; }, // 激活 tabBarActiveTintColor: '#1f99b0', // 未选中 tabBarInactiveTintColor: 'gray', })}> <Tab.Screen name="HomeStack" component={HomeStackScreen} options={{ // 小徽章 tabBarBadge: 4, tabBarBadgeStyle: { color: '#fff', backgroundColor: '#67c1b5', }, }} /> <Tab.Screen name="SettingsStack" component={SettingsStackScreen} /> </Tab.Navigator> </NavigationContainer> ); }
全屏模态框 Modal 1 2 3 4 5 6 7 8 9 10 11 12 <NavigationContainer> <Stack.Navigator> <Stack.Group> <Stack.Screen name="Home" component={HomeScreen} /> <Stack.Screen name="Details" component={DetailsScreen} /> </Stack.Group> {/*模态框*/} <Stack.Group screenOptions={{ presentation: 'modal' }}> <Stack.Screen name="MyModal" component={ModalScreen} /> </Stack.Group> </Stack.Navigator> </NavigationContainer>
深层嵌套 Stack 的跳转、传参 1 2 3 4 5 6 7 // 跳转其他Tab页的某页 <Button title="跳转到SettingsStack的详情页" onPress={() => navigation.navigate('SettingsStack', { screen: 'Details',params: { title: 'xx' }}) } />
项目中注意 1 2 3 4 5 6 7 // 在拆分出去的组件中 import { useNavigation } from '@react-navigation/native'; // 组件接收props必须放在前面,且不能放在{}里面 // 里面不接收{navigation},是无效的 const Slides = (props,{xxx}) => { const navigation = useNavigation(); }
其他插件 1 2 3 4 5 6 7 8 9 10 11 12 // 安装webview yarn add react-native-webview // 使用 import { WebView } from 'react-native-webview'; <WebView onLoadProgress={({ nativeEvent }) => { // 设置 percent 为 0...1,有小数 setProgress(nativeEvent.progress); }} source={{ uri: 'xxx' }} /> // 参数source里面接收网页地址,onLoadProgress为加载进度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // 滚动Tab栏安装 yarn add react-native-scrollable-tab-view @react-native-community/viewpager // 在点击 Tab 栏的时候会报错 // 删除node_modules/react-native-scrollable-tab-view/index.js中, // 所有getNode()方法的调用 // 使用 import ScrollableTabView, { ScrollableTabBar } from 'react-native-scrollable-tab-view'; <ScrollableTabView style={styles.container} initialPage={0} renderTabBar={() => <ScrollableTabBar />} tabBarUnderlineStyle={{ xxx }} tabBarBackgroundColor={xxx} tabBarInactiveTextColor={xxx} tabBarActiveTextColor={xxx} tabBarTextStyle={{ xxx }}> <Text tabLabel="xxx">生活的内容</Text> </ScrollableTabView> // 表格 yarn add react-native-tableview-simple // 使用 import { Cell, Section, TableView } from 'react-native-tableview-simple'; <TableView appearance={'light'}> <Section header="浏览"> <Cell title="xxx"/> <Cell title="xxx"/> </Section> </TableView>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 // 安装-展开更多 yarn add react-native-collapsible react-native-linear-gradient // 使用 import React, { useState } from 'react'; import { Dimensions, StyleSheet, View, Text, TouchableOpacity } from 'react-native'; import Collapsible from 'react-native-collapsible'; import LinearGradient from 'react-native-linear-gradient'; import Ionicons from 'react-native-vector-icons/Ionicons'; // 获取宽度 const screen = Dimensions.get('window'); const HomeScreen = () => { const [collapsed, setCollapsed] = useState(true); return ( <View style={styles.container}> <View style={{ position: 'relative' }}> <Collapsible collapsed={collapsed} collapsedHeight={62} duration={300}> <Text style={styles.body}>xxx</Text> </Collapsible> <LinearGradient colors={['rgba(255, 255, 255, 0.1)', 'rgba(255, 255, 255,1)']} style={[styles.linearGradient, { height: collapsed ? 42 : 0 }]} /> </View> <TouchableOpacity onPress={() => setCollapsed(!collapsed)} style={{ marginTop: 12 }}> <Ionicons name={collapsed ? 'chevron-down' : 'chevron-up'} size={25} color={collapsed ? Colors.primary : '#8F8C90'} style={styles.collapseIcon} /> </TouchableOpacity> </View> ); }; const styles = StyleSheet.create({ container: { backgroundColor: '#fff', flex: 1, }, body: { fontSize: 13, color: '#241F25', lineHeight: 20, }, linearGradient: { position: 'absolute', bottom: 0, width: screen.width, }, collapseIcon: { textAlign: 'center', }, });
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // 安装react-native-share yarn add react-native-share // 使用 import * as React from 'react'; import { TouchableOpacity } from 'react-native'; import Share from 'react-native-share'; const HomeScreen = () => { // 定义分享参数 const shareOptions = { title: '分享的标题', message: '分享的消息', url: 'https://clwy.cn', subject: `来自「长乐未央」的分享`, }; return ( // 点击触发分享 <TouchableOpacity onPress={() => { Share.open(shareOptions).then(res => { console.log(res); }).catch(err => { err && console.log(err); }); }}> </TouchableOpacity> ); };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 // 安装视频播放 yarn add react-native-video // ios同理npx pod-install // Android修改android/build.gradle allprojects { repositories { .... # 保留其他的,添加下面的 jcenter() { content { includeModule("com.yqritc", "android-scalablevideoview") } } } } // 使用,对于android播放兼容 // 安装 yarn add react-native-orientation-locker yarn add @sayem314/react-native-keep-awake yarn add react-native-vector-icons yarn add react-native-clwy-video-player // 使用 import Video from 'react-native-clwy-video-player' const [fullscreen, setFullscreen] = React.useState(false); useLayoutEffect(() => { navigation.setOptions({ headerShown: !fullscreen }); }, [fullscreen, navigation]); const logo = 'xxx'; const image = 'xxx'; const source = 'xxx'; <Video url={source} autoPlay logo={logo} placeholder={image} hideFullScreenControl={false} onFullScreen={status => setFullscreen(status)} rotateToFullScreen />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 // 侧边栏安装 yarn add react-native-side-menu-updated // 使用 import SideMenu from 'react-native-side-menu-updated'; // 左侧列表组件 const Menu = props => { const { onItemSelected } = props; return ( <TouchableHighlight underlayColor="#ddd" onPress={() => onItemSelected('选择了列表')}> <Text>列表</Text> </TouchableHighlight> ); } // 父组件 const HomeScreen = () => { const [title, setTitle] = useState('选择了列表'); const [isOpen, setIsOpen] = useState(false); // 切换选择 const onMenuItemSelected = item => { setTitle(item); setIsOpen(false); }; // 左侧菜单 const menu = <Menu onItemSelected={onMenuItemSelected} />; // 展开左侧菜单 const updateMenuState = menuState => { setIsOpen(menuState); }; return ( <SideMenu menu={menu} isOpen={isOpen} onChange={menuState => updateMenuState(menuState)} disableGestures={true}> <View> <Text>{title}</Text> {/*切换按钮*/} <TouchableWithoutFeedback onPress={() => { setIsOpen(!isOpen); }}> <Text>点这里展开</Text> </TouchableWithoutFeedback> </View> </SideMenu> ); };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 // 选项卡切换 yarn add @react-native-segmented-control/segmented-control // 存储 yarn add @react-native-async-storage/async-storage // 使用 import AsyncStorage from '@react-native-async-storage/async-storage'; // 存字符串 const storeData = async () => { try { // name 是你自定义的 key 名 await AsyncStorage.setItem('name', 'aaron') } catch (e) { // 保存错误 } } // 读取字符串 const getData = async () => { try { // name 是之前存入的值 const name = await AsyncStorage.getItem('name') if(name !== null) { console.log(name) } } catch(e) { // 读取 name 错误 } } // 存对象 const storeData = async () => { try { const user = { name: 'aaron', sex: 'male' } // 将对象,转为json格式的字符串 const jsonValue = JSON.stringify(user) await AsyncStorage.setItem('user', jsonValue) } catch (e) { // 保存错误 } } // 读取对象 const getData = async () => { try { const jsonValue = await AsyncStorage.getItem('user') // 把 json 格式的字符串,转回对象 const user != null ? JSON.parse(jsonValue) : null console.log(user.name, user.sex) } catch(e) { // 读取 user 错误 } }