CameraX kullanım alanı döndürmeleri

Bu konuda, doğru rotasyon bilgilerine sahip resimler almak için uygulamanızda CameraX kullanım alanlarını ImageAnalysis veya ImageCapture kullanım alanından nasıl ayarlayabileceğiniz gösterilmektedir. Bu durumda:

  • ImageAnalysis kullanım alanındaki Analyzer, doğru rotasyonla kareler almalıdır.
  • ImageCapture kullanım alanı, resimleri doğru yönde döndürerek çekmelidir.

Terminoloji

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

Ekran yönü
Bu, cihazın hangi tarafının yukarı konumda olduğunu belirtir ve şu 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 saat yönünün tersine, doğal yönünden döndürüldüğü dereceyi temsil eder.
Hedef rotasyonu
Bu, doğal yönüne ulaşmak için cihazın saat yönünde döndürüleceği derece sayısını temsil eder.

Hedef rotasyonu nasıl belirlenir?

Aşağıdaki örnekler, bir cihazın doğal yönüne göre hedef dönüşünün nasıl belirleneceğini gösterir.

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

Cihaz örneği: Pixel 3 XL

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

Ekran rotasyonu = 0
Hedef rotasyon = 0

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

Ekran rotasyonu = 90
Hedef rotasyon = 90

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

Cihaz örneği: Pixel C

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

Ekran rotasyonu = 0
Hedef rotasyon = 0

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

Ekran rotasyonu = 270
Hedef rotasyon = 270

Resim döndürme

Hangisi başarıya ulaştı? Sensör yönü Android'de sabit bir değer olarak tanımlanır. Bu değer, cihaz doğal bir pozisyondayken sensörün cihazın üst tarafından döndürüldüğü dereceyi (0, 90, 180, 270) temsil eder. Şemalardaki tüm durumlarda resim döndürme, verilerin dik görünebilmesi 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 resim döndürmenin ne olması gerektiği gösterilmektedir. Ayrıca, hedef rotasyonun ekran rotasyonuna ayarlandığı varsayılır.

1. Örnek: Sensör 90 derece döndürüldü

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
Görüntü yönü = Yatay
Resim döndürme = 0

2. Örnek: Sensör 270 derece döndürüldü

Cihaz örneği: Nexus 5X

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

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

3. Örnek: Sensör 0 derece döndürüldü

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önüşünü hesaplama

ImageAnalysis

ImageAnalysis cihazının Analyzer cihazı, kameradan ImageProxy şekilde resim alır. Her resim, şunlar aracılığıyla erişilebilen rotasyon bilgilerini içerir:

val rotation = imageProxy.imageInfo.rotationDegrees

Bu değer, ImageAnalysis öğesinin hedef dönüşüyle eşleşmesi için resmin saat yönünde döndürülmesi gereken dereceyi temsil eder. Bir Android uygulaması söz konusu olduğunda ImageAnalysis öğesinin hedef dönüşü genellikle ekranın yönüyle eşleşir.

Resim Yakalama

Bir yakalama sonucu hazır olduğunda sinyal vermek için ImageCapture örneğine bir geri çağırma eklenir. Sonuç, yakalanan görüntü veya bir hata olabilir.

Fotoğraf çekerken, sağlanan geri arama aşağıdaki türlerden birinde olabilir:

  • OnImageCapturedCallback: Bellek içi erişimi olan bir görüntüyü ImageProxy biçiminde alır.
  • OnImageSavedCallback: Yakalanan görüntü, ImageCapture.OutputFileOptions tarafından belirtilen konumda başarıyla saklandığında çağrılır. Seçenekler bir File, bir OutputStream veya MediaStore içinde bir konum belirtebilir.

Yakalanan resmin biçiminden (ImageProxy, File, OutputStream, MediaStore Uri) bakılmaksızın, yakalanan resmin saat yönünde döndürülmesi gereken döndürme derecelerini temsil eder. Bu döndürme işlemi, ImageCapture uygulamasının hedef dönüşüne uygun olarak yine Android uygulaması bağlamında genellikle ekranın yönüyle eşleşir.

Yakalanan görüntünün dönüşünü alma, aşağıdaki yöntemlerden biri kullanılarak yapılabilir:

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

Resmin dönüşünü doğrulama

ImageAnalysis ve ImageCapture kullanım alanları, başarılı bir yakalama isteğinin ardından kameradan ImageProxy alır. ImageProxy, bir resmi ve döndürme de dahil olmak üzere resimle ilgili bilgileri sarmalar. Bu döndürme bilgileri, kullanım senaryosunun hedef dönüşüyle eşleşmesi için resmin döndürülmesi gereken dereceleri gösterir.

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

ImageCapture/ImageAnalysis hedef rotasyonu yönergeleri

Çoğu cihaz varsayılan olarak ters dikey veya ters yatay yönde döndürülmediğinden, bazı Android uygulamaları bu yönleri desteklemez. Bir uygulamanın bunu destekleyip desteklememesi, kullanım alanlarının hedef rotasyonunu güncelleme şeklini değiştirir.

Aşağıda, kullanım alanlarının hedef rotasyonunun ekran rotasyonuyla nasıl senkronize tutulacağını tanımlayan iki tablo bulunmaktadır. İlk örnekte, dört yönün tümü desteklenirken bunun nasıl yapılacağı gösterilmektedir; ikincisinde ise yalnızca cihazın varsayılan olarak döndüğü yönleri ele alınmaktadır.

Uygulamanızda uyulacak kuralları seçmek için:

  1. Uygulamanızın kamerasının (Activity) kilitli ve kilitsiz bir yöne sahip olup olmadığını ya da yön yapılandırma değişikliklerini geçersiz kılıp kılmadığını doğrulayın.

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

Dört yönün hepsini destekleyin

Bu tabloda, cihazın ters dikey yöne doğru dönmediği durumlarda uyulması gereken belirli yönergelerden bahsedilmektedir. Aynı durum, yatay yönde döndürülecek şekilde dönmeyen cihazlara da uygulanabilir.

Senaryo Kurallar Tek pencere modu Çoklu pencere bölünmüş ekran modu
Kilidi açılmış yön Activity her oluşturulduğunda (ör. Activity onCreate() geri çağırması) kullanım alanlarını ayarlayın.
OrientationEventListener için onOrientationChanged(). Geri çağırmanın içinde, kullanım alanlarının hedef rotasyonunu güncelleyin. Bu, cihazın 180 derece döndürülmesi gibi bir yön değişikliğinden sonra bile sistemin Activity öğesini yeniden oluşturmadığı durumları yönetir. Ayrıca, ekran ters dikey yönde olduğunda ve cihaz varsayılan olarak ters dikey yöne dönmediğinde de kullanılır. Ayrıca, cihaz döndürüldüğünde Activity öğesinin yeniden oluşturulmadığı durumları da işler (örneğin, 90 derece). Bu durum, küçük form faktörüne sahip cihazlarda uygulama ekranın yarısını kaplarken, büyük cihazlarda ise ekranın üçte ikisini kapladığında ortaya çıkar.
İsteğe bağlı: AndroidManifest dosyasında Activity öğesinin screenOrientation özelliğini fullSensor olarak ayarlayın. Bu, cihaz ters dikey durumdayken kullanıcı arayüzünün dik olmasına ve cihaz 90 derece döndürüldüğünde Activity öğesinin sistem tarafından yeniden oluşturulmasına olanak tanır. Varsayılan olarak dikey dikey yöne doğru döndürülmeyen cihazlar üzerinde bir etkisi yoktur. Ekran ters dikey yöndeyken çoklu pencere modu desteklenmez.
Kilitli yön Kullanım alanlarını Activity ilk oluşturulduğunda (ör. Activity onCreate() geri çağırması) yalnızca bir kez ayarlayın.
OrientationEventListener için onOrientationChanged(). Geri çağırmanın içinde Önizleme dışındaki kullanım alanlarının hedef rotasyonunu güncelleyin. Ayrıca, cihaz döndürüldüğünde Activity öğesinin yeniden oluşturulmadığı durumları da işler (örneğin, 90 derece). Bu durum, küçük form faktörüne sahip cihazlarda uygulama ekranın yarısını kaplarken, büyük cihazlarda ise ekranın üçte ikisini kapladığında ortaya çıkar.
Yön configChanges geçersiz kılındı Kullanım alanlarını Activity ilk oluşturulduğunda (ör. Activity onCreate() geri çağırması) yalnızca bir kez ayarlayın.
OrientationEventListener için onOrientationChanged(). Geri çağırmanın içinde, kullanım alanlarının hedef rotasyonunu güncelleyin. Ayrıca, cihaz döndürüldüğünde Activity öğesinin yeniden oluşturulmadığı durumları da işler (örneğin, 90 derece). Bu durum, küçük form faktörüne sahip cihazlarda uygulama ekranın yarısını kaplarken, büyük cihazlarda ise ekranın üçte ikisini kapladığında ortaya çıkar.
İsteğe bağlı: AndroidManifest dosyasında Activity'in screenOrientation özelliğini fullSensor olarak ayarlayın. Cihaz ters dikey durumdayken kullanıcı arayüzünün dik olmasına olanak tanır. Varsayılan olarak dikey dikey yöne doğru döndürülmeyen cihazlar üzerinde bir etkisi yoktur. Ekran ters dikey yöndeyken çoklu pencere modu desteklenmez.

Yalnızca cihaz tarafından desteklenen yönler desteklenir

Yalnızca cihazın varsayılan olarak desteklediği yönleri (ters dikey/ters yatay olabilir veya olmayabilir) destekler.

Senaryo Kurallar Çoklu pencere bölünmüş ekran modu
Kilidi açılmış yön Activity her oluşturulduğunda (ör. Activity onCreate() geri çağırması) kullanım alanlarını ayarlayın.
DisplayListener uygulamasının onDisplayChanged() özelliğini kullanın. Geri çağırmanın içinde, cihazın 180 derece döndürülmesi gibi kullanım alanlarının hedef rotasyonunu güncelleyin. Ayrıca, cihaz döndürüldüğünde Activity öğesinin yeniden oluşturulmadığı durumları da işler (örneğin, 90 derece). Bu durum, küçük form faktörüne sahip cihazlarda uygulama ekranın yarısını kaplarken, büyük cihazlarda ise ekranın üçte ikisini kapladığında ortaya çıkar.
Kilitli yön Kullanım alanlarını Activity ilk oluşturulduğunda (ör. Activity onCreate() geri çağırması) yalnızca bir kez ayarlayın.
OrientationEventListener için onOrientationChanged(). Geri çağırmanın içinde, kullanım alanlarının hedef rotasyonunu güncelleyin. Ayrıca, cihaz döndürüldüğünde Activity öğesinin yeniden oluşturulmadığı durumları da işler (örneğin, 90 derece). Bu durum, küçük form faktörüne sahip cihazlarda uygulama ekranın yarısını kaplarken, büyük cihazlarda ise ekranın üçte ikisini kapladığında ortaya çıkar.
Yön configChanges geçersiz kılındı Kullanım alanlarını Activity ilk oluşturulduğunda (ör. Activity onCreate() geri çağırması) yalnızca bir kez ayarlayın.
DisplayListener uygulamasının onDisplayChanged() özelliğini kullanın. Geri çağırmanın içinde, cihazın 180 derece döndürülmesi gibi kullanım alanlarının hedef rotasyonunu güncelleyin. Ayrıca, cihaz döndürüldüğünde Activity öğesinin yeniden oluşturulmadığı durumları da işler (örneğin, 90 derece). Bu durum, küçük form faktörüne sahip cihazlarda uygulama ekranın yarısını kaplarken, büyük cihazlarda ise ekranın üçte ikisini kapladığında ortaya çıkar.

Kilitli olmayan yön

Bazı cihazların varsayılan olarak desteklemediği ters dikey/yatay ekran dışında, Activity cihazın ekran yönü (dikey veya yatay gibi) cihazın fiziksel yönüyle eşleştiğinde kilitsiz bir yöne sahip olur. Cihazı dört yönde de dönmeye zorlamak için Activity öğesinin screenOrientation özelliğini fullSensor olarak ayarlayın.

Çoklu pencere modunda, varsayılan olarak ters dikey/yatay görünümü desteklemeyen bir cihaz, screenOrientation özelliği fullSensor olarak ayarlanmış olsa bile ters dikey/yatay görünümü döndürmez.

<!-- 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

Bir ekranın yönü, cihazın fiziksel yönü ne olursa olsun aynı ekran yönünde (dikey veya yatay gibi) kaldığında kilitli olur. Bu, AndroidManifest.xml dosyasındaki bildirimi içinde Activity öğesinin screenOrientation özelliğini belirterek yapılabilir.

Ekranın yönü kilitli olduğunda sistem, cihaz döndürüldüğünde Activity öğesini yok etmez ve yeniden oluşturmaz.

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

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

Activity yön yapılandırması değişikliklerini geçersiz kılarsa cihazın fiziksel yönü değiştiğinde sistem, cihazı yok etmez ve yeniden oluşturmaz. Sistem, kullanıcı arayüzünü cihazın fiziksel yönüyle eşleşecek ş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.

Yönü kilidi açılmış bir Activity söz konusu olduğunda, sistem yön değiştirildiğinde Activity öğesini söküp yeniden oluşturduğundan cihaz her döndürüldüğünde bu kurulum yapılır. Bu, kullanım alanlarında, hedef rotasyonlarının her seferinde varsayılan olarak ekranın yönüyle eşleşecek şekilde ayarlanmasıyla sonuçlanır.

Yönü kilitli olan veya yön yapılandırması değişikliklerini geçersiz kılan bir Activity olması durumunda 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 kullanmak, cihazın yönü değiştikçe kamera kullanım alanlarının hedef dönüşünü sürekli olarak güncellemenize olanak tanır.

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 (örneğin, cihaz 180 derece döndürüldükten sonra sistem yok edip Activity yeniden oluşturmadığında) kamera kullanım alanlarının hedef dönüşünü güncellemenize olanak tanır.

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)
    }
}