React Native【详解】内置 API

发布于:2025-06-21 ⋅ 阅读:(20) ⋅ 点赞:(0)

屏幕 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支持,判断是否是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

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',    // 数据 URI
      text: '分享的文本内容',           // 文本数据
      // 其他自定义键值对
    }
    

分享文本内容

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>
      <Button
        title={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 }}>
        <TextInput
          style={{ height: 40, borderColor: 'gray', borderWidth: 1, marginBottom: 16 }}
          placeholder="输入文本..."
        />
        <Text>点击空白区域关闭键盘</Text>
      </View>
    </TouchableWithoutFeedback>
  );
};

网站公告

今日签到

点亮在社区的每一天
去签到