React Native定位服务 #
概述 #
定位服务是移动应用的常见功能,用于获取设备的地理位置。本章节介绍如何在 React Native 中使用定位服务。
权限配置 #
iOS 权限 #
在 ios/MyApp/Info.plist 中添加:
xml
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要访问您的位置以提供相关服务</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>需要始终访问您的位置以提供后台定位服务</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>需要访问您的位置以提供相关服务</string>
Android 权限 #
在 android/app/src/main/AndroidManifest.xml 中添加:
xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
@react-native-community/geolocation #
React Native 官方维护的定位库。
安装 #
bash
npm install @react-native-community/geolocation
cd ios && pod install
获取当前位置 #
tsx
import React, {useState} from 'react';
import {View, Text, Button, StyleSheet, Alert} from 'react-native';
import Geolocation from '@react-native-community/geolocation';
interface Position {
latitude: number;
longitude: number;
accuracy: number;
timestamp: number;
}
const LocationExample = () => {
const [position, setPosition] = useState<Position | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const getCurrentPosition = () => {
setLoading(true);
setError(null);
Geolocation.getCurrentPosition(
pos => {
setPosition({
latitude: pos.coords.latitude,
longitude: pos.coords.longitude,
accuracy: pos.coords.accuracy,
timestamp: pos.timestamp,
});
setLoading(false);
},
err => {
setError(err.message);
setLoading(false);
Alert.alert('定位失败', err.message);
},
{
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 10000,
},
);
};
return (
<View style={styles.container}>
{loading && <Text>正在获取位置...</Text>}
{error && <Text style={styles.error}>{error}</Text>}
{position && (
<View style={styles.info}>
<Text style={styles.label}>纬度:</Text>
<Text style={styles.value}>{position.latitude}</Text>
<Text style={styles.label}>经度:</Text>
<Text style={styles.value}>{position.longitude}</Text>
<Text style={styles.label}>精度:</Text>
<Text style={styles.value}>{position.accuracy}米</Text>
</View>
)}
<Button title="获取当前位置" onPress={getCurrentPosition} />
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 16,
},
info: {
marginBottom: 20,
alignItems: 'center',
},
label: {
fontSize: 14,
color: '#666',
marginTop: 8,
},
value: {
fontSize: 18,
fontWeight: 'bold',
},
error: {
color: '#FF3B30',
marginBottom: 16,
},
});
export default LocationExample;
位置配置选项 #
tsx
interface GeolocationOptions {
enableHighAccuracy?: boolean; // 使用高精度定位
timeout?: number; // 超时时间(毫秒)
maximumAge?: number; // 缓存最大时间(毫秒)
distanceFilter?: number; // 最小更新距离(米)
forceRequestLocation?: boolean; // 强制请求位置
showLocationDialog?: boolean; // 显示位置设置对话框
}
Geolocation.getCurrentPosition(
successCallback,
errorCallback,
{
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 10000,
distanceFilter: 10,
},
);
位置追踪 #
监听位置变化 #
tsx
import React, {useState, useEffect, useRef} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import Geolocation from '@react-native-community/geolocation';
const LocationTracker = () => {
const [position, setPosition] = useState<Position | null>(null);
const watchIdRef = useRef<number | null>(null);
useEffect(() => {
startWatching();
return () => {
stopWatching();
};
}, []);
const startWatching = () => {
watchIdRef.current = Geolocation.watchPosition(
pos => {
setPosition({
latitude: pos.coords.latitude,
longitude: pos.coords.longitude,
accuracy: pos.coords.accuracy,
timestamp: pos.timestamp,
speed: pos.coords.speed,
heading: pos.coords.heading,
});
},
error => {
console.error('Watch position error:', error);
},
{
enableHighAccuracy: true,
distanceFilter: 10,
interval: 5000,
fastestInterval: 2000,
},
);
};
const stopWatching = () => {
if (watchIdRef.current !== null) {
Geolocation.clearWatch(watchIdRef.current);
watchIdRef.current = null;
}
};
return (
<View style={styles.container}>
<Text style={styles.title}>实时位置追踪</Text>
{position && (
<View style={styles.info}>
<Text>纬度: {position.latitude}</Text>
<Text>经度: {position.longitude}</Text>
<Text>速度: {position.speed} m/s</Text>
<Text>方向: {position.heading}°</Text>
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 20,
},
info: {
alignItems: 'center',
},
});
export default LocationTracker;
react-native-geolocation-service #
功能更强大的定位库,支持后台定位。
安装 #
bash
npm install react-native-geolocation-service
cd ios && pod install
使用示例 #
tsx
import Geolocation from 'react-native-geolocation-service';
import {Platform, PermissionsAndroid} from 'react-native';
const requestLocationPermission = async (): Promise<boolean> => {
if (Platform.OS === 'ios') {
const auth = await Geolocation.requestAuthorization('whenInUse');
return auth === 'granted';
}
if (Platform.OS === 'android') {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
return false;
};
const getLocation = async () => {
const hasPermission = await requestLocationPermission();
if (!hasPermission) {
return null;
}
return new Promise((resolve, reject) => {
Geolocation.getCurrentPosition(
position => {
resolve(position);
},
error => {
reject(error);
},
{
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 10000,
forceRequestLocation: true,
},
);
});
};
距离计算 #
使用 Haversine 公式 #
tsx
const calculateDistance = (
lat1: number,
lon1: number,
lat2: number,
lon2: number,
): number => {
const R = 6371; // 地球半径(公里)
const dLat = toRad(lat2 - lat1);
const dLon = toRad(lon2 - lon1);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRad(lat1)) *
Math.cos(toRad(lat2)) *
Math.sin(dLon / 2) *
Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c;
return distance;
};
const toRad = (deg: number): number => {
return deg * (Math.PI / 180);
};
// 使用示例
const distance = calculateDistance(
39.9042, 116.4074, // 北京
31.2304, 121.4737, // 上海
);
console.log(`距离: ${distance.toFixed(2)} 公里`);
地理编码 #
使用第三方 API #
tsx
interface Address {
street: string;
city: string;
state: string;
country: string;
postalCode: string;
}
const reverseGeocode = async (
latitude: number,
longitude: number,
): Promise<Address | null> => {
try {
const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?lat=${latitude}&lon=${longitude}&format=json`,
);
const data = await response.json();
return {
street: data.address?.road || '',
city: data.address?.city || data.address?.town || '',
state: data.address?.state || '',
country: data.address?.country || '',
postalCode: data.address?.postcode || '',
};
} catch (error) {
console.error('Geocoding error:', error);
return null;
}
};
// 使用示例
const address = await reverseGeocode(39.9042, 116.4074);
console.log(address);
地图显示 #
使用 react-native-maps #
bash
npm install react-native-maps
tsx
import React from 'react';
import {View, StyleSheet} from 'react-native';
import MapView, {Marker, PROVIDER_GOOGLE} from 'react-native-maps';
const MapExample = () => {
const initialRegion = {
latitude: 39.9042,
longitude: 116.4074,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
};
return (
<View style={styles.container}>
<MapView
provider={PROVIDER_GOOGLE}
style={styles.map}
initialRegion={initialRegion}
showsUserLocation
showsMyLocationButton>
<Marker
coordinate={{
latitude: 39.9042,
longitude: 116.4074,
}}
title="北京"
description="中国首都"
/>
</MapView>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
map: {
flex: 1,
},
});
export default MapExample;
完整示例 #
tsx
import React, {useState, useEffect, useRef} from 'react';
import {
View,
Text,
Button,
StyleSheet,
Alert,
ActivityIndicator,
TouchableOpacity,
} from 'react-native';
import Geolocation from 'react-native-geolocation-service';
import {PermissionsAndroid, Platform} from 'react-native';
interface LocationData {
latitude: number;
longitude: number;
accuracy: number;
timestamp: number;
address?: string;
}
const LocationService = () => {
const [location, setLocation] = useState<LocationData | null>(null);
const [loading, setLoading] = useState(false);
const [tracking, setTracking] = useState(false);
const watchIdRef = useRef<number | null>(null);
useEffect(() => {
return () => {
if (watchIdRef.current !== null) {
Geolocation.clearWatch(watchIdRef.current);
}
};
}, []);
const requestPermission = async (): Promise<boolean> => {
if (Platform.OS === 'ios') {
const auth = await Geolocation.requestAuthorization('whenInUse');
return auth === 'granted';
}
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: '定位权限',
message: '需要访问您的位置以提供服务',
buttonNeutral: '稍后询问',
buttonNegative: '取消',
buttonPositive: '确定',
},
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
};
const getCurrentLocation = async () => {
const hasPermission = await requestPermission();
if (!hasPermission) {
Alert.alert('权限被拒绝', '请在设置中开启定位权限');
return;
}
setLoading(true);
Geolocation.getCurrentPosition(
position => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy,
timestamp: position.timestamp,
});
setLoading(false);
},
error => {
setLoading(false);
Alert.alert('定位失败', error.message);
},
{
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 10000,
},
);
};
const startTracking = async () => {
const hasPermission = await requestPermission();
if (!hasPermission) {
Alert.alert('权限被拒绝', '请在设置中开启定位权限');
return;
}
watchIdRef.current = Geolocation.watchPosition(
position => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy,
timestamp: position.timestamp,
});
},
error => {
Alert.alert('追踪错误', error.message);
setTracking(false);
},
{
enableHighAccuracy: true,
distanceFilter: 10,
interval: 5000,
fastestInterval: 2000,
},
);
setTracking(true);
};
const stopTracking = () => {
if (watchIdRef.current !== null) {
Geolocation.clearWatch(watchIdRef.current);
watchIdRef.current = null;
}
setTracking(false);
};
return (
<View style={styles.container}>
<Text style={styles.title}>定位服务</Text>
{loading && <ActivityIndicator size="large" color="#007AFF" />}
{location && (
<View style={styles.info}>
<Text style={styles.label}>纬度</Text>
<Text style={styles.value}>{location.latitude.toFixed(6)}</Text>
<Text style={styles.label}>经度</Text>
<Text style={styles.value}>{location.longitude.toFixed(6)}</Text>
<Text style={styles.label}>精度</Text>
<Text style={styles.value}>{location.accuracy?.toFixed(1)} 米</Text>
<Text style={styles.label}>时间</Text>
<Text style={styles.value}>
{new Date(location.timestamp).toLocaleString()}
</Text>
</View>
)}
<View style={styles.buttons}>
<TouchableOpacity
style={styles.button}
onPress={getCurrentLocation}
disabled={loading}>
<Text style={styles.buttonText}>获取位置</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, tracking ? styles.stopButton : styles.startButton]}
onPress={tracking ? stopTracking : startTracking}>
<Text style={styles.buttonText}>
{tracking ? '停止追踪' : '开始追踪'}
</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 20,
},
info: {
backgroundColor: '#fff',
borderRadius: 8,
padding: 16,
marginBottom: 20,
},
label: {
fontSize: 12,
color: '#666',
marginTop: 8,
},
value: {
fontSize: 16,
fontWeight: '600',
},
buttons: {
gap: 12,
},
button: {
padding: 16,
borderRadius: 8,
alignItems: 'center',
},
startButton: {
backgroundColor: '#34C759',
},
stopButton: {
backgroundColor: '#FF3B30',
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
});
export default LocationService;
总结 #
定位服务要点:
- 权限配置:iOS 和 Android 都需要配置权限
- 获取位置:单次获取当前位置
- 位置追踪:实时监听位置变化
- 距离计算:使用 Haversine 公式
- 地图集成:使用 react-native-maps
继续学习 推送通知,了解通知功能实现。
最后更新:2026-03-28