Uygulamanızın kullanıma sunulmasını sağlayın

Büyük ve katlanmamış ekranlar ile benzersiz katlanmış durumlar, katlanabilir cihazlarda yeni kullanıcı deneyimlerine olanak tanır. Uygulamanızı katlanabilir cihazlara uygun hale getirmek için Jetpack WindowManager kitaplığını kullanın. Bu kitaplık, katlanabilir cihazların pencere özellikleriyle (ör. katlanma ve menteşe) ilgili bir API yüzeyi sağlar. Uygulamanız katlama özelliğini desteklediğinde, önemli içerikleri katlama veya menteşe alanına yerleştirmemek için düzenini uyarlayabilir ve katlama ile menteşeleri doğal ayırıcılar olarak kullanabilir.

Bir cihazın masaüstü veya kitap duruşu gibi yapılandırmaları destekleyip desteklemediğini anlamak, farklı düzenleri destekleme veya belirli özellikler sunma konusundaki kararlara yön verebilir.

Pencere bilgileri

Jetpack WindowManager'daki WindowInfoTracker arayüzü, pencere düzeni bilgilerini gösterir. Arayüzün windowLayoutInfo() yöntemi, uygulamanızı katlanabilir bir cihazın katlanma durumu hakkında bilgilendiren WindowLayoutInfo verileri akışını döndürür. WindowInfoTracker#getOrCreate() yöntemi, WindowInfoTracker öğesinin bir örneğini oluşturur.

WindowManager, Kotlin akışları ve Java geri çağırmaları kullanılarak WindowLayoutInfo veri toplanmasını destekler.

Kotlin akışları

WindowLayoutInfo veri toplamayı başlatmak ve durdurmak için repeatOnLifecycle kod bloğunun yaşam döngüsü en az STARTED olduğunda yürütüldüğü ve yaşam döngüsü STOPPED olduğunda durdurulduğu yeniden başlatılabilir yaşam döngüsü farkında olan bir eşzamanlı rutin kullanabilirsiniz. Yaşam döngüsü STARTED olduğunda kod bloğunun yürütülmesi otomatik olarak yeniden başlatılır. Aşağıdaki örnekte, kod bloğu WindowLayoutInfo verilerini toplar ve kullanır:

class DisplayFeaturesActivity : AppCompatActivity() {

    private lateinit var binding: ActivityDisplayFeaturesBinding

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

        binding = ActivityDisplayFeaturesBinding.inflate(layoutInflater)
        setContentView(binding.root)

        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                WindowInfoTracker.getOrCreate(this@DisplayFeaturesActivity)
                    .windowLayoutInfo(this@DisplayFeaturesActivity)
                    .collect { newLayoutInfo ->
                        // Use newLayoutInfo to update the layout.
                    }
            }
        }
    }
}

Java geri çağırmaları

androidx.window:window-java bağımlılığında bulunan geri çağırma uyumluluk katmanı, Kotlin akışı kullanmadan WindowLayoutInfo güncellemelerini toplamanıza olanak tanır. Yapı, WindowInfoTrackerCallbackAdapter sınıfını içerir. Bu sınıf, WindowInfoTracker öğesini uyarlayarak örneğin WindowLayoutInfo güncellemelerini almak için geri çağırmaları kaydetmeyi (ve kaydı silmeyi) destekler:

public class SplitLayoutActivity extends AppCompatActivity {

    private WindowInfoTrackerCallbackAdapter windowInfoTracker;
    private ActivitySplitLayoutBinding binding;
    private final LayoutStateChangeCallback layoutStateChangeCallback =
            new LayoutStateChangeCallback();

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

       windowInfoTracker =
                new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
   }

   @Override
   protected void onStart() {
       super.onStart();
       windowInfoTracker.addWindowLayoutInfoListener(
                this, Runnable::run, layoutStateChangeCallback);
   }

   @Override
   protected void onStop() {
       super.onStop();
       windowInfoTracker
           .removeWindowLayoutInfoListener(layoutStateChangeCallback);
   }

   class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
       @Override
       public void accept(WindowLayoutInfo newLayoutInfo) {
           SplitLayoutActivity.this.runOnUiThread( () -> {
               // Use newLayoutInfo to update the layout.
           });
       }
   }
}

RxJava desteği

RxJava'yı (2 veya 3 sürümü) kullanıyorsanız Kotlin akışı kullanmadan WindowLayoutInfo güncellemelerini toplamanıza olanak tanıyan Observable veya Flowable kullanmanızı sağlayan yapılardan yararlanabilirsiniz.

androidx.window:window-rxjava2 ve androidx.window:window-rxjava3 bağımlılıkları tarafından sağlanan uyumluluk katmanı, uygulamanızın WindowLayoutInfo güncellemelerini almasını sağlayan WindowInfoTracker#windowLayoutInfoFlowable() ve WindowInfoTracker#windowLayoutInfoObservable() yöntemlerini içerir. Örneğin:

class RxActivity: AppCompatActivity {

    private lateinit var binding: ActivityRxBinding

    private var disposable: Disposable? = null
    private lateinit var observable: Observable<WindowLayoutInfo>

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

        // Create a new observable.
        observable = WindowInfoTracker.getOrCreate(this@RxActivity)
            .windowLayoutInfoObservable(this@RxActivity)
   }

   @Override
   protected void onStart() {
       super.onStart();

        // Subscribe to receive WindowLayoutInfo updates.
        disposable?.dispose()
        disposable = observable
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { newLayoutInfo ->
            // Use newLayoutInfo to update the layout.
        }
   }

   @Override
   protected void onStop() {
       super.onStop();

        // Dispose of the WindowLayoutInfo observable.
        disposable?.dispose()
   }
}

Katlanabilir ekranların özellikleri

Jetpack WindowManager'ın WindowLayoutInfo sınıfı, bir ekran penceresinin özelliklerini DisplayFeature öğeleri listesi olarak kullanılabilir hâle getirir.

FoldingFeature, aşağıdaki özellikleri de içeren katlanabilir ekranlar hakkında bilgi sağlayan bir DisplayFeature türüdür:

  • state: Cihazın katlanmış durumu, FLAT veya HALF_OPENED

  • orientation: Katlama veya menteşenin yönü, HORIZONTAL veya VERTICAL

  • occlusionType: Katlama veya menteşe, ekranın bir kısmını gizliyor mu? NONE veya FULL

  • isSeparating: Katlama veya menteşe, iki mantıksal ekran alanı oluşturuyor mu? Doğru veya yanlış.

Ekran iki görüntüleme alanına ayrıldığından katlanabilir cihazlar HALF_OPENED her zaman isSeparating olarak bildirir. Ayrıca, uygulama her iki ekrana da yayıldığında çift ekranlı bir cihazda isSeparating her zaman doğrudur.

FoldingFeature bounds özelliği (DisplayFeature öğesinden devralınır), katlama veya menteşe gibi katlanabilir bir özelliğin sınırlayıcı dikdörtgenini temsil eder. Sınırlar, ekrandaki öğeleri özelliğe göre konumlandırmak için kullanılabilir:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    // ...
    lifecycleScope.launch(Dispatchers.Main) {
        lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
            // Safely collects from WindowInfoTracker when the lifecycle is
            // STARTED and stops collection when the lifecycle is STOPPED.
            WindowInfoTracker.getOrCreate(this@MainActivity)
                .windowLayoutInfo(this@MainActivity)
                .collect { layoutInfo ->
                    // New posture information.
                    val foldingFeature = layoutInfo.displayFeatures
                        .filterIsInstance<FoldingFeature>()
                        .firstOrNull()
                    // Use information from the foldingFeature object.
                }
        }
    }
}

Java

private WindowInfoTrackerCallbackAdapter windowInfoTracker;
private final LayoutStateChangeCallback layoutStateChangeCallback =
                new LayoutStateChangeCallback();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    // ...
    windowInfoTracker =
            new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
}

@Override
protected void onStart() {
    super.onStart();
    windowInfoTracker.addWindowLayoutInfoListener(
            this, Runnable::run, layoutStateChangeCallback);
}

@Override
protected void onStop() {
    super.onStop();
    windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback);
}

class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
    @Override
    public void accept(WindowLayoutInfo newLayoutInfo) {
        // Use newLayoutInfo to update the Layout.
        List<DisplayFeature> displayFeatures = newLayoutInfo.getDisplayFeatures();
        for (DisplayFeature feature : displayFeatures) {
            if (feature instanceof FoldingFeature) {
                // Use information from the feature object.
            }
        }
    }
}

Masa üstü duruşu

Uygulamanız, FoldingFeature nesnesinde yer alan bilgileri kullanarak telefonu bir yüzeye yerleştirme, menteşeyi yatay konuma getirme ve katlanabilir ekranı yarıya kadar açma gibi duruşları destekleyebilir.

Masaüstü duruşu, kullanıcıların telefonlarını ellerinde tutmadan kullanabilmelerini sağlar. Masa üstü duruşu, medya izlemek, fotoğraf çekmek ve görüntülü görüşme yapmak için idealdir.

Şekil 1. Masaüstü duruşunda bir video oynatıcı uygulaması: Ekranın dikey kısmında video, yatay kısmında oynatma kontrolleri.

Cihazın masaüstü duruşunda olup olmadığını belirlemek için FoldingFeature.State ve FoldingFeature.Orientation kullanın:

Kotlin

fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
}

Java

boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}

Cihazın masaüstü duruşunda olduğunu öğrendikten sonra uygulama düzeninizi buna göre güncelleyin. Medya uygulamalarında bu genellikle oynatmanın katlanma çizgisinin üzerinde yerleştirilmesi ve kontroller ile ek içeriklerin, eller serbest görüntüleme veya dinleme deneyimi için hemen ardından konumlandırılması anlamına gelir.

Android 15 (API düzeyi 35) ve sonraki sürümlerde, cihazın mevcut durumundan bağımsız olarak bir cihazın masaüstü duruşunu destekleyip desteklemediğini tespit etmek için senkron bir API çağırabilirsiniz.

API, cihaz tarafından desteklenen duruşların bir listesini sağlar. Liste, masaüstü duruşunu içeriyorsa duruşu desteklemek için uygulama düzeninizi bölebilir ve masaüstü ile tam ekran düzenleri için uygulama kullanıcı arayüzünüzde A/B testleri çalıştırabilirsiniz.

Kotlin

if (WindowSdkExtensions.getInstance().extensionsVersion >= 6) {
    val postures = WindowInfoTracker.getOrCreate(context).supportedPostures
    if (postures.contains(TABLE_TOP)) {
        // Device supports tabletop posture.
   }
}

Java

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
    List<SupportedPosture> postures = WindowInfoTracker.getOrCreate(context).getSupportedPostures();
    if (postures.contains(SupportedPosture.TABLETOP)) {
        // Device supports tabletop posture.
    }
}

Örnekler

Kitap duruşu

Kitap duruşu da benzersiz bir katlanabilir özellik. Bu duruşta cihaz yarıya kadar açılıyor ve menteşe dikey konumda oluyor. Kitap duruşu, e-kitap okumak için idealdir. Büyük ekranlı katlanabilir cihazlarda, ciltli kitap gibi açılan iki sayfalık düzenle kitap duruşu, gerçek kitap okuma deneyimini yakalar.

Ellerinizi kullanmadan fotoğraf çekerken farklı bir en boy oranı yakalamak istiyorsanız fotoğrafçılık için de kullanılabilir.

Kitap duruşunu, masaüstü duruşu için kullanılan tekniklerle aynı şekilde uygulayın. Tek fark, kodun katlama özelliği yönünün yatay yerine dikey olduğunu kontrol etmesi gerektiğidir:

Kotlin

fun isBookPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.VERTICAL
}

Java

boolean isBookPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.VERTICAL);
}

Pencere boyutu değişiklikleri

Bir cihazın yapılandırmasında değişiklik yapıldığında (ör. cihaz katlandığında veya açıldığında, döndürüldüğünde ya da çoklu pencere modunda bir pencerenin boyutu değiştirildiğinde) uygulamanın görüntüleme alanı değişebilir.

Jetpack WindowManager WindowMetricsCalculator sınıfı, mevcut ve maksimum pencere metriklerini almanızı sağlar. API düzeyi 30'da kullanıma sunulan platform gibi, WindowMetrics WindowManager WindowMetrics de pencere sınırlarını sağlar ancak API, API düzeyi 14'e kadar geriye dönük olarak uyumludur.

Pencere boyutu sınıflarını kullanma başlıklı makaleyi inceleyin.

Ek kaynaklar

Örnekler

  • Jetpack WindowManager: Jetpack WindowManager kitaplığının nasıl kullanılacağına dair örnek
  • Jetcaster : Compose ile masaüstü duruşu uygulama

Codelab uygulamaları