CameraX kullanım alanı döndürmeleri

Bu konuda, ImageAnalysis veya ImageCapture kullanım alanından doğru döndürme bilgileri içeren resimler almak için uygulamanızda CameraX kullanım alanlarını nasıl ayarlayacağınız gösterilmektedir. Bu nedenle:

  • ImageAnalysis kullanım alanının Analyzer doğru döndürülmüş kareler almalıdır.
  • ImageCapture kullanım alanı, fotoğrafları doğru döndürmeyle çekmelidir.

Terminoloji

Bu konuda aşağıdaki terminoloji kullanılır. Bu nedenle, her terimin ne anlama geldiğini bilmeniz önemlidir:

Ekran yönü
Cihazın hangi tarafının yukarı baktığını belirtir ve dört değerden biri olabilir: dikey, yatay, ters dikey veya ters yatay.
Ekran döndürme
Bu, Display.getRotation() tarafından döndürülen değerdir ve cihazın doğal yönüne göre saat yönünün tersine döndürüldüğü dereceleri temsil eder.
Hedef rotasyon
Bu değer, cihazın doğal yönüne ulaşması için saat yönünde döndürülmesi gereken derece sayısını gösterir.

Hedef rotasyonu belirleme

Aşağıdaki örneklerde, bir cihazın doğal yönelimine göre hedef rotasyonunun nasıl belirleneceği gösterilmektedir.

1. örnek: Dikey doğal yön

Cihaz örneği: Pixel 3 XL

Doğal yön = Dikey
Geçerli yön = Dikey

Ekran döndürme = 0
Hedef döndürme = 0

Doğal yön = Dikey
Mevcut yön = Yatay

Ekran döndürme = 90
Hedef döndürme = 90

2. Örnek: Yatay doğal yön

Cihaz örneği: Pixel C

Doğal yön = Yatay
Mevcut yön = Yatay

Ekran döndürme = 0
Hedef döndürme = 0

Doğal yön = Yatay
Geçerli yön = Dikey

Ekran döndürme = 270
Hedef döndürme = 270

Resim döndürme

Hangi uç yukarı bakıyor? Sensör yönü, Android'de sabit bir değer olarak tanımlanır. Bu değer, cihaz doğal konumdayken sensörün cihazın üst kısmından döndürüldüğü dereceleri (0, 90, 180, 270) gösterir. Şemalardaki tüm durumlarda, resim döndürme, verilerin dikey görünmesi için saat yönünde nasıl döndürülmesi gerektiğini açıklar.

Aşağıdaki örneklerde, kamera sensörünün yönüne bağlı olarak görüntünün nasıl döndürülmesi gerektiği gösterilmektedir. Ayrıca, hedef rotasyonunun görüntü rotasyonuna ayarlandığı varsayılır.

Örnek 1: Sensör 90 derece döndürülmüş

Cihaz örneği: Pixel 3 XL

Ekran döndürme = 0
Ekran yönü = Dikey
Resim döndürme = 90

Ekran döndürme = 90
Ekran yönü = Yatay
Resim döndürme = 0

2. örnek: Sensör 270 derece döndürülmüş

Cihaz örneği: Nexus 5X

Ekran döndürme = 0
Ekran yönü = Dikey
Resim döndürme = 270

Ekran döndürme = 90
Ekran yönü = Yatay
Resim döndürme = 180

3. örnek: Sensör 0 derece döndürülmüş

Cihaz örneği: Pixel C (Tablet)

Ekran döndürme = 0
Ekran yönü = Yatay
Resim döndürme = 0

Ekran döndürme = 270
Ekran yönü = Dikey
Resim döndürme = 90

Bir resmin döndürülmüşlüğünü hesaplama

ImageAnalysis

ImageAnalysis'nin Analyzer, kameradan ImageProxy biçiminde resim alır. Her resimde, aşağıdakiler aracılığıyla erişilebilen döndürme bilgileri bulunur:

val rotation = imageProxy.imageInfo.rotationDegrees

Bu değer, resmin ImageAnalysis'ün hedef rotasyonuyla eşleşmesi için saat yönünde döndürülmesi gereken dereceleri temsil eder. Bir Android uygulaması bağlamında, ImageAnalysis'ün hedef rotasyonu genellikle ekranın yönüyle eşleşir.

ImageCapture

Bir yakalama sonucunun hazır olduğunu bildirmek için ImageCapture örneğine geri çağırma işlevi eklenir. Sonuç, çekilen resim veya bir hata olabilir.

Fotoğraf çekilirken sağlanan geri çağırma aşağıdaki türlerden biri olabilir:

  • OnImageCapturedCallback: ImageProxy biçiminde bellek içi erişime sahip bir resim alır.
  • OnImageSavedCallback: Çekilen resim ImageCapture.OutputFileOptions tarafından belirtilen konumda başarıyla depolandığında çağrılır. Seçenekler, File, OutputStream veya MediaStore'deki bir yeri belirtebilir.

Formatından (ImageProxy, File, OutputStream, MediaStore Uri) bağımsız olarak çekilen görüntünün döndürülmesi, ImageCapture'in hedef döndürmesiyle eşleşmesi için çekilen görüntünün saat yönünde döndürülmesi gereken döndürme derecelerini temsil eder. Bu döndürme derecesi, Android uygulaması bağlamında genellikle ekranın yönüyle eşleşir.

Çekilen görüntünün döndürülmesi aşağıdaki yöntemlerden biriyle alınabilir:

ImageProxy

val rotation = imageProxy.imageInfo.rotationDegrees

File

val exif = Exif.createFromFile(file)
val rotation = exif.rotation

OutputStream

val byteArray = outputStream.toByteArray()
val exif = Exif.createFromInputStream(ByteArrayInputStream(byteArray))
val rotation = exif.rotation

MediaStore uri

val inputStream = contentResolver.openInputStream(outputFileResults.savedUri)
val exif = Exif.createFromInputStream(inputStream)
val rotation = exif.rotation

Bir resmin döndürüldüğünü doğrulama

ImageAnalysis ve ImageCapture kullanım alanları, başarılı bir yakalama isteği sonrasında kameradan ImageProxy alır. ImageProxy, bir resmi ve döndürülmesi de dahil olmak üzere resimle ilgili bilgileri kapsar. Bu döndürme bilgileri, kullanım alanının hedef döndürmesiyle eşleşmesi için resmin döndürülmesi gereken dereceleri temsil eder.

Bir resmin döndürme doğrulama akışı

ImageCapture/ImageAnalysis hedefi döndürme yönergeleri

Birçok cihaz varsayılan olarak dikey veya yatay modu tersine çevirmez. Bu nedenle, bazı Android uygulamaları bu yönleri desteklemez. Bir uygulamanın bu özelliği destekleyip desteklememesi, kullanım alanlarının hedef rotasyonunun güncellenme şeklini değiştirir.

Aşağıda, kullanım alanlarının hedef dönüşünün ekran dönüşüyle nasıl senkronize edileceğini açıklayan iki tablo verilmiştir. İlk yöntemde, dört yönü de desteklerken bu işlemin nasıl yapılacağı gösterilir. İkinci yöntemde ise yalnızca cihazın varsayılan olarak döndüğü yönler ele alınır.

Uygulamanızda hangi yönergelerin uygulanacağını seçmek için:

  1. Uygulamanızın kamerasının Activity kilitli bir yönü, kilidi açık bir yönü veya yön yapılandırması değişikliklerini geçersiz kılma özelliği olup olmadığını doğrulayın.

  2. Uygulamanızın kamerasının Activity dört cihaz yönünün (dikey, ters dikey, yatay ve ters yatay) tümünü işleyip işlemeyeceğine veya yalnızca çalıştığı cihazın varsayılan olarak desteklediği yönleri işleyip işlemeyeceğine karar verin.

Dört yönü de desteklemelidir.

Bu tabloda, cihazın yatay dikeye dönmediği durumlarda uyulması gereken belirli kurallar belirtilmiştir. Aynı durum, ters yatay moduna dönmeyen cihazlar için de geçerlidir.

Senaryo Kurallar Tek pencere modu Çoklu pencere bölünmüş ekran modu
Kilitli olmayan yön Activity her oluşturulduğunda kullanım alanlarını ayarlayın (ör. Activity'nin onCreate() geri çağırma işlevinde).
OrientationEventListener'nin onOrientationChanged() özelliğini kullanın. Geri çağırma içinde, kullanım alanlarının hedef rotasyonunu güncelleyin. Bu, sistemin bir yön değişikliğinden sonra bile Activity'yi yeniden oluşturmadığı durumlarda (ör. cihaz 180 derece döndürüldüğünde) geçerlidir. Ekran ters dikey yöndeyken ve cihaz varsayılan olarak ters dikey yönde dönmüyorsa da kullanılır. Cihaz döndürüldüğünde (ör. 90 derece) Activity öğesinin yeniden oluşturulmadığı durumları da ele alır. Bu durum, küçük form faktörlü cihazlarda uygulama ekranın yarısını kapladığında ve büyük cihazlarda uygulama ekranın üçte ikisini kapladığında gerçekleşir.
İsteğe bağlı: AndroidManifest dosyasında Activity'nin screenOrientation özelliğini fullSensor olarak ayarlayın. Bu sayede cihaz ters dikey moddayken kullanıcı arayüzü dik olur ve cihaz 90 derece döndürüldüğünde Activity sistemi tarafından yeniden oluşturulur. Varsayılan olarak ters dikeye dönmeyen cihazları etkilemez. Ekran ters dikey yöndeyken çoklu pencere modu desteklenmez.
Kilitli yön Kullanım alanlarını yalnızca bir kez, Activity ilk oluşturulduğunda (ör. Activity'ın onCreate() geri çağırma çağrısında) ayarlayın.
OrientationEventListener'nin onOrientationChanged() özelliğini kullanın. Geri çağırma içinde, Önizleme hariç kullanım alanlarının hedef rotasyonunu güncelleyin. Cihaz döndürüldüğünde (ör. 90 derece) Activity öğesinin yeniden oluşturulmadığı durumları da ele alır. Bu durum, küçük form faktörlü cihazlarda uygulama ekranın yarısını kapladığında ve büyük cihazlarda uygulama ekranın üçte ikisini kapladığında gerçekleşir.
Yön configChanges geçersiz kılındı Kullanım alanlarını yalnızca bir kez, Activity ilk oluşturulduğunda (ör. Activity'ın onCreate() geri çağırma çağrısında) ayarlayın.
OrientationEventListener'nin onOrientationChanged() özelliğini kullanın. Geri çağırma içinde, kullanım alanlarının hedef rotasyonunu güncelleyin. Cihaz döndürüldüğünde (ör. 90 derece) Activity öğesinin yeniden oluşturulmadığı durumları da ele alır. Bu durum, küçük form faktörlü cihazlarda uygulama ekranın yarısını kapladığında ve büyük cihazlarda uygulama ekranın üçte ikisini kapladığında gerçekleşir.
İsteğe bağlı: AndroidManifest dosyasında etkinliğin screenOrientation özelliğini fullSensor olarak ayarlayın. Cihaz ters dikey moddayken kullanıcı arayüzünün dik durmasına olanak tanır. Varsayılan olarak ters dikeye dönmeyen cihazları etkilemez. Ekran ters dikey yöndeyken çoklu pencere modu desteklenmez.

Yalnızca cihaz tarafından desteklenen yönleri destekleyin.

Yalnızca cihazın varsayılan olarak desteklediği yönleri destekler (yatay dikey/yatay yatay yönleri içerebilir veya içermeyebilir).

Senaryo Kurallar Çoklu pencere bölünmüş ekran modu
Kilitli olmayan yön Activity her oluşturulduğunda kullanım alanlarını ayarlayın (ör. Activity'nin onCreate() geri çağırma işlevinde).
DisplayListener'nin onDisplayChanged() sürümünü kullanın. Geri çağırma içinde, kullanım alanlarının hedef dönüşmesini güncelleyin (ör. cihaz 180 derece döndürüldüğünde). Cihaz döndürüldüğünde (ör. 90 derece) Activity öğesinin yeniden oluşturulmadığı durumları da ele alır. Bu durum, küçük form faktörlü cihazlarda uygulama ekranın yarısını kapladığında ve büyük cihazlarda uygulama ekranın üçte ikisini kapladığında gerçekleşir.
Kilitli yön Kullanım alanlarını yalnızca bir kez, Activity ilk oluşturulduğunda (ör. Activity'ın onCreate() geri çağırma çağrısında) ayarlayın.
OrientationEventListener'nin onOrientationChanged() özelliğini kullanın. Geri çağırma içinde, kullanım alanlarının hedef rotasyonunu güncelleyin. Cihaz döndürüldüğünde (ör. 90 derece) Activity öğesinin yeniden oluşturulmadığı durumları da ele alır. Bu durum, küçük form faktörlü cihazlarda uygulama ekranın yarısını kapladığında ve büyük cihazlarda uygulama ekranın üçte ikisini kapladığında gerçekleşir.
Yön configChanges geçersiz kılındı Kullanım alanlarını yalnızca bir kez, Activity ilk oluşturulduğunda (ör. Activity'ın onCreate() geri çağırma çağrısında) ayarlayın.
DisplayListener'nin onDisplayChanged() sürümünü kullanın. Geri çağırma içinde, kullanım alanlarının hedef dönüşmesini güncelleyin (ör. cihaz 180 derece döndürüldüğünde). Cihaz döndürüldüğünde (ör. 90 derece) Activity öğesinin yeniden oluşturulmadığı durumları da ele alır. Bu durum, küçük form faktörlü cihazlarda uygulama ekranın yarısını kapladığında ve büyük cihazlarda uygulama ekranın üçte ikisini kapladığında gerçekleşir.

Kilidi açılmış yön

Activity, ekran yönü (dikey veya yatay gibi) cihazın fiziksel yönüyle eşleştiğinde kilidi açık bir yönde olur. Bazı cihazlar varsayılan olarak ters dikey/yatay yönü desteklemez. Cihazı dört yöne de dönmeye zorlamak için Activity'nin screenOrientation mülkünü fullSensor olarak ayarlayın.

Çoklu pencere modunda, varsayılan olarak ters dikey/yatay modu desteklemeyen bir cihaz, screenOrientation mülkü fullSensor olarak ayarlanmış olsa bile ters dikey/yatay moduna dönmez.

<!-- The Activity has an unlocked orientation, but might not rotate to reverse
portrait/landscape in single-window mode if the device doesn't support it by
default. -->
<activity android:name=".UnlockedOrientationActivity" />

<!-- The Activity has an unlocked orientation, and will rotate to all four
orientations in single-window mode. -->
<activity
   android:name=".UnlockedOrientationActivity"
   android:screenOrientation="fullSensor" />

Kilitli yön

Ekran, cihazın fiziksel yönünden bağımsız olarak aynı ekran yönünde (dikey veya yatay gibi) kaldığında kilitli bir yöndedir. Bu işlem, AndroidManifest.xml dosyasındaki Activity tanımında screenOrientation özelliğini belirterek yapılabilir.

Ekran kilitli bir yöndeyken sistem, cihaz döndürüldüğünde Activity'yi yok edip yeniden oluşturmaz.

<!-- The Activity keeps a portrait orientation even as the device rotates. -->
<activity
   android:name=".LockedOrientationActivity"
   android:screenOrientation="portrait" />

Yönlendirme yapılandırması değişiklikleri geçersiz kılındı

Bir Activity, yön yapılandırması değişikliklerini geçersiz kıldığında sistem, cihazın fiziksel yönü değiştiğinde bu yapılandırmayı yok edip yeniden oluşturmaz. Sistem, kullanıcı arayüzünü cihazın fiziksel yönüne uyacak şekilde günceller.

<!-- The Activity's UI might not rotate in reverse portrait/landscape if the
device doesn't support it by default. -->
<activity
   android:name=".OrientationConfigChangesOverriddenActivity"
   android:configChanges="orientation|screenSize" />

<!-- The Activity's UI will rotate to all 4 orientations in single-window
mode. -->
<activity
   android:name=".OrientationConfigChangesOverriddenActivity"
   android:configChanges="orientation|screenSize"
   android:screenOrientation="fullSensor" />

Kamera kullanım alanları kurulumu

Yukarıda açıklanan senaryolarda kamera kullanım alanları, Activity ilk oluşturulduğunda ayarlanabilir.

Kilidi açık yönü olan bir Activity söz konusu olduğunda, sistem yön değişikliklerinde Activity'yi yok edip yeniden oluşturduğundan bu kurulum cihaz her döndürüldüğünde yapılır. Bu, kullanım alanlarının hedef rotasyonunu her seferinde varsayılan olarak ekranın yönüne uyacak şekilde ayarlamasına neden olur.

Kilitli yönü olan veya yön yapılandırması değişikliklerini geçersiz kılan bir Activity söz konusu olduğunda bu kurulum, Activity ilk oluşturulduğunda bir kez yapılır.

class CameraActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)

       val cameraProcessFuture = ProcessCameraProvider.getInstance(this)
       cameraProcessFuture.addListener(Runnable {
          val cameraProvider = cameraProcessFuture.get()

          // By default, the use cases set their target rotation to match the
          // display’s rotation.
          val preview = buildPreview()
          val imageAnalysis = buildImageAnalysis()
          val imageCapture = buildImageCapture()

          cameraProvider.bindToLifecycle(
              this, cameraSelector, preview, imageAnalysis, imageCapture)
       }, mainExecutor)
   }
}

OrientationEventListener kurulumu

OrientationEventListener kullanarak, cihazın yönü değiştikçe kamera kullanım alanlarının hedef dönüşünü sürekli olarak güncelleyebilirsiniz.

class CameraActivity : AppCompatActivity() {

    private val orientationEventListener by lazy {
        object : OrientationEventListener(this) {
            override fun onOrientationChanged(orientation: Int) {
                if (orientation == ORIENTATION_UNKNOWN) {
                    return
                }

                val rotation = when (orientation) {
                     in 45 until 135 -> Surface.ROTATION_270
                     in 135 until 225 -> Surface.ROTATION_180
                     in 225 until 315 -> Surface.ROTATION_90
                     else -> Surface.ROTATION_0
                 }

                 imageAnalysis.targetRotation = rotation
                 imageCapture.targetRotation = rotation
            }
        }
    }

    override fun onStart() {
        super.onStart()
        orientationEventListener.enable()
    }

    override fun onStop() {
        super.onStop()
        orientationEventListener.disable()
    }
}

DisplayListener kurulumu

DisplayListener kullanmak, belirli durumlarda kamera kullanım alanlarının hedef dönüşünü güncellemenize olanak tanır. Örneğin, sistem cihaz 180 derece döndükten sonra Activity'ı yok edip yeniden oluşturmadığında.

class CameraActivity : AppCompatActivity() {

    private val displayListener = object : DisplayManager.DisplayListener {
        override fun onDisplayChanged(displayId: Int) {
            if (rootView.display.displayId == displayId) {
                val rotation = rootView.display.rotation
                imageAnalysis.targetRotation = rotation
                imageCapture.targetRotation = rotation
            }
        }

        override fun onDisplayAdded(displayId: Int) {
        }

        override fun onDisplayRemoved(displayId: Int) {
        }
    }

    override fun onStart() {
        super.onStart()
        val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
        displayManager.registerDisplayListener(displayListener, null)
    }

    override fun onStop() {
        super.onStop()
        val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
        displayManager.unregisterDisplayListener(displayListener)
    }
}