Not: Bu sayfada, desteği sonlandırılan Camera sınıfı ele alınmaktadır. CameraX veya belirli kullanım alanları için Camera2 kullanmanızı öneririz. Hem CameraX hem de Camera2, Android 5.0 (API düzeyi 21) ve sonraki sürümleri destekler.
Bu derste, işi cihazdaki başka bir kamera uygulamasına devrederek fotoğraf çekmeyi öğreneceksiniz. (Kendi kamera işlevinizi oluşturmak istiyorsanız Kamerayı kontrol etme başlıklı makaleyi inceleyin.)
Müşteri uygulamanızı çalıştıran cihazlar tarafından çekilen gökyüzü fotoğraflarını birleştirerek dünya çapında bir hava durumu haritası oluşturan kitle kaynaklı bir hava durumu hizmeti uyguladığınızı varsayalım. Fotoğrafları entegre etmek, uygulamanızın yalnızca küçük bir parçasıdır. Kamerayı yeniden icat etmek yerine, minimum düzeyde çabayla fotoğraf çekmek istiyorsunuz. Neyse ki Android işletim sistemli çoğu cihazda en az bir kamera uygulaması yüklü Bu derste, telefonun sizin için nasıl fotoğraf çekeceğini öğreneceksiniz.
Kamera özelliğini isteme
Uygulamanızın temel işlevlerinden biri fotoğraf çekmekse Google Play'deki görünürlüğünü kamerası olan cihazlarla kısıtlayın. Uygulamanızın kameraya ihtiyaç duyduğunu belirtmek için manifest dosyanıza <uses-feature>
etiketi ekleyin:
<manifest ... > <uses-feature android:name="android.hardware.camera" android:required="true" /> ... </manifest>
Uygulamanız kamera kullanıyorsa ancak çalışması için kameraya ihtiyaç duymuyorsa android:required
yerine false
değerini ayarlayın. Bu sayede Google Play, kamerası olmayan cihazların uygulamanızı indirmesine izin verir. Ardından, hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
işlevini çağırarak çalışma zamanında kameranın kullanılabilirliğini kontrol etmek sizin sorumluluğunuzdadır.
Kamera yoksa kamera özelliklerinizi devre dışı bırakmanız gerekir.
Küçük resmi alma
Uygulamanızın amacı yalnızca fotoğraf çekmek değilse muhtemelen kamera uygulamasından resmi alıp onunla bir şeyler yapmak istersiniz.
Android Kamera uygulaması, onActivityResult()
'a gönderilen Intent
döndürme mesajında, "data"
anahtarının altındaki eklerde küçük bir Bitmap
olarak kodlar. Aşağıdaki kod bu resmi alır ve bir ImageView
içinde gösterir.
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); } }
Not: "data"
adresindeki bu küçük resim, simge olarak kullanılabilir ancak başka bir amaç için uygun değildir. Tam boyutlu bir resimle çalışmak biraz daha fazla iş gerektirir.
Tam boyutlu fotoğrafı kaydetme
Android Kamera uygulaması, kaydedilecek bir dosya belirtirseniz tam boyutlu fotoğraf kaydeder. Kamera uygulamasının fotoğrafı kaydetmesi gereken tam nitelikli bir dosya adı sağlamanız gerekir.
Genellikle kullanıcının cihaz kamerasıyla çektiği fotoğraflar, tüm uygulamalar tarafından erişilebilmesi için cihazdaki herkese açık harici depolama alanına kaydedilmelidir. Paylaşılan fotoğraflar için doğru dizin, getExternalStoragePublicDirectory()
tarafından DIRECTORY_PICTURES
bağımsız değişkeniyle sağlanır. Bu yöntemle sağlanan dizin tüm uygulamalar arasında paylaşılır. Android 9 (API seviyesi 28) ve önceki sürümlerde bu dizinde okuma ve yazma işlemleri için sırasıyla READ_EXTERNAL_STORAGE
ve
WRITE_EXTERNAL_STORAGE
izinleri gerekir:
<manifest ...> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
Android 10 (API düzeyi 29) ve sonraki sürümlerde, fotoğraf paylaşmak için uygun dizin MediaStore.Images
tablosudur.
Uygulamanızın yalnızca kullanıcının uygulamanızı kullanarak çektiği fotoğraflara erişmesi gerektiği sürece herhangi bir depolama izni beyan etmeniz gerekmez.
Ancak fotoğrafların yalnızca uygulamanız için gizli kalmasını istiyorsanız bunun yerine Context.getExternalFilesDir()
tarafından sağlanan dizini kullanabilirsiniz.
Android 4.3 ve önceki sürümlerde bu dizine yazmak için WRITE_EXTERNAL_STORAGE
izni de gerekir. Android 4.4'ten itibaren dizin diğer uygulamalar tarafından erişilemediğinden bu izin artık gerekli değildir. Bu nedenle, maxSdkVersion
özelliğini ekleyerek iznin yalnızca Android'in eski sürümlerinde istenmesi gerektiğini belirtebilirsiniz:
<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> ... </manifest>
Not: getExternalFilesDir()
veya getFilesDir()
tarafından sağlanan dizinlere kaydettiğiniz dosyalar, kullanıcı uygulamanızı kaldırdığında silinir.
Dosyanın dizine karar verdikten sonra çakışmalara karşı dayanıklı bir dosya adı oluşturmanız gerekir.
Ayrıca, yolu daha sonra kullanmak üzere bir üye değişkenine kaydedebilirsiniz. Burada, tarih ve saat damgası kullanarak yeni bir fotoğraf için benzersiz bir dosya adı döndüren bir yöntemde örnek bir çözüm verilmiştir.
(Bu örnekte, yöntemi bir Context
içinden çağırdığınız varsayılmaktadır.)
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; }
Fotoğraf için dosya oluşturmak üzere bu yöntemi kullanabilirsiniz. Artık Intent
öğesini aşağıdaki gibi oluşturup çağırabilirsiniz:
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); } } }
Not: content://
URI döndüren getUriForFile(Context, String, File)
sınıfını kullanıyoruz. Android 7.0 (API düzeyi 24) ve sonraki sürümleri hedefleyen daha yeni uygulamalarda, bir paket sınırında file://
URI'sinin iletilmesi FileUriExposedException
'ye neden olur.
Bu nedenle, artık FileProvider
kullanarak resimleri daha genel bir şekilde depolama yöntemi sunuyoruz.
Artık FileProvider
öğesini yapılandırmanız gerekiyor. Uygulamanızın manifest dosyasına bir sağlayıcı ekleyin:
<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>
Yetkililer dizesinin, getUriForFile(Context, String, File)
için ikinci bağımsız değişkenle eşleştiğinden emin olun.
Sağlayıcı tanımının meta veri bölümünde, uygun yolların
<?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>
Yol bileşeni, Environment.DIRECTORY_PICTURES
ile çağrıldığında getExternalFilesDir()
tarafından döndürülen yola karşılık gelir.
com.example.package.name
yerine uygulamanızın gerçek paket adını girdiğinizden emin olun. Ayrıca, external-path
dışında kullanabileceğiniz yol belirteçlerinin kapsamlı bir açıklaması için FileProvider
dokümanlarına göz atın.
Fotoğrafı bir galeriye ekleme
Bir intent aracılığıyla fotoğraf oluşturduğunuzda, resminizin nereye kaydedileceğini belirttiğiniz için resminizin nerede olduğunu bilmeniz gerekir. Diğer kullanıcılar için fotoğrafınızı erişilebilir hale getirmenin en kolay yolu, sistemi kullanan medya sağlayıcıdan erişilebilir hale getirmektir.
Not: Fotoğrafınızı getExternalFilesDir()
tarafından sağlanan dizin içine kaydettiyseniz bu dosyalar uygulamanıza özel olduğu için medya tarayıcısı dosyalara erişemez.
Aşağıdaki örnek yöntemde, fotoğrafınızı Medya Sağlayıcı'nın veritabanına eklemek için sistemin medya tarayıcısının nasıl çağrılacağı gösterilmektedir. Böylece fotoğrafınız Android Galeri uygulamasında ve diğer uygulamalarda kullanılabilir hale gelir.
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); }
Ölçeklendirilmiş bir resmin kodunu çözme
Sınırlı bellek ile birden fazla tam boyutlu resim yönetmek zor olabilir. Uygulamanızın, sadece birkaç resim gösterdikten sonra belleği tükendiğini fark ederseniz JPEG'i hedef görünümün boyutuna uyacak şekilde ölçeklendirilmiş bir bellek dizisine genişleterek kullanılan dinamik yığın miktarını önemli ölçüde azaltabilirsiniz. Aşağıdaki örnek yöntemde bu teknik gösterilmektedir.
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); }