react-native-camera ile Android uygulamasında çekilen fotoğrafın Galeriye kaydedilmesi nasıl yapılır?
Daha önce RNCamera üzerine bir yazı yazmıştım ama o yazı daha çok Instagram uygulaması serisine özel bir yazı olduğundan ve karmaşıklığından dolayı daha spesifik ve amaca yönelik bir yazı yazmak istedim. iOS cihazım olmadığı için sadece Android cihazlardaki yapılandırma kısımlarına değineceğim.
Bu yazıda fotoğrafın çekilmesi, görüntülenmesi ve galeriye kaydedilmesi işlemleri yer alacaktır.
Android üzerinden çalışma yapmak için emulator yerine gerçek cihazınızı kullanmanızı öneririm. Wifi üzerinden uygulamanızı geliştirebilmeniz için de bir yazı oluşturdum.
Geliştirim yapmak için telefonumuzu bilgisayarımıza bağladıktan sonra projeyi oluşturup boş haliyle çalıştırabiliriz:
npx react-native init SampleRNCamera --template react-native-template-typescript
cd SampleRNCamera
npx react-native run-android
Uygulama çalıştığına göre artık react-native-camera kütüphanesini ekleyebiliriz:
yarn add react-native-camera
Kütüphane eklendikten sonra AndroidManifest.xml dosyasını komut satırından vscode veya nano ile açarak düzenleyebiliriz:
nano android/app/src/main/AndroidManifest.xml
Dosya açıldıktan sonra <uses-permission android:name="android.permission.INTERNET" /> kısmının altına aşağıdaki izinleri vermemiz gerekiyor:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Not: Dosyayı kaydederek nano uygulamasından çıkmak için CTRL+X, Y ve Enter tuşlarına basmamız yeterli olacaktır.
Android uygulamasında ayrıca build.gradle için aşağıdaki satırı eklememiz gerekiyor. Dosyayı açalım:
nano android/app/build.gradle
defaultConfig kısmına missingDimensionStrategy satırını ekleyelim:
android {
compileSdkVersion rootProject.ext.compileSdkVersioncompileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}defaultConfig {
applicationId "com.samplerncamera"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
missingDimensionStrategy 'react-native-camera', 'general'
}
Daha sonra App.tsx dosyasında fotoğraf çekimi ve çekilen fotoğrafın görüntülenmesi için aşağıdaki gibi kodu ekleyebilirsiniz:
import React, {useRef, useState} from 'react';
import {
StyleSheet,
View,
Text,
TouchableOpacity,
Image,
ImageBackground,
Button,
} from 'react-native';import {RNCamera} from 'react-native-camera';const App: React.FC = () => {const camera = useRef<RNCamera>(null);
const [imageUri, setImageUri] = useState('')const takePicture = async () => {
if (camera) {
const options = {quality: 0.5, base64: false};
const data = await camera.current?.takePictureAsync(options).then(data => {
console.log(data.uri);
setImageUri(data==undefined ? '': data.uri);
})
}
};return (
imageUri == '' ?
<View style={styles.container}>
<RNCamera
ref={camera}
style={styles.preview}
type={RNCamera.Constants.Type.back}
flashMode={RNCamera.Constants.FlashMode.on}
captureAudio={false}
androidCameraPermissionOptions={{
title: 'Kamera izni gerekiyor',
message: 'Kamera ile manzara fotoğrafı çekmek için izin vermeniz gerekiyor.',
buttonPositive: 'Tamam',
buttonNegative: 'İptal'
}}
androidRecordAudioPermissionOptions={{
title: 'Mikrofon izni gerekiyor',
message: 'Mikrofon ile ses kaydı yapmak için için izin vermeniz gerekiyor.',
buttonPositive: 'Tamam',
buttonNegative: 'İptal'
}}
onGoogleVisionBarcodesDetected={({barcodes}) => {
console.log(barcodes);
}}
/>
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={takePicture} >
<Text style={{fontSize: 50}}>📸</Text>
</TouchableOpacity>
</View>
</View>
:
<View style={{flex: 1}}>
<ImageBackground style={styles.imageBackground} source={{uri: imageUri}}/>
<View style={styles.okButtonContainer}>
<Button title="TAMAM" onPress={() => setImageUri('')} />
</View>
</View>
);
};const styles = StyleSheet.create({
container: {
flex: 1
},
preview: {
flex: 1,
},
buttonContainer: {
width:'100%',
position: 'absolute',
bottom:50,
right: 0,
left:0,
alignItems:'center'
},
imageBackground: {
width:'100%',
height: '100%'
},
okButtonContainer: {
position: 'absolute',
bottom: 0,
width: '100%'
}
});export default App;
Kaydedip çalıştırdığınızda, fotoğraf çekim öncesi ve sonrası ekran görüntüsü aşağıdaki gibi olacaktır:
Çekilen fotoğrafaşağıdaki gibi uygulamanın cache dizinine kaydedecektir ve bu dizine direkt olarak erişim imkanı yoktur:
Fotoğrafın galeriye kaydedilmesi için CameraRoll nesnesini kullanmamız gerekiyor. setImageUri’dan sonra aşağıdaki satırı ekleyerek kaydedebiliriz:
CameraRoll.saveToCameraRoll(data.uri, 'photo');
Program çalıştığında hata vermese dahi, fotoğrafı kaydetmeyecektir. Bundler’da aşağıdaki uyarı mesajını görebilirsiniz:
CameraRoll sınıfı artık kendi kütüphanesine taşındığı için bu mesajın gelmesi doğaldır. Şimdi react-native-cameraroll kütüphanesini yükleyelim:
yarn add @react-native-community/cameraroll
Bu kütüphane yükledikten sonra birkaç özel ayarlama yapmak gerekiyor. MainApplication.java dosyasını açalım:
nano android/app/src/main/java/com/samplerncamera/MainApplication.java
Aşağıdaki import’u ekleyelim:
import com.reactnativecommunity.cameraroll.CameraRollPackage;
getPackages() fonksiyonunun içerisinde ise aşağıdaki paketin eklenmesini sağlayalım:
packages.add(new CameraRollPackage());
Ctrl+X, Y, Enter ile dosyayı kaydedip çıkalım. android/settings.gradle dosyasını aşağıdaki gibi açalım:
nano android/settings.gradle
Aşağıdaki satırları ekleyelim:
include ':@react-native-community_cameraroll'
project(':@react-native-community_cameraroll').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/cameraroll/android')
Kaydederek çıkalım ve son olarak android/app/build.gradle dosyasını açalım ve dependencies kısmına aşağıdaki satırı ekleyelim:
compile project(':@react-native-community_cameraroll')
App.tsx dosyasına gelelim ve CameraRoll’ün community paketinden yüklenmesini sağlayalım:
import CameraRoll from "@react-native-community/cameraroll";
Kodumuz bu şekilde çalışacaktır fakat storage’a yazma yetkisi verilmediği için Galeri’ye kaydedemeyecektir. Terminal’de Metro Bundler’da aşağıdaki hatayı görebilirsiniz:
Bunun için PermissionsAndroid’i kullanmamız gerekiyor. Bu nedenle aşağıdaki gibi bir requestStoragePermission adında bir fonksiyon oluşturalım:
const requestStoragePermission = async (callback: any) => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
title: 'Galeri kaydetme izni gerekiyor',
message: 'Fotoğrafınızın galeriye kaydedilmesi için izin veriniz',
buttonNeutral: 'Daha sonra sor',
buttonNegative: 'İptal',
buttonPositive: 'Tamam',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('✅İzin verildi');
callback();
} else {
console.log('❌İzin verilmedi');
}
} catch (err) {
console.warn(err);
}
}
CameraRoll’a kaydettiğimiz satırı bu fonksiyon ile sarmalayalım:
requestStoragePermission(() => CameraRoll.saveToCameraRoll(data.uri, 'photo'));
Uygulamayı çalıştırdığınızda fotoğraf çekimi sonrası aşağıdaki gibi izin diyalog’ları çıkaracaktır ve izin verildiğinde Galeri’ye kaydedecektir.
App.tsx’in son hali aşağıdaki gibidir:
import React, {useRef, useState} from 'react';
import {
StyleSheet,
View,
Text,
TouchableOpacity,
Image,
ImageBackground,
Button,
PermissionsAndroid,
} from 'react-native';import {RNCamera} from 'react-native-camera';
import CameraRoll from "@react-native-community/cameraroll";const App: React.FC = () => {const camera = useRef<RNCamera>(null);
const [imageUri, setImageUri] = useState('')const takePicture = async () => {
if (camera) {
const options = {quality: 0.5, base64: false};
const data = await camera.current?.takePictureAsync(options).then(data => {
console.log(data.uri);
setImageUri(data==undefined ? '': data.uri);
requestStoragePermission(() => CameraRoll.saveToCameraRoll(data.uri, 'photo'));
})
}
};const requestStoragePermission = async (callback: any) => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
title: 'Galeri kaydetme izni gerekiyor',
message: 'Fotoğrafınızın galeriye kaydedilmesi için izin veriniz',
buttonNeutral: 'Daha sonra sor',
buttonNegative: 'İptal',
buttonPositive: 'Tamam',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('✅İzin verildi');
callback();
} else {
console.log('❌İzin verilmedi');
}
} catch (err) {
console.warn(err);
}
}return (
imageUri == '' ?
<View style={styles.container}>
<RNCamera
ref={camera}
style={styles.preview}
type={RNCamera.Constants.Type.back}
flashMode={RNCamera.Constants.FlashMode.on}
captureAudio={false}
androidCameraPermissionOptions={{
title: 'Kamera izni gerekiyor',
message: 'Kamera ile manzara fotoğrafı çekmek için izin vermeniz gerekiyor.',
buttonPositive: 'Tamam',
buttonNegative: 'İptal'
}}
androidRecordAudioPermissionOptions={{
title: 'Mikrofon izni gerekiyor',
message: 'Mikrofon ile ses kaydı yapmak için için izin vermeniz gerekiyor.',
buttonPositive: 'Tamam',
buttonNegative: 'İptal'
}}
onGoogleVisionBarcodesDetected={({barcodes}) => {
console.log(barcodes);
}}
/>
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={takePicture} >
<Text style={{fontSize: 50}}>📸</Text>
</TouchableOpacity>
</View>
</View>
:
<View style={{flex: 1}}>
<ImageBackground style={styles.imageBackground} source={{uri: imageUri}}/>
<View style={styles.okButtonContainer}>
<Button title="TAMAM" onPress={() => setImageUri('')} />
</View>
</View>
);
};const styles = StyleSheet.create({
container: {
flex: 1
},
preview: {
flex: 1,
},
buttonContainer: {
width:'100%',
position: 'absolute',
bottom:50,
right: 0,
left:0,
alignItems:'center'
},
imageBackground: {
width:'100%',
height: '100%'
},
okButtonContainer: {
position: 'absolute',
bottom: 0,
width: '100%'
}
});export default App;
Sonuç olarak
React Native’de kamera izinleri, hem native hem de JS tarafında değişiklik yapmayı gerektiriyor. İlk bakışta biraz karmaşık gibi gözükse de birkaç adımda kolaylıkla hallediliyor.
Projenin bitmiş halini react-native-camera-sample reposunda bulabilirsiniz. Bu yazım hakkında soru ve görüşlerinizi aşağıdaki yorumlar kısmından yazabilirsiniz. Bana destek vermek için alkış simgesine tıklayabilirsiniz. Sonraki yazımda görüşmek üzere…