react-navigation
安装核心包
yarn add @react-navigation/native
安装@react-navigation/native本身依赖的相关包
react-native-reanimated:动画库
react-native-gesture-handler:手势库
react-native-screens:使用原生代码实现screen容器可以提高性能流畅度
react-native-safe-area-context:可以让我们的组件渲染在一个安全的区域(比如有些移动设备是异性的,刘海屏、挖孔屏等)
@react-native-community/masked-view:堆栈式导航器所依赖的库
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
React Native 0.60以上,链接是自动的,因此不需要运行react原生链接。
但在iOS开发上,则需要安装pod来完成链接
cd ios
pod install
打开android/app/src/main/java/<your package name>/MainActivity.java
加入以下代码
public class MainActivity extends ReactActivity {
// ...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
// ...
}
在该文件顶部加入
import android.os.Bundle;
在项目入口文件引入@react-navigation/native
import { NavigationContainer } from '@react-navigation/native';
如何使用堆栈式导航器
安装核心库
yarn add @react-navigation/stack
比如说写一个home组件
import React from 'react';
import { View, Text } from 'react-native';
class Home extends React.Component {
render() {
return (
<View>
<Text>Home</Text>
</View>
)
}
}
export default Home;
再写一个Detail组件
import React from 'react';
import { View, Text } from 'react-native';
class Detail extends React.Component {
render() {
return (
<View>
<Text>Detail</Text>
</View>
)
}
}
export default Detail;
然后在navigator下新建个文件夹index.tsx
import React from 'react';
import {NavigationContainer} from '@react-navigation/native'
import {createStackNavigator} from '@react-navigation/stack'
import Home from '@/pages/Home';
import Detail from '@/pages/Detail';
type RootStackPareamList = {
Home: undefined;
Detail:undefined;
}
let Stack = createStackNavigator<RootStackPareamList>();
/*{
Navigator, // 导航器
Screen // 路由,也就是页面
}
*/
class Navigator extends React.Component {
render(){
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home}/>
<Stack.Screen name="Detail" component={Detail}/>
</Stack.Navigator>
</NavigationContainer>);
}
}
export default Navigator;
然后在src/index.tsx
import Navigator from '@/navigator/index';
export default Navigator;
可以发现Android和ios的标题显示不一样,这是因为ios和Android的默认设计风格不一样导致的。
如何统一ios和Android的标题风格呢
使用screanOptions属性就好,在Stack.Screen里也有options属性,这样就可以单独设置某个页面的标题样式了,还有一些常用的属性 如:headerTitle:'首页'
import React from 'react';
import {NavigationContainer} from '@react-navigation/native'
import {createStackNavigator} from '@react-navigation/stack'
import Home from '@/pages/Home';
import Detail from '@/pages/Detail';
type RootStackPareamList = {
Home: undefined;
Detail:undefined;
}
const Stack = createStackNavigator<RootStackPareamList>();
/*{
Navigator, // 导航器
Screen // 路由,也就是页面
}
*/
class Navigator extends React.Component {
render(){
return (
<NavigationContainer>
<Stack.Navigator
screanOptions={{
headerTitleAlign:'center',
}}>
<Stack.Screen options={{ headerTitleAlign:'left, headerTitle:'首页'}} name="Home" component={Home}/>
<Stack.Screen name="Detail" component={Detail}/>
</Stack.Navigator>
</NavigationContainer>);
}
}
export default Navigator;
页面间的跳转如何实现?
import React from 'react';
import {NavigationContainer} from '@react-navigation/native'
import {createStackNavigator} from '@react-navigation/stack'
import Home from '@/pages/Home';
import Detail from '@/pages/Detail';
import {
createStackNavigator,
StackNavigationProp,
} from '@react-navigation/stack'; // 自动引入
type RootStackPareamList = {
Home: undefined;
Detail:undefined;
}
// 加这一行
export type RootStackNavigation = StackNavigationProp<RootStackPareamList>
const Stack = createStackNavigator<RootStackPareamList>();
/*{
Navigator, // 导航器
Screen // 路由,也就是页面
}
*/
class Navigator extends React.Component {
render(){
return (
<NavigationContainer>
<Stack.Navigator
headerMode="float"
screanOptions={{
headerTitleAlign:'center',
}}>
<Stack.Screen options={{ headerTitleAlign:'left, headerTitle:'首页'}} name="Home" component={Home}/>
<Stack.Screen name="Detail" component={Detail}/>
</Stack.Navigator>
</NavigationContainer>);
}
}
export default Navigator;
tips:
headerMode="float"所有页面共用一个标题栏(ios的默认)
headerMode="none"没有标题栏
headerMode="screen"每个页面都有一个标题栏(Android的默认)
在Home组件中使用
import React from 'react';
import { View, Text, Button } from 'react-native';
import { RootStackNavigation } from '@/navigator/index'
interface IProps {
navigation:RootStackNavigation;
}
class Home extends React.Component<IProps> {
onPress = () => {
const {navigation} = this.props;
navigation.navigate("Detail");
}
render() {
return (
<View>
<Text>Home</Text>
<Button title="跳转到详情页" onPress={this.onPress}/>
</View>
)
}
}
export default Home;
目前这样跳转是比较生硬的。
如何产生一个跳转的动画效果?
让前一个页面的从左边划走,然后后一个页面从右往左进来,透明度从0到1渐变
可以使用headerStyleInterpolator:HeaderStyleInterpolators.forUIKit,ios就能实现上述效果
使用cardStyleInterpolator:CardStyleInterpolators.forHorizontalIOS,Android也能实现跟ios类似的效果了
<Stack.Navigator
headerMode="float"
screanOptions={{
headerTitleAlign:'center',
headerStyleInterpolator:HeaderStyleInterpolators.forUIKit,
cardStyleInterpolator:CardStyleInterpolators.forHorizontalIOS,
}}>
</Stack.Navigator>
但是可以发现ios是可以通过手势操作关闭详情页得,但是Android是不可以得,因为ios默认开启手势,Android默认不开启
使用gestureEnabled:true,开启手势系统,然后会神奇的发现,Android从左向右滑动还是无法关闭详情页,因为Android默认的手势方向是垂直方向,所以往下滑就能关闭详情页,如何让它像ios那样向右滑动可以关闭呢?
继续设置手势方向为水平即可:gestureDirection:'horizontal',
<Stack.Navigator
headerMode="float"
screanOptions={{
headerTitleAlign:'center',
headerStyleInterpolator:HeaderStyleInterpolators.forUIKit,
cardStyleInterpolator:CardStyleInterpolators.forHorizontalIOS,
gestureEnabled:true,
gestureDirection:'horizontal',
}}>
</Stack.Navigator>
仔细观察会发现,Android的显示没有ios美观。
如何针对Android写样式呢?
【安卓改善前效果如下图】
【改善后的Android效果】,左边阴影去掉了,也去掉了标题的下边框阴影
使用headerStyle
<Stack.Navigator
headerMode="float"
screanOptions={{
headerTitleAlign:'center',
headerStyleInterpolator:HeaderStyleInterpolators.forUIKit,
cardStyleInterpolator:CardStyleInterpolators.forHorizontalIOS,
gestureEnabled:true,
gestureDirection:'horizontal',
headerStyle: {
...Platform:select({
android: {
elevation:0,
borderBottomWidth:StyleSheet.hairlineWidth,
}
})
},
}}>
</Stack.Navigator>
页面跳转间如何传参呢?
这是开发种十分常见的场景,如进入详情页,肯定需要在跳转的时候传递id的
那么需要在src/navigator/index.tsx种修改(就是导航组件的位置,你的项目不一定在这个路径)
设置一下详情页接收参数的类型,因为后续在Detail需要用到这个类型,因此再加个export导出
export type RootStackPareamList = {
Home: undefined;
Detail:{
id:number;
}
}
然后再Home组件,增加传递参数的逻辑
// home组件里的那个跳转方法
onPress = () => {
const {navigation} = this.props;
navigation.navigate("Detail",{
id:100,
}); // 这里增加了传参~
}
在Detail组件接收即可
import React from 'react';
import { View, Text } from 'react-native';
import { RootStackParamList } from '@/navigator/index';
interface IProps {
route:RouteProp<RootStackParamList,'Detail'>;
}
class Detail extends React.Component<IProps> {
render() {
const { route } = this.props
return (
<View>
<Text>Detail</Text>
<Text>{route.params.id}</Text>
</View>
)
}
}
export default Detail;
后续再写一个标签导航器~