React Native【详解】内置 API
屏幕 Dimensions
获取屏幕信息
import { Dimensions } from "react-native";
export default function demo() {const { width, height, scale, fontScale } = Dimensions.get("window");console.log(width, height, scale, fontScale);
}
- 参数为 window 时,不包含顶部状态栏的高度
- 参数为 screen 时,包含顶部状态栏的高度
- 沉浸模式时,会状态栏状态栏,此时传参 window 和 screen 效果相同
监听屏幕变化
import { useEffect } from "react";
import { Dimensions } from "react-native";
export default function Demo() {useEffect(() => {// 添加监听const listener = Dimensions.addEventListener("change",({ window, screen }) => {console.log(window, screen);});return () => {// 移除监听listener.remove();};}, []);
}
平台 Platform
import { Platform, StyleSheet } from "react-native";
export default function Demo() {// 获取手机的操作系统console.log(Platform.OS); // ios or android or web or macos or tvos or windows or xbm// 获取手机的操作系统版本console.log(Platform.Version);// 获取手机品牌等底层信息console.log(Platform.constants);// 仅ios支持,判断是否是ipadconsole.log(Platform.isPad);// 判断是否是tvconsole.log(Platform.isTV);
}
const styles = StyleSheet.create({container: {// 随平台变化,动态设置样式...Platform.select({ios: { backgroundColor: "red" }, // ios平台的样式android: { backgroundColor: "blue" }, // android平台的样式web: { backgroundColor: "green" }, // web平台的样式}),},
});
样式 StyleSheet
import { StyleSheet } from "react-native";
定义样式 StyleSheet.create
const styles = StyleSheet.create({container: {flex: 1,alignItems: "center",justifyContent: "center",},
});
样式合并 StyleSheet.compose
计算结果为一个数组,但比直接写数组性能更好(因为多次使用时,不会重复生成数组对象)
const style1 = {color: "red", // 红色};const style2 = {fontSize: 20, // 字号};const style3 = StyleSheet.compose(style1, style2);console.log(style3); // console.log(style3); // [{"color": "red"}, {"fontSize": 20}]
样式平铺 StyleSheet.flatten
计算结果为一个对象,相同的样式会去重
const style1 = {color: "red", // 红色};const style2 = {fontSize: 20, // 字号};const style3 = StyleSheet.flatten([style1, style2]);console.log(style3); // {"color": "red", "fontSize": 20}
铺满全屏 StyleSheet.absoluteFill
为如下样式的简写,样式效果为铺满全屏
{"bottom": 0, "left": 0, "position": "absolute", "right": 0, "top": 0}
1 像素 StyleSheet.hairlineWidth
常用于很细小的分割线
console.log(StyleSheet.hairlineWidth); // 0.38095238095238093
为计算 1 像素的简写方式
1/Dimensions.get("screen").scale;
外链 Linking
打开外链 Linking.openURL
// 打开网页
Linking.openURL('https://example.com');// 拨打电话
Linking.openURL('tel:10086');// 发送短信
Linking.openURL('sms:10086');// 发送邮件
Linking.openURL('mailto:support@example.com?subject=Feedback&body=Hello');
打开地图定位
import { Linking } from 'react-native';// 经纬度定位(示例:北京天安门)
const latitude = 39.9042;
const longitude = 116.4074;
const label = '天安门广场';// 构建通用地理 URI
const url = Platform.select({ios: `maps://app?saddr&daddr=${latitude},${longitude}(${label})`,android: `geo:${latitude},${longitude}?q=${latitude},${longitude}(${label})`,
});Linking.openURL(url).catch(err => console.error('无法打开地图:', err));
打开其他应用
import { Linking } from 'react-native';// 检查应用是否安装
Linking.canOpenURL('应用协议://').then(supported => {if (supported) {// 可以打开// 打开应用Linking.openURL('应用协议://功能路径?参数=值');} else {// 未安装或不支持}});
实战范例
// 微信
Linking.openURL('weixin://'); // 打开微信
Linking.openURL('weixin://dl/business/?t=特定业务码'); // 打开微信特定业务// QQ
Linking.openURL('mqq://'); // 打开QQ
Linking.openURL('mqq://im/chat?chat_type=wpa&uin=123456789'); // 打开QQ聊天// 微博
Linking.openURL('sinaweibo://'); // 打开微博
Linking.openURL('sinaweibo://userinfo?uid=用户ID'); // 打开用户主页// 支付宝
Linking.openURL('alipay://'); // 打开支付宝
Linking.openURL('alipayqr://platformapi/startapp?saId=10000007'); // 打开扫一扫// 银联
Linking.openURL('uppay://'); // 打开银联
判断是否能打开外链 Linking.openURL
// 检查是否可以打开网页链接
Linking.canOpenURL('https://example.com').then(supported => {if (supported) {Linking.openURL('https://example.com');} else {console.log('无法打开此链接');// 可以提供备选方案,如显示错误信息}}).catch(err => console.error('检查链接时出错:', err));
打开应用设置 Linking.openSettings
// 打开应用的系统设置页面
Linking.openSettings().catch(err => console.error('无法打开设置:', err));
获取应用启动时的初始链接 Linking.getInitialURL
// 异步获取初始链接
async function getLaunchURL() {try {const url = await Linking.getInitialURL();if (url) {console.log('应用通过链接启动:', url);// 处理链接参数handleDeepLink(url);} else {console.log('应用正常启动,无初始链接');}} catch (error) {console.error('获取初始链接失败:', error);}
}
【Android 特有】Linking.sendIntent
功能 | Linking.openURL | Linking.sendIntent |
---|---|---|
平台支持 | iOS 和 Android | 仅 Android |
调用方式 | 通过 URL Scheme(如 weixin:// ) | 通过 Android Intent Action |
参数灵活性 | 只能传递简单 URL 参数 | 可传递复杂数据(如 URI、Bundle) |
适用场景 | 打开网页、应用或执行通用操作 | 精确控制 Android 组件启动 |
Linking.sendIntent(action, extras).then(success => console.log('Intent 发送成功:', success)).catch(error => console.error('Intent 发送失败:', error));
-
action 必要,字符串类型,表示 Intent 的动作,常见值如下:
'android.intent.action.VIEW' // 查看内容 'android.intent.action.SEND' // 分享内容 'android.intent.action.DIAL' // 拨号 'android.intent.action.CAMERA_BUTTON' // 相机按钮
-
extras 可选,对象类型,包含传递给 Intent 的额外数据
{type: 'text/plain', // MIME 类型packageName: 'com.example.app', // 目标应用包名flags: 0x10000000, // Intent 标志位uri: 'content://path/to/file', // 数据 URItext: '分享的文本内容', // 文本数据// 其他自定义键值对 }
分享文本内容
Linking.sendIntent('android.intent.action.SEND',{type: 'text/plain',text: '这是一段要分享的文本',subject: '分享主题'}
);
分享图片
Linking.sendIntent('android.intent.action.SEND',{type: 'image/jpeg',uri: 'content://com.example.app/fileprovider/images/image.jpg',flags: 0x10000000 // FLAG_GRANT_READ_URI_PERMISSION}
);
启动特定应用
Linking.sendIntent('android.intent.action.MAIN',{packageName: 'com.tencent.mm', // 微信包名className: 'com.tencent.mm.ui.LauncherUI' // 微信主界面类名}
);
打开系统设置页面
// 打开 Wi-Fi 设置
Linking.sendIntent('android.settings.WIFI_SETTINGS');// 打开应用详情页
Linking.sendIntent('android.settings.APPLICATION_DETAILS_SETTINGS',{uri: 'package:com.example.myapp'}
);
像素比率 PixelRatio
获取当前设备的像素比率
const ratio = PixelRatio.get();
将逻辑像素转换为物理像素
const layoutSize = 100; // 逻辑像素
const physicalPixels = PixelRatio.getPixelSizeForLayoutSize(layoutSize);
console.log(`${layoutSize} 逻辑像素 = ${physicalPixels} 物理像素`);
// 在 Pixel Ratio 为 3 的设备上:100 → 300
将布局尺寸四舍五入为最接近的物理像素值,避免模糊
const layoutSize = 100.5; // 逻辑像素
const roundedSize = PixelRatio.roundToNearestPixel(layoutSize);
console.log(`四舍五入后的尺寸: ${roundedSize}`);
// 在 Pixel Ratio 为 3 的设备上:100.5 → 100.333(301 物理像素 / 3)
获取字体缩放比例
const fontScale = PixelRatio.getFontScale();
console.log('字体缩放比例:', fontScale);
// 默认为 1.0,用户增大字体时可能为 1.2、1.5 等
【实战】图片资源适配
import { Image, PixelRatio } from 'react-native';const getImageSource = () => {const ratio = PixelRatio.get();if (ratio >= 3) {return require('./image@3x.png'); // 高分辨率} else if (ratio >= 2) {return require('./image@2x.png'); // 中等分辨率} else {return require('./image@1x.png'); // 低分辨率}
};<Image source={getImageSource()} style={{ width: 100, height: 100 }} />
【实战】边框宽度处理
确保 1px 边框在所有设备上显示清晰
import { PixelRatio, StyleSheet } from 'react-native';const styles = StyleSheet.create({border: {borderWidth: 1 / PixelRatio.get(), // 物理像素为 1px},
});
【实战】精确布局计算
在需要高精度的场景中(如图表、游戏),确保布局计算准确
const WIDTH = 375; // 设计稿宽度
const scale = PixelRatio.getWindowDimensions().width / WIDTH;const normalize = (size) => {const newSize = size * scale;return PixelRatio.roundToNearestPixel(newSize);
};// 使用示例
const titleSize = normalize(24);
console.log('适配后的字体大小:', titleSize);
【Android 特有】自定义返回键的行为 BackHandler
返回键事件监听
// 定义处理函数
const handleBackPress = () => {// 处理逻辑return true;
};// 添加监听器
BackHandler.addEventListener('hardwareBackPress', handleBackPress);// 移除监听器
BackHandler.removeEventListener('hardwareBackPress', handleBackPress);
退出应用
BackHandler.exitApp(); // 退出应用
- 【实战】阻止返回键直接退出应用
import React, { useEffect } from 'react';
import { BackHandler, Alert } from 'react-native';function App() {useEffect(() => {const backAction = () => {Alert.alert('确认退出', '确定要退出应用吗?', [{ text: '取消', onPress: () => null, style: 'cancel' },{ text: '确认', onPress: () => BackHandler.exitApp() },]);return true; // 阻止默认行为};const backHandler = BackHandler.addEventListener('hardwareBackPress',backAction);return () => backHandler.remove();}, []);return <YourAppContent />;
}
- 【实战】双击返回键退出应用
import React, { useState, useEffect } from 'react';
import { BackHandler, ToastAndroid } from 'react-native';function App() {const [lastBackPressed, setLastBackPressed] = useState(0);useEffect(() => {const backAction = () => {const now = Date.now();const delta = now - lastBackPressed;if (delta < 2000) { // 2秒内连续点击BackHandler.exitApp(); // 退出应用return true;}setLastBackPressed(now);ToastAndroid.show('再按一次退出应用', ToastAndroid.SHORT);return true;};const backHandler = BackHandler.addEventListener('hardwareBackPress',backAction);return () => backHandler.remove();}, [lastBackPressed]);return <YourAppContent />;
}
- 【实战】返回键导航回退
import React, { useEffect } from 'react';
import { BackHandler, NavigationContainer } from 'react-native';
import { createStackNavigator } from '@react-navigation/stack';const Stack = createStackNavigator();function App() {const navigation = useNavigation();useEffect(() => {const backAction = () => {if (navigation.canGoBack()) {navigation.goBack(); // 导航回退return true; // 已处理}return false; // 未处理,执行默认行为(退出应用)};const backHandler = BackHandler.addEventListener('hardwareBackPress',backAction);return () => backHandler.remove();}, [navigation]);return (<NavigationContainer><Stack.Navigator>{/* 路由配置 */}</Stack.Navigator></NavigationContainer>);
}
推荐第三方库的 useBackHandler
npm install @react-native-community/hooks
import { useBackHandler }from '@react-native-community/hooks'
useBackHandler(() => {// 按返回键,即退出应用return true;});
【Android 特有】权限 PermissionsAndroid
权限值
PermissionsAndroid.PERMISSIONS.CAMERA // 相机
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO // 麦克风
PermissionsAndroid.PERMISSIONS.READ_CONTACTS // 读取联系人
PermissionsAndroid.PERMISSIONS.WRITE_CONTACTS // 修改联系人
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION // 精确定位
PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION // 模糊定位
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE // 读取外部存储
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE // 写入外部存储
PermissionsAndroid.PERMISSIONS.SEND_SMS // 发送短信
PermissionsAndroid.PERMISSIONS.READ_SMS // 读取短信
权限结果
PermissionsAndroid.RESULTS.GRANTED // 已授予
PermissionsAndroid.RESULTS.DENIED // 已拒绝
PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN // 已拒绝且选中"不再询问"
如果要申请某个权限,还必须在 AndroidManifest.xml 中声明该权限
<uses-permission android:name="android.permission.CAMERA" />
检查权限状态 PermissionsAndroid.check
import { PermissionsAndroid } from 'react-native';async function checkCameraPermission() {const granted = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.CAMERA);if (granted) {console.log('已获得相机权限');} else {console.log('未获得相机权限');}
}
请求单个权限 PermissionsAndroid.request
async function requestCameraPermission() {try {const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA,{title: '相机权限请求',message: '需要相机权限以使用扫码功能',buttonPositive: '同意',buttonNegative: '拒绝',buttonNeutral: '稍后询问' // Android 6.0+ 可用});if (granted === PermissionsAndroid.RESULTS.GRANTED) {console.log('用户授予相机权限');} else {console.log('用户拒绝相机权限');}} catch (err) {console.warn('权限请求错误:', err);}
}
请求多个权限 PermissionsAndroid.requestMultiple
async function requestLocationAndContacts() {const permissions = [PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,PermissionsAndroid.PERMISSIONS.READ_CONTACTS];const results = await PermissionsAndroid.requestMultiple(permissions);if (results['android.permission.ACCESS_FINE_LOCATION'] === PermissionsAndroid.RESULTS.GRANTED &&results['android.permission.READ_CONTACTS'] === PermissionsAndroid.RESULTS.GRANTED) {console.log('用户授予所有请求的权限');} else {console.log('用户拒绝了部分权限');}
}
【实战】请求相机权限
import React, { useState, useEffect } from 'react';
import {Button,View,Text,PermissionsAndroid,ToastAndroid
} from 'react-native';function CameraPermissionExample() {const [hasCameraPermission, setHasCameraPermission] = useState(false);useEffect(() => {checkCameraPermission();}, []);// 检查权限const checkCameraPermission = async () => {const status = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.CAMERA);setHasCameraPermission(status);};// 请求权限const requestCameraPermission = async () => {try {const result = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA,{title: '相机权限',message: '需要相机权限以拍照上传',buttonPositive: '同意',buttonNegative: '拒绝'});if (result === PermissionsAndroid.RESULTS.GRANTED) {ToastAndroid.show('相机权限已获取', ToastAndroid.SHORT);setHasCameraPermission(true);} else if (result === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {ToastAndroid.show('请在设置中手动开启相机权限', ToastAndroid.LONG);} else {ToastAndroid.show('相机权限被拒绝', ToastAndroid.SHORT);}} catch (err) {console.error('权限请求错误:', err);}};return (<View style={{ padding: 20 }}><Text>相机权限状态: {hasCameraPermission ? '已授权' : '未授权'}</Text><Buttontitle={hasCameraPermission ? '打开相机' : '请求权限'}onPress={hasCameraPermission ? openCamera : requestCameraPermission}/></View>);
}// 打开相机的函数实现
const openCamera = () => {// 实现相机功能console.log('打开相机');
};
【实战】引导用户到设置页面手动授权
当用户拒绝权限并选择 “不再询问” 时,需要引导用户到设置页面手动授权:
import { Linking } from 'react-native';// ...const handlePermissionDenied = () => {Alert.alert('权限被拒','需要相机权限才能使用此功能,请在设置中授予权限',[{ text: '取消', style: 'cancel' },{text: '去设置',onPress: () => Linking.openSettings()}]);
};
振动 Vibration
Android 中
需要在 AndroidManifest.xml 中声明振动权限
<uses-permission android:name="android.permission.VIBRATE" />
// 振动 400 毫秒(默认持续时间)
Vibration.vibrate();
// 自定义持续时间(500 毫秒)
Vibration.vibrate(500);
// 停止振动
Vibration.cancel();
自定义振动模式
// 模式:等待 100ms → 振动 200ms → 等待 300ms → 振动 400ms
const pattern = [100, 200, 300, 400];// 振动一次,不循环
Vibration.vibrate(pattern, false);
// 循环振动(第二个参数为 true)
Vibration.vibrate(pattern, true);
IOS 中
- 无需配置,直接使用
- 每次振动,固定 400 ms ,无法自定义
- 自定义模式有差异
// 模式:等待 100ms → 振动 400ms → 等待 200ms → 振动 400ms ……
const pattern = [100, 200, 300, 400];
键盘 Keyboard
useEffect(() => {// 监听键盘显示事件const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow',(e) => {ToastAndroid.show(`键盘高度: ${e.endCoordinates.height}`, ToastAndroid.SHORT);});// 监听键盘隐藏事件const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide',() => {ToastAndroid.show('键盘已隐藏', ToastAndroid.SHORT);});// 组件卸载时移除监听return () => {keyboardDidShowListener.remove();keyboardDidHideListener.remove();};}, []);
// 关闭键盘Keyboard.dismiss();
【实战】点击空白区域关闭键盘
import React from 'react';
import { View, TextInput, TouchableWithoutFeedback, Keyboard } from 'react-native';const DismissKeyboardExample = () => {const dismissKeyboard = () => {Keyboard.dismiss();};return (<TouchableWithoutFeedback onPress={dismissKeyboard} accessible={false}><View style={{ flex: 1, padding: 16 }}><TextInputstyle={{ height: 40, borderColor: 'gray', borderWidth: 1, marginBottom: 16 }}placeholder="输入文本..."/><Text>点击空白区域关闭键盘</Text></View></TouchableWithoutFeedback>);
};