Not: Bu sayfada, desteği sonlandırılan Camera sınıfı ele alınmaktadır. CameraX'i veya belirli kullanım alanları için Camera2'yi kullanmanızı öneririz. Hem CameraX hem de Camera2, Android 5.0 (API düzeyi 21) ve sonraki sürümleri destekler.
Camera1'den CameraX'e nasıl geçiş yapacağınızı öğrenmek için
Camera1'den CameraX'e geçiş başlıklı makaleyi inceleyin.
Alternatif olarak, Camera1'den CameraX'e geçiş için temsilci becerisini kullanabilirsiniz.
Bu derste, işi cihazdaki başka bir kamera uygulamasına devrederek nasıl fotoğraf çekileceği anlatılmaktadır. (Kendi kamera işlevinizi oluşturmayı tercih ediyorsanız Kamerayı kontrol etme başlıklı makaleyi inceleyin.)
İstemci uygulamanızı çalıştıran cihazlar tarafından çekilen gökyüzü resimlerini birleştirerek küresel 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 değil, en az zahmetle fotoğraf çekmek istiyorsunuz. Neyse ki Android destekli cihazların çoğunda en az bir kamera uygulaması yüklüdür. Bu derste, cihazınızın sizin için nasıl fotoğraf çekebileceğini öğreneceksiniz.
Kamera özelliğini isteme
Uygulamanızın temel işlevlerinden biri fotoğraf çekmekse Google Play'de görünürlüğünü kamerası olan cihazlarla kısıtlayın. Uygulamanızın kamera gerektirdiğini bildirmek için manifest dosyanıza bir <uses-feature> etiketi ekleyin:
<manifest ... > <uses-feature android:name="android.hardware.camera" android:required="true" /> ... </manifest>
Uygulamanız kamera kullanıyor ancak çalışması için kamera gerekmiyorsa android:required özelliğini false olarak ayarlayın. Bunu yaptığınızda Google Play, kamerası olmayan cihazların uygulamanızı indirmesine izin verir. Ardından, hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) yöntemini çağırarak çalışma zamanında kameranın kullanılabilirliğini kontrol etmek sizin sorumluluğunuzdadır.
Kamera kullanılamıyorsa kamera özelliklerinizi devre dışı bırakmanız gerekir.
Küçük resmi alma
Uygulamanızın amacı yalnızca fotoğraf çekmek değilse muhtemelen resmi kamera uygulamasından geri alıp üzerinde işlem yapmak istersiniz.
Android Kamera uygulaması, fotoğrafı onActivityResult() öğesine gönderilen Intent dönüşünde, ekstralarda "data" anahtarı altında 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" tarafından sağlanan bu küçük resim, simge olarak kullanılabilir ancak başka bir kullanım için uygun olmayabilir. Tam boyutlu bir resimle çalışmak biraz daha fazla çaba gerektirir.
Tam boyutlu fotoğrafı kaydetme
Android Kamera uygulaması, kaydedeceği bir dosya verirseniz tam boyutlu bir 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 tüm fotoğraflar, tüm uygulamalar tarafından erişilebilmesi için cihazın 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 düzeyi 28) ve önceki sürümlerde bu dizini okumak ve bu dizine yazmak 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 gerekiyorsa herhangi bir depolama alanı izni beyan etmeniz gerekmez.
Ancak fotoğrafların yalnızca uygulamanıza özel kalmasını istiyorsanız 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, dizine diğer uygulamalar tarafından erişilemediği için bu izin artık gerekli değildir. Bu nedenle, maxSdkVersion özelliğini ekleyerek iznin yalnızca Android'in daha 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 dizinine karar verdikten sonra çakışmaya dayanıklı bir dosya adı oluşturmanız gerekir.
Yolu daha sonra kullanmak üzere bir üye değişkenine de kaydedebilirsiniz. Tarih-saat damgası kullanarak yeni bir fotoğraf için benzersiz bir dosya adı döndüren bir yöntemde örnek bir çözüm aşağıda 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öntem kullanılabildiğinden, artık Intent öğesini şu şekilde 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'si döndüren getUriForFile(Context, String, File) kullanıyoruz. Android 7.0 (API düzeyi 24) ve sonraki sürümleri hedefleyen daha yeni uygulamalarda, paket sınırları arasında file:// URI'si iletmek FileUriExposedException hatasına neden olur.
Bu nedenle, artık FileProvider kullanarak resimleri depolamanın daha genel bir yolunu sunuyoruz.
Şimdi FileProvider yapılandırmanız gerekiyor. Uygulamanızın manifest dosyasında, uygulamanıza 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şlevinin ikinci bağımsız değişkeniyle eşleştiğinden emin olun.
Sağlayıcı tanımının meta veri bölümünde, sağlayıcının uygun yolların özel bir kaynak dosyasında (
<?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 ifadesini uygulamanızın gerçek paket adıyla değiştirdiğinizden emin olun. Ayrıca, external-path dışında kullanabileceğiniz yol belirleyicilerin kapsamlı bir açıklaması için FileProvider belgelerine göz atın.
Fotoğrafı bir galeriye ekleme
Bir intent aracılığıyla fotoğraf oluşturduğunuzda, resminizin nerede olduğunu bilmeniz gerekir. Çünkü resmin nereye kaydedileceğini en başta siz belirtmiştiniz. Diğer herkes için fotoğrafınızı erişilebilir hale getirmenin en kolay yolu, sistemin medya sağlayıcısından erişilebilir hale getirmek olabilir.
Not: Fotoğrafınızı getExternalFilesDir() tarafından sağlanan dizine kaydettiyseniz medya tarayıcı, uygulamanıza özel oldukları için dosyalara erişemez.
Aşağıdaki örnek yöntem, fotoğrafınızı Medya Sağlayıcı'nın veritabanına eklemek için sistemin medya tarayıcısını nasıl çağıracağınızı gösterir. Böylece fotoğraf, 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 resmi çözme
Sınırlı bellekle birden fazla tam boyutlu resmi yönetmek zor olabilir. Uygulamanızın yalnızca birkaç resim gösterdikten sonra yetersiz bellek sorunu yaşadığını 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öntem bu tekniği göstermektedir.
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); }