Hinweis:Auf dieser Seite wird die Klasse Camera beschrieben, die eingestellt wird. Wir empfehlen die Verwendung von CameraX oder für bestimmte Anwendungsfälle Camera2. Sowohl CameraX als auch Camera2 unterstützen Android 5.0 (API-Level 21) und höher.
In dieser Lektion erfahren Sie, wie Sie ein Foto aufnehmen, indem Sie die Aufgabe an eine andere Kamera-App auf dem Gerät delegieren. Wenn Sie lieber Ihre eigene Kamerafunktion erstellen möchten, lesen Sie den Hilfeartikel Kamera steuern.
Angenommen, Sie implementieren einen Crowdsourcing-Wetterdienst, der eine globale Wetterkarte erstellt, indem Bilder des Himmels, die von Geräten mit Ihrer Client-App aufgenommen wurden, zusammengeführt werden. Die Einbindung von Fotos ist nur ein kleiner Teil Ihrer Anwendung. Sie möchten Fotos mit minimalem Aufwand aufnehmen und nicht die Kamera neu erfinden. Glücklicherweise ist auf den meisten Android-Geräten bereits mindestens eine Kamera-App installiert. In dieser Lektion erfahren Sie, wie Sie ein Foto aufnehmen lassen.
Kamerafunktion anfordern
Wenn das Aufnehmen von Fotos eine wichtige Funktion Ihrer App ist, beschränken Sie die Sichtbarkeit bei Google Play auf Geräte mit Kamera. Wenn Sie angeben möchten, dass Ihre Anwendung eine Kamera benötigt, fügen Sie das Tag <uses-feature>
in Ihre Manifestdatei ein:
<manifest ... > <uses-feature android:name="android.hardware.camera" android:required="true" /> ... </manifest>
Wenn Ihre Anwendung eine Kamera verwendet, diese aber nicht zur Funktion benötigt, setzen Sie android:required
stattdessen auf false
. So können Nutzer auf Geräten ohne Kamera Ihre App herunterladen. Sie müssen dann die Verfügbarkeit der Kamera zur Laufzeit prüfen, indem Sie hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
aufrufen.
Wenn keine Kamera verfügbar ist, sollten Sie die Kamerafunktionen deaktivieren.
Thumbnail abrufen
Wenn das einfache Aufnehmen eines Fotos nicht der Höhepunkt Ihrer App ist, möchten Sie das Bild wahrscheinlich aus der Kamera-App zurückholen und etwas damit tun.
Die Android-Kamera-App codiert das Foto in der AntwortIntent
, die an onActivityResult()
gesendet wird, als kleine Bitmap
in den Extras unter dem Schlüssel "data"
. Der folgende Code ruft dieses Bild ab und zeigt es in einer ImageView
an.
Kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { val imageBitmap = data.extras.get("data") as Bitmap imageView.setImageBitmap(imageBitmap) } }
Java
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { Bundle extras = data.getExtras(); Bitmap imageBitmap = (Bitmap) extras.get("data"); imageView.setImageBitmap(imageBitmap); } }
Hinweis:Dieses Thumbnail-Bild von "data"
eignet sich zwar für ein Symbol, aber nicht für viel mehr. Die Arbeit mit einem Bild in voller Größe ist etwas aufwendiger.
Foto in Originalgröße speichern
Die Android-Kamera-App speichert ein Foto in Originalgröße, wenn Sie eine Datei angeben, in der es gespeichert werden soll. Sie müssen einen voll qualifizierten Dateinamen angeben, unter dem die Kamera-App das Foto speichern soll.
Grundsätzlich sollten alle Fotos, die der Nutzer mit der Kamera des Geräts aufnimmt, auf dem Gerät im öffentlichen externen Speicher gespeichert werden, damit alle Apps darauf zugreifen können. Das richtige Verzeichnis für freigegebene Fotos wird von getExternalStoragePublicDirectory()
mit dem Argument DIRECTORY_PICTURES
angegeben. Das Verzeichnis, das mit dieser Methode angegeben wird, wird von allen Apps gemeinsam genutzt. Unter Android 9 (API-Ebene 28) und niedriger sind die Berechtigungen READ_EXTERNAL_STORAGE
und WRITE_EXTERNAL_STORAGE
erforderlich, um in diesem Verzeichnis lesen und schreiben zu können:
<manifest ...> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
Unter Android 10 (API-Level 29) und höher ist die Tabelle MediaStore.Images
das richtige Verzeichnis für die Freigabe von Fotos.
Sie müssen keine Speicherberechtigungen angeben, solange Ihre App nur auf die Fotos zugreifen muss, die der Nutzer mit Ihrer App aufgenommen hat.
Wenn die Fotos jedoch nur für Ihre App sichtbar sein sollen, können Sie stattdessen das von Context.getExternalFilesDir()
bereitgestellte Verzeichnis verwenden.
Unter Android 4.3 und niedriger ist für das Schreiben in dieses Verzeichnis außerdem die Berechtigung WRITE_EXTERNAL_STORAGE
erforderlich. Ab Android 4.4 ist die Berechtigung nicht mehr erforderlich, da andere Apps nicht auf das Verzeichnis zugreifen können. Sie können angeben, dass die Berechtigung nur bei niedrigeren Android-Versionen angefordert werden soll, indem Sie das Attribut maxSdkVersion
hinzufügen:
<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> ... </manifest>
Hinweis:Dateien, die Sie in den von getExternalFilesDir()
oder getFilesDir()
bereitgestellten Verzeichnissen speichern, werden gelöscht, wenn der Nutzer Ihre App deinstalliert.
Nachdem Sie das Verzeichnis für die Datei festgelegt haben, müssen Sie einen kollisionsresistenten Dateinamen erstellen.
Sie können den Pfad auch in einer Mitgliedsvariablen für die spätere Verwendung speichern. Hier ist eine Beispiellösung in einer Methode, die mit einem Zeitstempel einen eindeutigen Dateinamen für ein neues Foto zurückgibt.
In diesem Beispiel wird davon ausgegangen, dass Sie die Methode innerhalb einer Context
aufrufen.
Kotlin
lateinit var currentPhotoPath: String @Throws(IOException::class) private fun createImageFile(): File { // Create an image file name val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date()) val storageDir: File = getExternalFilesDir(Environment.DIRECTORY_PICTURES) return File.createTempFile( "JPEG_${timeStamp}_", /* prefix */ ".jpg", /* suffix */ storageDir /* directory */ ).apply { // Save a file: path for use with ACTION_VIEW intents currentPhotoPath = absolutePath } }
Java
String currentPhotoPath; private File createImageFile() throws IOException { // Create an image file name String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); File image = File.createTempFile( imageFileName, /* prefix */ ".jpg", /* suffix */ storageDir /* directory */ ); // Save a file: path for use with ACTION_VIEW intents currentPhotoPath = image.getAbsolutePath(); return image; }
Nachdem Sie eine Datei für das Foto erstellt haben, können Sie Intent
so erstellen und aufrufen:
Kotlin
private fun dispatchTakePictureIntent() { Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent -> // Ensure that there's a camera activity to handle the intent takePictureIntent.resolveActivity(packageManager)?.also { // Create the File where the photo should go val photoFile: File? = try { createImageFile() } catch (ex: IOException) { // Error occurred while creating the File ... null } // Continue only if the File was successfully created photoFile?.also { val photoURI: Uri = FileProvider.getUriForFile( this, "com.example.android.fileprovider", it ) takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI) startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE) } } } }
Java
private void dispatchTakePictureIntent() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // Ensure that there's a camera activity to handle the intent if (takePictureIntent.resolveActivity(getPackageManager()) != null) { // Create the File where the photo should go File photoFile = null; try { photoFile = createImageFile(); } catch (IOException ex) { // Error occurred while creating the File ... } // Continue only if the File was successfully created if (photoFile != null) { Uri photoURI = FileProvider.getUriForFile(this, "com.example.android.fileprovider", photoFile); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); } } }
Hinweis:Wir verwenden getUriForFile(Context, String, File)
, das einen content://
-URI zurückgibt. Bei neueren Apps, die auf Android 7.0 (API-Level 24) oder höher ausgerichtet sind, führt das Übergeben eines file://
-URI über eine Paketgrenze zu einer FileUriExposedException
.
Daher stellen wir jetzt eine allgemeinere Möglichkeit zum Speichern von Bildern mithilfe einer FileProvider
vor.
Jetzt müssen Sie FileProvider
konfigurieren. Fügen Sie Ihrer Anwendung im Manifest einen Anbieter hinzu:
<application> ... <provider android:name="androidx.core.content.FileProvider" android:authorities="com.example.android.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data> </provider> ... </application>
Der String „authorities“ muss mit dem zweiten Argument für getUriForFile(Context, String, File)
übereinstimmen.
Im Abschnitt „Metadaten“ der Anbieterdefinition sehen Sie, dass der Anbieter erwartet, dass zulässige Pfade in einer speziellen Ressourcendatei
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="my_images" path="Pictures" /> </paths>
Die Pfadkomponente entspricht dem Pfad, der von getExternalFilesDir()
zurückgegeben wird, wenn er mit Environment.DIRECTORY_PICTURES
aufgerufen wird.
Ersetzen Sie com.example.package.name
durch den tatsächlichen Paketnamen Ihrer App. In der Dokumentation zu FileProvider
finden Sie eine ausführliche Beschreibung der Pfadspezifier, die Sie neben external-path
verwenden können.
Foto zu einer Galerie hinzufügen
Wenn Sie ein Foto über einen Intent erstellen, sollten Sie wissen, wo sich das Bild befindet, da Sie angegeben haben, wo es gespeichert werden soll. Für alle anderen ist es am einfachsten, Ihr Foto über den Medienanbieter des Systems zugänglich zu machen.
Hinweis:Wenn Sie Ihr Foto im von getExternalFilesDir()
bereitgestellten Verzeichnis gespeichert haben, kann der Medienscanner nicht auf die Dateien zugreifen, da sie nur für Ihre App freigegeben sind.
In der folgenden Beispielmethode wird gezeigt, wie Sie den Medienscanner des Systems aufrufen, um Ihr Foto zur Datenbank des Medienanbieters hinzuzufügen und es in der Android-Galerie-App und anderen Apps verfügbar zu machen.
Kotlin
private fun galleryAddPic() { Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).also { mediaScanIntent -> val f = File(currentPhotoPath) mediaScanIntent.data = Uri.fromFile(f) sendBroadcast(mediaScanIntent) } }
Java
private void galleryAddPic() { Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); File f = new File(currentPhotoPath); Uri contentUri = Uri.fromFile(f); mediaScanIntent.setData(contentUri); this.sendBroadcast(mediaScanIntent); }
Skaliertes Bild decodieren
Bei begrenztem Arbeitsspeicher kann die Verwaltung mehrerer Bilder in voller Größe schwierig sein. Wenn der Speicher Ihrer Anwendung nach dem Anzeigen weniger als 10 Bilder aufgebraucht ist, können Sie die Größe des dynamischen Heaps erheblich reduzieren, indem Sie das JPEG in ein Speicherarray erweitern, das bereits auf die Größe der Zielansicht skaliert ist. Die folgende Beispielmethode veranschaulicht diese Technik.
Kotlin
private fun setPic() { // Get the dimensions of the View val targetW: Int = imageView.width val targetH: Int = imageView.height val bmOptions = BitmapFactory.Options().apply { // Get the dimensions of the bitmap inJustDecodeBounds = true BitmapFactory.decodeFile(currentPhotoPath, bmOptions) val photoW: Int = outWidth val photoH: Int = outHeight // Determine how much to scale down the image val scaleFactor: Int = Math.max(1, Math.min(photoW / targetW, photoH / targetH)) // Decode the image file into a Bitmap sized to fill the View inJustDecodeBounds = false inSampleSize = scaleFactor inPurgeable = true } BitmapFactory.decodeFile(currentPhotoPath, bmOptions)?.also { bitmap -> imageView.setImageBitmap(bitmap) } }
Java
private void setPic() { // Get the dimensions of the View int targetW = imageView.getWidth(); int targetH = imageView.getHeight(); // Get the dimensions of the bitmap BitmapFactory.Options bmOptions = new BitmapFactory.Options(); bmOptions.inJustDecodeBounds = true; BitmapFactory.decodeFile(currentPhotoPath, bmOptions); int photoW = bmOptions.outWidth; int photoH = bmOptions.outHeight; // Determine how much to scale down the image int scaleFactor = Math.max(1, Math.min(photoW/targetW, photoH/targetH)); // Decode the image file into a Bitmap sized to fill the View bmOptions.inJustDecodeBounds = false; bmOptions.inSampleSize = scaleFactor; bmOptions.inPurgeable = true; Bitmap bitmap = BitmapFactory.decodeFile(currentPhotoPath, bmOptions); imageView.setImageBitmap(bitmap); }