前言

官方文档: react native


windows环境搭建

没有Nodeyarn的需要安装

一、安装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)

注意:一些问题

  1. 在启动安卓模拟器会出现Unable to locate adb报错?

    解决:打开Android Studio软件上方file - Project Structure 里面设置Project SDK开始为No SDK,改为Add Android SDK…选择你的android sdk的目录即可(修改默认地址的也是自己的SDK目录)

  2. 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

  3. 启动模拟器后出现弹框提示?

    解决:可能是你不是默认的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;

项目配置

顶部 Header Bar

用法 说明
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 错误
}
}