Appearance
个人主页/小工具APP - 原生功能整合
原生功能整合实现
在本节中,我们将为个人主页/小工具APP添加原生功能整合,包括相机/相册调用、定位功能、本地存储等。
步骤1:安装必要的依赖
首先,我们需要安装一些必要的依赖库来实现原生功能:
bash
# 安装相机和相册访问库
npm install react-native-image-picker
# 安装定位库
npm install react-native-geolocation-service
# 安装权限请求库
npm install react-native-permissions
# 安装本地存储库
npm install @react-native-async-storage/async-storage步骤2:创建原生功能页面
创建 src/screens/NativeFeaturesScreen.js 文件:
javascript
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Image, ScrollView, Alert, Platform } from 'react-native';
import * as ImagePicker from 'react-native-image-picker';
import Geolocation from 'react-native-geolocation-service';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useNavigation } from '@react-navigation/native';
const NativeFeaturesScreen = () => {
const navigation = useNavigation();
const [selectedImage, setSelectedImage] = useState(null);
const [location, setLocation] = useState(null);
const [storedData, setStoredData] = useState(null);
// 加载存储的数据
useEffect(() => {
loadStoredData();
}, []);
// 从本地存储加载数据
const loadStoredData = async () => {
try {
const data = await AsyncStorage.getItem('user_data');
if (data) {
setStoredData(JSON.parse(data));
}
} catch (error) {
console.error('加载数据失败:', error);
}
};
// 保存数据到本地存储
const saveData = async () => {
try {
const data = {
name: '张三',
email: 'zhangsan@example.com',
lastUpdated: new Date().toISOString()
};
await AsyncStorage.setItem('user_data', JSON.stringify(data));
setStoredData(data);
Alert.alert('成功', '数据已保存到本地存储');
} catch (error) {
console.error('保存数据失败:', error);
Alert.alert('错误', '保存数据失败');
}
};
// 清除本地存储数据
const clearData = async () => {
try {
await AsyncStorage.removeItem('user_data');
setStoredData(null);
Alert.alert('成功', '本地存储数据已清除');
} catch (error) {
console.error('清除数据失败:', error);
Alert.alert('错误', '清除数据失败');
}
};
// 请求相机权限
const requestCameraPermission = async () => {
if (Platform.OS === 'ios') {
const { status } = await ImagePicker.requestCameraPermissionsAsync();
return status === 'granted';
} else {
// Android 权限处理
return true;
}
};
// 请求相册权限
const requestGalleryPermission = async () => {
if (Platform.OS === 'ios') {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
return status === 'granted';
} else {
// Android 权限处理
return true;
}
};
// 打开相机
const openCamera = async () => {
const hasPermission = await requestCameraPermission();
if (!hasPermission) {
Alert.alert('权限被拒绝', '需要相机权限才能拍照');
return;
}
const result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 0.8,
});
if (!result.canceled) {
setSelectedImage(result.assets[0].uri);
}
};
// 打开相册
const openGallery = async () => {
const hasPermission = await requestGalleryPermission();
if (!hasPermission) {
Alert.alert('权限被拒绝', '需要相册权限才能选择图片');
return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 0.8,
});
if (!result.canceled) {
setSelectedImage(result.assets[0].uri);
}
};
// 请求定位权限
const requestLocationPermission = () => {
return new Promise((resolve) => {
Geolocation.requestAuthorization((status) => {
resolve(status === 'granted');
});
});
};
// 获取当前位置
const getCurrentLocation = async () => {
const hasPermission = await requestLocationPermission();
if (!hasPermission) {
Alert.alert('权限被拒绝', '需要定位权限才能获取位置');
return;
}
Geolocation.getCurrentPosition(
(position) => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy
});
},
(error) => {
Alert.alert('错误', '获取位置失败: ' + error.message);
},
{
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 10000
}
);
};
return (
<ScrollView style={styles.container}>
<Text style={styles.title}>原生功能整合</Text>
{/* 相机/相册功能 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>相机/相册</Text>
<View style={styles.imageContainer}>
{selectedImage ? (
<Image source={{ uri: selectedImage }} style={styles.image} />
) : (
<View style={styles.placeholderImage}>
<Text style={styles.placeholderText}>选择图片</Text>
</View>
)}
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity style={styles.button} onPress={openCamera}>
<Text style={styles.buttonText}>拍照</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={openGallery}>
<Text style={styles.buttonText}>从相册选择</Text>
</TouchableOpacity>
</View>
</View>
{/* 定位功能 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>定位功能</Text>
<TouchableOpacity style={styles.button} onPress={getCurrentLocation}>
<Text style={styles.buttonText}>获取当前位置</Text>
</TouchableOpacity>
{location && (
<View style={styles.locationInfo}>
<Text style={styles.locationText}>纬度: {location.latitude}</Text>
<Text style={styles.locationText}>经度: {location.longitude}</Text>
<Text style={styles.locationText}>精度: {location.accuracy} 米</Text>
</View>
)}
</View>
{/* 本地存储功能 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>本地存储</Text>
<View style={styles.buttonContainer}>
<TouchableOpacity style={styles.button} onPress={saveData}>
<Text style={styles.buttonText}>保存数据</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={clearData}>
<Text style={styles.buttonText}>清除数据</Text>
</TouchableOpacity>
</View>
{storedData && (
<View style={styles.storedData}>
<Text style={styles.storedDataText}>姓名: {storedData.name}</Text>
<Text style={styles.storedDataText}>邮箱: {storedData.email}</Text>
<Text style={styles.storedDataText}>更新时间: {new Date(storedData.lastUpdated).toLocaleString()}</Text>
</View>
)}
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20,
padding: 20,
textAlign: 'center',
},
section: {
backgroundColor: '#fff',
marginHorizontal: 20,
marginBottom: 20,
padding: 20,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.1,
shadowRadius: 3.84,
elevation: 5,
},
sectionTitle: {
fontSize: 18,
fontWeight: '600',
marginBottom: 16,
},
imageContainer: {
alignItems: 'center',
marginBottom: 16,
},
image: {
width: 200,
height: 200,
borderRadius: 100,
},
placeholderImage: {
width: 200,
height: 200,
borderRadius: 100,
backgroundColor: '#e0e0e0',
alignItems: 'center',
justifyContent: 'center',
},
placeholderText: {
fontSize: 16,
color: '#666',
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
},
button: {
backgroundColor: '#007AFF',
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 8,
flex: 1,
marginHorizontal: 5,
alignItems: 'center',
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: '500',
},
locationInfo: {
marginTop: 16,
padding: 12,
backgroundColor: '#f0f0f0',
borderRadius: 8,
},
locationText: {
fontSize: 14,
marginBottom: 4,
},
storedData: {
marginTop: 16,
padding: 12,
backgroundColor: '#f0f0f0',
borderRadius: 8,
},
storedDataText: {
fontSize: 14,
marginBottom: 4,
},
});
export default NativeFeaturesScreen;步骤3:更新导航配置
更新 src/navigation/StackNavigators.js 文件,添加原生功能页面:
javascript
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from '../screens/HomeScreen';
import ProfileScreen from '../screens/ProfileScreen';
import ProfileEditScreen from '../screens/ProfileEditScreen';
import NativeFeaturesScreen from '../screens/NativeFeaturesScreen';
import CalculatorScreen from '../screens/tools/CalculatorScreen';
import NotesScreen from '../screens/tools/NotesScreen';
import NoteDetailScreen from '../screens/tools/NoteDetailScreen';
const Stack = createStackNavigator();
export const HomeStack = () => {
return (
<Stack.Navigator>
<Stack.Screen
name="HomeMain"
component={HomeScreen}
options={{ title: '首页' }}
/>
</Stack.Navigator>
);
};
export const ProfileStack = () => {
return (
<Stack.Navigator>
<Stack.Screen
name="ProfileMain"
component={ProfileScreen}
options={{ title: '个人中心' }}
/>
<Stack.Screen
name="ProfileEdit"
component={ProfileEditScreen}
options={{ title: '编辑资料' }}
/>
<Stack.Screen
name="NativeFeatures"
component={NativeFeaturesScreen}
options={{ title: '原生功能' }}
/>
</Stack.Navigator>
);
};
export const ToolsStack = () => {
return (
<Stack.Navigator>
<Stack.Screen
name="ToolsMain"
component={ToolsScreen}
options={{ title: '工具' }}
/>
<Stack.Screen
name="Calculator"
component={CalculatorScreen}
options={{ title: '计算器' }}
/>
<Stack.Screen
name="Notes"
component={NotesScreen}
options={{ title: '便签' }}
/>
<Stack.Screen
name="NoteDetail"
component={NoteDetailScreen}
options={{ title: '便签详情' }}
/>
</Stack.Navigator>
);
};步骤4:更新个人中心页面
更新 src/screens/ProfileScreen.js 文件,添加原生功能入口:
javascript
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Image } from 'react-native';
import { useNavigation } from '@react-navigation/native';
const ProfileScreen = () => {
const navigation = useNavigation();
// 模拟用户数据
const userData = {
name: '张三',
email: 'zhangsan@example.com',
phone: '13800138000',
bio: '这是个人简介',
};
const handleEditProfile = () => {
navigation.navigate('ProfileEdit', { userData });
};
const handleNativeFeatures = () => {
navigation.navigate('NativeFeatures');
};
return (
<View style={styles.container}>
<View style={styles.header}>
<Image
source={{ uri: 'https://via.placeholder.com/150' }}
style={styles.avatar}
/>
<Text style={styles.name}>{userData.name}</Text>
<Text style={styles.email}>{userData.email}</Text>
<TouchableOpacity
style={styles.editButton}
onPress={handleEditProfile}
>
<Text style={styles.editButtonText}>编辑资料</Text>
</TouchableOpacity>
</View>
<View style={styles.infoSection}>
<Text style={styles.infoTitle}>个人信息</Text>
<View style={styles.infoItem}>
<Text style={styles.infoLabel}>手机号:</Text>
<Text style={styles.infoValue}>{userData.phone}</Text>
</View>
<View style={styles.infoItem}>
<Text style={styles.infoLabel}>简介:</Text>
<Text style={styles.infoValue}>{userData.bio}</Text>
</View>
</View>
<View style={styles.menuSection}>
<TouchableOpacity style={styles.menuItem} onPress={handleNativeFeatures}>
<Text style={styles.menuText}>原生功能</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.menuItem}>
<Text style={styles.menuText}>设置</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.menuItem}>
<Text style={styles.menuText}>关于</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.menuItem}>
<Text style={styles.menuText}>退出登录</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
header: {
backgroundColor: '#fff',
padding: 20,
alignItems: 'center',
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
avatar: {
width: 100,
height: 100,
borderRadius: 50,
marginBottom: 16,
},
name: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 8,
},
email: {
fontSize: 14,
color: '#666',
marginBottom: 16,
},
editButton: {
backgroundColor: '#007AFF',
paddingHorizontal: 20,
paddingVertical: 8,
borderRadius: 20,
},
editButtonText: {
color: '#fff',
fontSize: 14,
fontWeight: '500',
},
infoSection: {
backgroundColor: '#fff',
marginTop: 16,
padding: 20,
},
infoTitle: {
fontSize: 16,
fontWeight: '600',
marginBottom: 16,
},
infoItem: {
flexDirection: 'row',
marginBottom: 12,
},
infoLabel: {
fontSize: 14,
color: '#666',
width: 80,
},
infoValue: {
fontSize: 14,
flex: 1,
},
menuSection: {
backgroundColor: '#fff',
marginTop: 16,
},
menuItem: {
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
menuText: {
fontSize: 16,
},
});
export default ProfileScreen;步骤5:配置权限
Android 权限配置
在 android/app/src/main/AndroidManifest.xml 文件中添加以下权限:
xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />iOS 权限配置
在 ios/[项目名]/Info.plist 文件中添加以下权限:
xml
<key>NSCameraUsageDescription</key>
<string>需要使用相机拍照</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册选择图片</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要获取您的位置信息</string>原生功能整合总结
在本部分中,我们实现了以下原生功能:
- 相机/相册访问:使用
react-native-image-picker库实现拍照和选择图片功能 - 定位功能:使用
react-native-geolocation-service库获取当前位置 - 本地存储:使用
@react-native-async-storage/async-storage库实现数据的本地存储和读取 - 权限管理:实现了权限请求和处理逻辑
- 用户界面:设计了美观的原生功能展示页面
项目总结
至此,我们已经完成了个人主页/小工具APP的开发,包括:
- 多页面路由:使用 React Navigation 实现了底部标签导航和栈导航
- 表单提交:实现了个人资料编辑和表单验证功能
- 原生功能整合:集成了相机/相册、定位和本地存储功能
这个APP展示了 React Native 开发的核心功能,包括:
- 组件化开发
- 状态管理
- 导航系统
- 表单处理
- 原生功能集成
- 权限管理
通过这个项目,你可以掌握 React Native 开发的基本流程和核心概念,为开发更复杂的应用打下基础。
