屏幕 Dimensions
获取屏幕信息
c
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 效果相同
监听屏幕变化
c
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
c
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支持,判断是否是ipad
console.log(Platform.isPad);
// 判断是否是tv
console.log(Platform.isTV);
}
const styles = StyleSheet.create({
container: {
// 随平台变化,动态设置样式
...Platform.select({
ios: { backgroundColor: "red" }, // ios平台的样式
android: { backgroundColor: "blue" }, // android平台的样式
web: { backgroundColor: "green" }, // web平台的样式
}),
},
});
样式 StyleSheet
c
import { StyleSheet } from "react-native";
定义样式 StyleSheet.create
c
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
});
样式合并 StyleSheet.compose
计算结果为一个数组,但比直接写数组性能更好(因为多次使用时,不会重复生成数组对象)
c
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
计算结果为一个对象,相同的样式会去重
c
const style1 = {
color: "red", // 红色
};
const style2 = {
fontSize: 20, // 字号
};
const style3 = StyleSheet.flatten([style1, style2]);
console.log(style3); // {"color": "red", "fontSize": 20}
铺满全屏 StyleSheet.absoluteFill
为如下样式的简写,样式效果为铺满全屏
css
{"bottom": 0, "left": 0, "position": "absolute", "right": 0, "top": 0}
1 像素 StyleSheet.hairlineWidth
常用于很细小的分割线
c
console.log(StyleSheet.hairlineWidth); // 0.38095238095238093
为计算 1 像素的简写方式
c
1/Dimensions.get("screen").scale;
外链 Linking
打开外链 Linking.openURL
c
// 打开网页
Linking.openURL('https://example.com');
// 拨打电话
Linking.openURL('tel:10086');
// 发送短信
Linking.openURL('sms:10086');
// 发送邮件
Linking.openURL('mailto:[email protected]?subject=Feedback&body=Hello');
打开地图定位
c
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));
打开其他应用
c
import { Linking } from 'react-native';
// 检查应用是否安装
Linking.canOpenURL('应用协议://')
.then(supported => {
if (supported) {
// 可以打开
// 打开应用
Linking.openURL('应用协议://功能路径?参数=值');
} else {
// 未安装或不支持
}
});
实战范例
c
// 微信
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
c
// 检查是否可以打开网页链接
Linking.canOpenURL('https://example.com')
.then(supported => {
if (supported) {
Linking.openURL('https://example.com');
} else {
console.log('无法打开此链接');
// 可以提供备选方案,如显示错误信息
}
})
.catch(err => console.error('检查链接时出错:', err));
打开应用设置 Linking.openSettings
c
// 打开应用的系统设置页面
Linking.openSettings()
.catch(err => console.error('无法打开设置:', err));
获取应用启动时的初始链接 Linking.getInitialURL
c
// 异步获取初始链接
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 组件启动 |
c
Linking.sendIntent(action, extras)
.then(success => console.log('Intent 发送成功:', success))
.catch(error => console.error('Intent 发送失败:', error));
-
action 必要,字符串类型,表示 Intent 的动作,常见值如下:
c'android.intent.action.VIEW' // 查看内容 'android.intent.action.SEND' // 分享内容 'android.intent.action.DIAL' // 拨号 'android.intent.action.CAMERA_BUTTON' // 相机按钮
-
extras 可选,对象类型,包含传递给 Intent 的额外数据
c{ type: 'text/plain', // MIME 类型 packageName: 'com.example.app', // 目标应用包名 flags: 0x10000000, // Intent 标志位 uri: 'content://path/to/file', // 数据 URI text: '分享的文本内容', // 文本数据 // 其他自定义键值对 }
分享文本内容
c
Linking.sendIntent(
'android.intent.action.SEND',
{
type: 'text/plain',
text: '这是一段要分享的文本',
subject: '分享主题'
}
);
分享图片
c
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
}
);
启动特定应用
c
Linking.sendIntent(
'android.intent.action.MAIN',
{
packageName: 'com.tencent.mm', // 微信包名
className: 'com.tencent.mm.ui.LauncherUI' // 微信主界面类名
}
);
打开系统设置页面
c
// 打开 Wi-Fi 设置
Linking.sendIntent('android.settings.WIFI_SETTINGS');
// 打开应用详情页
Linking.sendIntent(
'android.settings.APPLICATION_DETAILS_SETTINGS',
{
uri: 'package:com.example.myapp'
}
);
像素比率 PixelRatio
获取当前设备的像素比率
c
const ratio = PixelRatio.get();
将逻辑像素转换为物理像素
c
const layoutSize = 100; // 逻辑像素
const physicalPixels = PixelRatio.getPixelSizeForLayoutSize(layoutSize);
console.log(`${layoutSize} 逻辑像素 = ${physicalPixels} 物理像素`);
// 在 Pixel Ratio 为 3 的设备上:100 → 300
将布局尺寸四舍五入为最接近的物理像素值,避免模糊
c
const layoutSize = 100.5; // 逻辑像素
const roundedSize = PixelRatio.roundToNearestPixel(layoutSize);
console.log(`四舍五入后的尺寸: ${roundedSize}`);
// 在 Pixel Ratio 为 3 的设备上:100.5 → 100.333(301 物理像素 / 3)
获取字体缩放比例
c
const fontScale = PixelRatio.getFontScale();
console.log('字体缩放比例:', fontScale);
// 默认为 1.0,用户增大字体时可能为 1.2、1.5 等
【实战】图片资源适配
c
import { Image, PixelRatio } from 'react-native';
const getImageSource = () => {
const ratio = PixelRatio.get();
if (ratio >= 3) {
return require('./[email protected]'); // 高分辨率
} else if (ratio >= 2) {
return require('./[email protected]'); // 中等分辨率
} else {
return require('./[email protected]'); // 低分辨率
}
};
<Image source={getImageSource()} style={{ width: 100, height: 100 }} />
【实战】边框宽度处理
确保 1px 边框在所有设备上显示清晰
c
import { PixelRatio, StyleSheet } from 'react-native';
const styles = StyleSheet.create({
border: {
borderWidth: 1 / PixelRatio.get(), // 物理像素为 1px
},
});
【实战】精确布局计算
在需要高精度的场景中(如图表、游戏),确保布局计算准确
c
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
返回键事件监听
c
// 定义处理函数
const handleBackPress = () => {
// 处理逻辑
return true;
};
// 添加监听器
BackHandler.addEventListener('hardwareBackPress', handleBackPress);
// 移除监听器
BackHandler.removeEventListener('hardwareBackPress', handleBackPress);
退出应用
c
BackHandler.exitApp(); // 退出应用
- 【实战】阻止返回键直接退出应用
c
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 />;
}
- 【实战】双击返回键退出应用
c
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 />;
}
- 【实战】返回键导航回退
c
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
c
npm install @react-native-community/hooks
c
import { useBackHandler }from '@react-native-community/hooks'
c
useBackHandler(() => {
// 按返回键,即退出应用
return true;
});
【Android 特有】权限 PermissionsAndroid
权限值
c
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 // 读取短信
权限结果
c
PermissionsAndroid.RESULTS.GRANTED // 已授予
PermissionsAndroid.RESULTS.DENIED // 已拒绝
PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN // 已拒绝且选中"不再询问"
如果要申请某个权限,还必须在 AndroidManifest.xml 中声明该权限
c
<uses-permission android:name="android.permission.CAMERA" />
检查权限状态 PermissionsAndroid.check
c
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
c
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
c
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('用户拒绝了部分权限');
}
}
【实战】请求相机权限
c
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>
<Button
title={hasCameraPermission ? '打开相机' : '请求权限'}
onPress={hasCameraPermission ? openCamera : requestCameraPermission}
/>
</View>
);
}
// 打开相机的函数实现
const openCamera = () => {
// 实现相机功能
console.log('打开相机');
};
【实战】引导用户到设置页面手动授权
当用户拒绝权限并选择 "不再询问" 时,需要引导用户到设置页面手动授权:
c
import { Linking } from 'react-native';
// ...
const handlePermissionDenied = () => {
Alert.alert(
'权限被拒',
'需要相机权限才能使用此功能,请在设置中授予权限',
[
{ text: '取消', style: 'cancel' },
{
text: '去设置',
onPress: () => Linking.openSettings()
}
]
);
};
振动 Vibration
Android 中
需要在 AndroidManifest.xml 中声明振动权限
html
<uses-permission android:name="android.permission.VIBRATE" />
c
// 振动 400 毫秒(默认持续时间)
Vibration.vibrate();
c
// 自定义持续时间(500 毫秒)
Vibration.vibrate(500);
c
// 停止振动
Vibration.cancel();
自定义振动模式
c
// 模式:等待 100ms → 振动 200ms → 等待 300ms → 振动 400ms
const pattern = [100, 200, 300, 400];
// 振动一次,不循环
Vibration.vibrate(pattern, false);
c
// 循环振动(第二个参数为 true)
Vibration.vibrate(pattern, true);
IOS 中
- 无需配置,直接使用
- 每次振动,固定 400 ms ,无法自定义
- 自定义模式有差异
c
// 模式:等待 100ms → 振动 400ms → 等待 200ms → 振动 400ms ......
const pattern = [100, 200, 300, 400];
键盘 Keyboard
c
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();
};
}, []);
c
// 关闭键盘
Keyboard.dismiss();
【实战】点击空白区域关闭键盘
c
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 }}>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1, marginBottom: 16 }}
placeholder="输入文本..."
/>
<Text>点击空白区域关闭键盘</Text>
</View>
</TouchableWithoutFeedback>
);
};