Not: Bu sayfa, kullanımdan kaldırılan Kamera sınıfıyla ilgilidir. KameraX veya belirli kullanım alanlarında Kamera2 kullanmanızı öneririz. Hem CameraX hem de Camera2, Android 5.0 (API düzeyi 21) ve sonraki sürümleri destekler.
Bu derste, çalışma için cihazdaki başka bir kamera uygulamasına yetki vererek fotoğraf çekmeyi öğreteceğiz. (Kendi kamera işlevselliğinizi oluşturmak isterseniz bkz. Kamerayı kontrol etme.)
İstemci uygulamanızı çalıştıran cihazlar tarafından çekilen gökyüzü resimlerini karıştırarak küresel hava durumu haritası oluşturan kitle kaynaklı bir hava durumu hizmeti kullandığı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, karmaşayı minimum düzeyde kullanarak fotoğraf çekmek istiyorsunuz. Neyse ki, Android destekli çoğu cihazda en az bir kamera uygulaması yüklüdür. Bu derste, sizin için bir fotoğraf çekmeyi öğreneceksiniz.
Kamera özelliğini isteyin
Uygulamanızın temel işlevlerinden biri resim çekmekse uygulamanın Google Play'deki görünürlüğünü kamerası olan cihazlarla sınırlandırın. Uygulamanızın bir kameraya bağlı olduğunun reklamını yapmak için manifest dosyanıza bir <uses-feature>
etiketi yerleştirin:
<manifest ... > <uses-feature android:name="android.hardware.camera" android:required="true" /> ... </manifest>
Uygulamanızın çalışması için kamera kullanıyor ancak gerekmiyorsa android:required
değerini false
olarak ayarlayın. Böylece Google Play, kamerası olmayan cihazların uygulamanızı indirmesine izin verir. Bu durumda, 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 al
Fotoğraf çekmenin basitliği uygulamanızın amacının zirvesi değilse muhtemelen resmi kamera uygulamasından geri almak ve bununla bir şeyler yapmak istersiniz.
Android Kamera uygulaması, onActivityResult()
öğesine gönderilen iadedeki fotoğrafı "data"
anahtarının altında küçük bir Bitmap
olarak kodlar.Intent
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"
ürününe ait bu küçük resim, bir simge için uygun olabilir ancak çok daha fazlasını kapsamaz. Tam boyutlu bir resimle ilgilenmek biraz daha fazla çaba gerektirir.
Fotoğrafı tam boyutlu olarak kaydet
Android Kamera uygulaması, kaydedileceği bir dosya verirseniz tam boyutlu bir fotoğrafı kaydeder. Kamera uygulamasının fotoğrafı kaydetmesi gereken tam nitelikli bir dosya adı girmeniz gerekir.
Genel olarak, kullanıcının cihazın kamerasıyla çektiği fotoğrafların tümü, tüm uygulamaların erişebilmesi için
cihazda herkese açık harici depolama alanına kaydedilmelidir. Paylaşılan fotoğraflar için doğru dizin, DIRECTORY_PICTURES
bağımsız değişkeniyle getExternalStoragePublicDirectory()
tarafından sağlanır. Bu yöntemin sağladığı dizin tüm uygulamalar arasında paylaşılır. Android 9 (API düzeyi 28) ve önceki sürümlerde bu dizinde okuma ve yazma işlemleri sırasıyla READ_EXTERNAL_STORAGE
ve WRITE_EXTERNAL_STORAGE
izinleri gerektirir:
<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 doğru dizin MediaStore.Images
tablosudur.
Uygulamanız 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ızda özel 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. Dizine diğer uygulamalar tarafından erişilemediği için Android 4.4'ten itibaren bu izin artık gerekli değildir. Bu yüzden, maxSdkVersion
özelliğini ekleyerek iznin yalnızca Android'in alt sürümlerinde istenmesi gerektiğini beyan edebilirsiniz:
<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, çarpışmaya dayanıklı bir dosya adı oluşturmanız gerekir.
Yolu daha sonra kullanmak üzere bir üye değişkenine kaydetmek de isteyebilirsiniz. Aşağıda, yeni bir fotoğraf için tarih-saat damgası kullanarak benzersiz dosya adı döndüren bir yönteme dair ö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 için kullanılabilen bu yöntem sayesinde 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 uygulamalar için paket sınırı boyunca bir file://
URI'si geçirmek FileUriExposedException
değerine neden olur.
Bu nedenle artık resimleri FileProvider
kullanarak depolamanın daha genel bir yöntemini sunuyoruz.
Şimdi FileProvider
eklentisini 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>
Yetkili dizesinin ikinci bağımsız değişkenle getUriForFile(Context, String, File)
ile 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
yerine uygulamanızın gerçek paket adını yazdığınızdan emin olun. external-path
dışında kullanabileceğiniz yol belirteçlerinin kapsamlı açıklaması için FileProvider
dokümanlarına da göz atın.
Fotoğrafı bir galeriye ekleme
Intent'i kullanarak fotoğraf oluşturduğunuzda, en başta nereye kaydedeceğinizi belirttiğiniz için resminizin nerede olduğunu bilmeniz gerekir. Diğer herkes için, fotoğrafınızı erişilebilir hale getirmenin en kolay yolu, fotoğrafı sistemin Medya Sağlayıcısından erişilebilir hale getirmektir.
Not: Fotoğrafınızı getExternalFilesDir()
tarafından sağlanan dizine kaydettiyseniz medya tarayıcı, bu dosyalar uygulamanıza özel olduğundan dosyalara erişemez.
Aşağıdaki örnek yöntem, fotoğrafınızı Medya Sağlayıcısının veritabanına ekleyerek Android Galerisi uygulamasında ve diğer uygulamalarda kullanılabilir hale getirmek için sistemin medya tarayıcısının nasıl çağrılacağını gösterir.
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 nedeniyle birden çok tam boyutlu resmi yönetmek zor olabilir. Uygulamanızın yalnızca birkaç resim görüntüledikten sonra belleğinin tükendiğini fark ederseniz JPEG dosyasını, hedef görünümün boyutuyla eşleşecek şekilde önceden ö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); }