Skip to content

个人主页/小工具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>

原生功能整合总结

在本部分中,我们实现了以下原生功能:

  1. 相机/相册访问:使用 react-native-image-picker 库实现拍照和选择图片功能
  2. 定位功能:使用 react-native-geolocation-service 库获取当前位置
  3. 本地存储:使用 @react-native-async-storage/async-storage 库实现数据的本地存储和读取
  4. 权限管理:实现了权限请求和处理逻辑
  5. 用户界面:设计了美观的原生功能展示页面

项目总结

至此,我们已经完成了个人主页/小工具APP的开发,包括:

  1. 多页面路由:使用 React Navigation 实现了底部标签导航和栈导航
  2. 表单提交:实现了个人资料编辑和表单验证功能
  3. 原生功能整合:集成了相机/相册、定位和本地存储功能

这个APP展示了 React Native 开发的核心功能,包括:

  • 组件化开发
  • 状态管理
  • 导航系统
  • 表单处理
  • 原生功能集成
  • 权限管理

通过这个项目,你可以掌握 React Native 开发的基本流程和核心概念,为开发更复杂的应用打下基础。

© 2026 编程马·菜鸟教程 版权所有