使用图片选择器
在本教程中,学习如何使用 Expo Image Picker。
React Native 提供了内置组件作为标准构建模块,例如 <View>、<Text> 和 <Pressable>。我们正在开发一个功能,以便从设备的媒体库中选择图片。这在核心组件中是无法实现的,因此我们需要一个库来在应用中添加此功能。
🌐 React Native provides built-in components as standard building blocks, such as <View>, <Text>, and <Pressable>. We are building a feature to select an image from the device's media gallery. This isn't possible with the core components and we'll need a library to add this feature in our app.
我们将使用 expo-image-picker,这是来自 Expo SDK 的一个库。
🌐 We'll use expo-image-picker, a library from Expo SDK.
expo-image-picker提供访问系统界面的功能,以从手机图库中选择图片和视频。

1
安装 expo-image-picker
🌐 Install expo-image-picker
要安装 expo-image-picker 库,请通过在终端中按 Ctrl + c 停止开发服务器,然后运行以下命令:
- npx expo install expo-image-picker[npx expo install](/more/expo-cli/#installation) 命令将安装该库并将其添加到项目的 package.json 依赖中。
🌐 The npx expo install command will install the library and add it to the project's dependencies in package.json.
信息 提示: 每当我们在项目中安装新库时,请通过在终端中按 Ctrl + c 停止开发服务器,然后运行安装命令。安装完成后,通过运行
npx expo start再次启动开发服务器。
2
从设备的媒体库中选择一张图片
🌐 Pick an image from the device's media library
expo-image-picker 提供了 launchImageLibraryAsync() 方法,通过从设备的媒体库中选择图片或视频来显示系统 UI。我们将使用上一章创建的主要主题按钮,从设备的媒体库中选择图片,并创建一个函数来启动设备的图片库以实现该功能。
在 app/(tabs)/index.tsx 中,导入 expo-image-picker 库,并在 Index 组件内创建一个 pickImageAsync() 函数:
🌐 In app/(tabs)/index.tsx, import expo-image-picker library and create a pickImageAsync() function inside the Index component:
// ...rest of the import statements remain unchanged import * as ImagePicker from 'expo-image-picker'; export default function Index() { const pickImageAsync = async () => { let result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images'], allowsEditing: true, quality: 1, }); if (!result.canceled) { console.log(result); } else { alert('You did not select any image.'); } }; // ...rest of the code remains same }
让我们来学习上面的代码是做什么的:
🌐 Let's learn what the above code does:
launchImageLibraryAsync()接收一个对象以指定不同的选项。这个对象是ImagePickerOptions对象,我们在调用方法时传入它。- 当
allowsEditing设置为true时,用户可以在 Android 和 iOS 上的选择过程中裁剪图片。
3
更新按钮组件
🌐 Update the button component
按下主按钮时,我们将在 Button 组件上调用 pickImageAsync() 函数。在 components/Button.tsx 中更新 Button 组件的 onPress 属性:
🌐 On pressing the primary button, we'll call the pickImageAsync() function on the Button component. Update the onPress prop of the Button component in components/Button.tsx:
import { StyleSheet, View, Pressable, Text } from 'react-native'; import FontAwesome from '@expo/vector-icons/FontAwesome'; type Props = { label: string; theme?: 'primary'; onPress?: () => void; }; export default function Button({ label, theme, onPress }: Props) { if (theme === 'primary') { return ( <View style={[ styles.buttonContainer, { borderWidth: 4, borderColor: '#ffd33d', borderRadius: 18 }, ]}> <Pressable style={[styles.button, { backgroundColor: '#fff' }]} onPress={onPress}> <FontAwesome name="picture-o" size={18} color="#25292e" style={styles.buttonIcon} /> <Text style={[styles.buttonLabel, { color: '#25292e' }]}>{label}</Text> </Pressable> </View> ); } return ( <View style={styles.buttonContainer}> <Pressable style={styles.button} onPress={() => alert('You pressed a button.')}> <Text style={styles.buttonLabel}>{label}</Text> </Pressable> </View> ); } const styles = StyleSheet.create({ buttonContainer: { width: 320, height: 68, marginHorizontal: 20, alignItems: 'center', justifyContent: 'center', padding: 3, }, button: { borderRadius: 10, width: '100%', height: '100%', alignItems: 'center', justifyContent: 'center', flexDirection: 'row', }, buttonIcon: { paddingRight: 8, }, buttonLabel: { color: '#fff', fontSize: 16, }, });
在 app/(tabs)/index.tsx 中,将 pickImageAsync() 函数添加到第一个 <Button> 的 onPress 属性上。
🌐 In app/(tabs)/index.tsx, add the pickImageAsync() function to the onPress prop on the first <Button>.
import { View, StyleSheet } from 'react-native'; import * as ImagePicker from 'expo-image-picker'; import Button from '@/components/Button'; import ImageViewer from '@/components/ImageViewer'; const PlaceholderImage = require('@/assets/images/background-image.png'); export default function Index() { const pickImageAsync = async () => { let result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images'], allowsEditing: true, quality: 1, }); if (!result.canceled) { console.log(result); } else { alert('You did not select any image.'); } }; return ( <View style={styles.container}> <View style={styles.imageContainer}> <ImageViewer imgSource={PlaceholderImage} /> </View> <View style={styles.footerContainer}> <Button theme="primary" label="Choose a photo" onPress={pickImageAsync} /> <Button label="Use this photo" /> </View> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#25292e', alignItems: 'center', }, imageContainer: { flex: 1, }, footerContainer: { flex: 1 / 3, alignItems: 'center', }, });
pickImageAsync() 函数调用 ImagePicker.launchImageLibraryAsync(),然后处理结果。launchImageLibraryAsync() 方法返回一个包含所选图片信息的对象。
🌐 The pickImageAsync() function invokes ImagePicker.launchImageLibraryAsync() and then handles the result. The launchImageLibraryAsync() method returns an object containing information about the selected image.
以下是 result 对象及其包含的属性的示例:
🌐 Here is an example of the result object and the properties it contains:
{ "assets": [ { "assetId": null, "base64": null, "duration": null, "exif": null, "fileName": "ea574eaa-f332-44a7-85b7-99704c22b402.jpeg", "fileSize": 4513577, "height": 4570, "mimeType": "image/jpeg", "rotation": null, "type": "image", "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FStickerSmash-13f21121-fc9d-4ec6-bf89-bf7d6165eb69/ImagePicker/ea574eaa-f332-44a7-85b7-99704c22b402.jpeg", "width": 2854 } ], "canceled": false }
{ "assets": [ { "assetId": "99D53A1F-FEEF-40E1-8BB3-7DD55A43C8B7/L0/001", "base64": null, "duration": null, "exif": null, "fileName": "IMG_0004.JPG", "fileSize": 2548364, "height": 1669, "mimeType": "image/jpeg", "type": "image", "uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252FStickerSmash-13f21121-fc9d-4ec6-bf89-bf7d6165eb69/ImagePicker/ea574eaa-f332-44a7-85b7-99704c22b402.jpeg", "width": 1668 } ], "canceled": false }
{ "assets": [ { "fileName": "some-image.png", "height": 720, "mimeType": "image/png", "uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAA" } ], "canceled": false }
4
使用所选图片
🌐 Use the selected image
result 对象提供了 assets 数组,其中包含所选图片的 uri。让我们从图片选择器获取这个值,并用它在应用中显示所选图片。
🌐 The result object provides the assets array, which contains the uri of the selected image. Let's take this value from the image picker and use it to show the selected image in the app.
修改 app/(tabs)/index.tsx 文件:
🌐 Modify the app/(tabs)/index.tsx file:
- 使用 React 的
useState钩子声明一个名为selectedImage的状态变量。我们将使用这个状态变量来保存所选图片的 URI。 - 更新
pickImageAsync()函数,将图片 URI 保存到selectedImage状态变量中。 - 将
selectedImage作为属性传递给ImageViewer组件。
import { View, StyleSheet } from 'react-native'; import * as ImagePicker from 'expo-image-picker'; import { useState } from 'react'; import Button from '@/components/Button'; import ImageViewer from '@/components/ImageViewer'; const PlaceholderImage = require('@/assets/images/background-image.png'); export default function Index() { const [selectedImage, setSelectedImage] = useState<string | undefined>(undefined); const pickImageAsync = async () => { let result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images'], allowsEditing: true, quality: 1, }); if (!result.canceled) { setSelectedImage(result.assets[0].uri); } else { alert('You did not select any image.'); } }; return ( <View style={styles.container}> <View style={styles.imageContainer}> <ImageViewer imgSource={PlaceholderImage} selectedImage={selectedImage} /> </View> <View style={styles.footerContainer}> <Button theme="primary" label="Choose a photo" onPress={pickImageAsync} /> <Button label="Use this photo" /> </View> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#25292e', alignItems: 'center', }, imageContainer: { flex: 1, }, footerContainer: { flex: 1 / 3, alignItems: 'center', }, });
将 selectedImage 属性传递给 ImageViewer 组件以显示所选图片,而不是占位符图片。
🌐 Pass the selectedImage prop to the ImageViewer component to display the selected image instead of a placeholder image.
- 修改 components/ImageViewer.tsx 文件以接受
selectedImage属性。 - 图片的来源越来越长了,所以我们也把它移到一个叫
imageSource的单独变量中吧。 - 将
imageSource作为Image组件上source属性的值传入。
import { ImageSourcePropType, StyleSheet } from 'react-native'; import { Image } from 'expo-image'; type Props = { imgSource: ImageSourcePropType; selectedImage?: string; }; export default function ImageViewer({ imgSource, selectedImage }: Props) { const imageSource = selectedImage ? { uri: selectedImage } : imgSource; return <Image source={imageSource} style={styles.image} />; } const styles = StyleSheet.create({ image: { width: 320, height: 440, borderRadius: 18, }, });
在上面的代码片段中,Image 组件使用条件运算符来加载图片的来源。所选择的图片是一个 uri 字符串,而不是像占位符图片那样的本地资源。
🌐 In the above snippet, the Image component uses a conditional operator to load the image's source. The picked image is a uri string, not a local asset like the placeholder image.
现在让我们来看一下我们的应用:
🌐 Let's take a look at our app now:
本教程示例应用中使用的图片选自 Unsplash
概括
🌐 Summary
Chapter 4: Use an image picker
我们已成功添加了从设备的媒体库中选择图片的功能。
在下一章中,我们将学习如何创建一个表情符号选择器模态组件。