Compose'da pencere ekleri

Android platformu, durum çubuğu ve gezinme çubuğu gibi sistem kullanıcı arayüzünü çizmekten sorumludur. Bu sistem kullanıcı arayüzü, kullanıcının kullandığı uygulamadan bağımsız olarak görüntülenir. WindowInsets, uygulamanızın doğru alanda çizildiğinden ve kullanıcı arayüzünüzün sistem kullanıcı arayüzü tarafından gizlenmediğinden emin olmak için sistem kullanıcı arayüzü hakkında bilgiler sağlar.

Sistem çubuklarının arkasına çizim yapmak için uçtan uca kullanma
Şekil 1. Sistem çubuklarının arkasına çizim yapmak için uçtan uca kullanma

Varsayılan olarak uygulamanızın kullanıcı arayüzü, durum çubuğu ve gezinme çubuğu gibi sistem kullanıcı arayüzü içinde yer alacak şekilde sınırlandırılmıştır. Bu, uygulamanızın içeriğinin sistem kullanıcı arayüzü öğeleri tarafından gizlenmemesini sağlar.

Ancak uygulamaların, sistem kullanıcı arayüzünün de gösterildiği bu alanlarda gösterilmesini etkinleştirmelerini öneririz. Bu, daha sorunsuz bir kullanıcı deneyimiyle sonuçlanır ve uygulamanızın, mevcut pencere alanından en iyi şekilde yararlanmasını sağlar. Bu ayrıca, özellikle yazılım klavyesini gösterirken ve gizlerken uygulamaların sistem kullanıcı arayüzüyle birlikte hareket etmesine olanak tanır.

Bu bölgelerde görüntülemeyi etkinleştirmek ve sistem kullanıcı arayüzünün arkasında içerik görüntülemek, uçtan uca geçiş olarak adlandırılır. Bu sayfada farklı ek öğe türlerini, uçtan uca geçişe nasıl dahil olacağınızı, kullanıcı arayüzünüze animasyon eklemek ve uygulamanızın bazı bölümlerinin anlaşılmasını önlemek için ekteki API'ları nasıl kullanacağınızı öğreneceksiniz.

Inset temel bilgileri

Uçtan uca uygulamalarda, önemli içeriklerin ve etkileşimlerin sistem kullanıcı arayüzü tarafından engellenmediğinden emin olmanız gerekir. Örneğin, gezinme çubuğunun arkasına bir düğme yerleştirilirse kullanıcı bu düğmeyi tıklayamayabilir.

Sistem kullanıcı arayüzünün boyutu ve yerleştirildiği yerle ilgili bilgiler, insets aracılığıyla belirtilir.

Sistem kullanıcı arayüzünün her bir bölümünde, boyutunu ve yerleştirildiği yeri açıklayan bir tür veri bulunur. Örneğin, durum çubuğu öğeleri durum çubuğunun boyutunu ve konumunu, gezinme çubuğu öğeleri ise gezinme çubuğunun boyutunu ve konumunu sağlar. Her bir ek türü, dört piksel boyutundan oluşur: üst, sol, sağ ve alt. Bu boyutlar, sistem kullanıcı arayüzünün, uygulama penceresinin karşılık gelen kenarlarından ne kadar uzandığını belirtir. Dolayısıyla, bu tür bir sistem kullanıcı arayüzüyle çakışmayı önlemek için uygulama kullanıcı arayüzünün bu miktara eklenmesi gerekir.

Aşağıdaki yerleşik Android ek türlerini WindowInsets üzerinden kullanabilirsiniz:

WindowInsets.statusBars

Durum çubuklarını açıklayan ekler. Bunlar, bildirim simgelerini ve diğer göstergeleri içeren en üstteki sistem kullanıcı arayüzü çubuklarıdır.

WindowInsets.statusBarsIgnoringVisibility

Görünür oldukları durumlar için durum çubuğu ekleri. Durum çubukları şu anda gizliyse (kapsamlı tam ekran moduna geçilmesi nedeniyle) ana durum çubuğunun alt öğeleri boş olur, ancak bu öğeler boş olmaz.

WindowInsets.navigationBars

Gezinme çubuklarını açıklayan ekler. Bunlar, cihazın sol, sağ veya alt tarafında bulunan ve görev çubuğunu veya gezinme simgelerini açıklayan sistem kullanıcı arayüzü çubuklarıdır. Bu ayarlar, kullanıcının tercih ettiği gezinme yöntemine ve görev çubuğuyla etkileşimde bulunma tercihine bağlı olarak çalışma zamanında değişebilir.

WindowInsets.navigationBarsIgnoringVisibility

Görünür oldukları durumlar için gezinme çubuğu iç öğeleri. Gezinme çubukları şu anda gizliyse (kapsamlı tam ekran moduna geçilmesi nedeniyle) ana gezinme çubuğunun öğeleri boş olur, ancak bu öğeler boş olmaz.

WindowInsets.captionBar

Serbest biçimli bir pencerede (ör. üst başlık çubuğu) sistem kullanıcı arayüzü pencere süslemesini açıklayan ek.

WindowInsets.captionBarIgnoringVisibility

Altyazı çubuğu, görünür oldukları zamana göre ayarlanır. Altyazı çubukları şu anda gizliyse ana altyazı çubuğu öğeleri boş olur, ancak bu öğeler boş olmaz.

WindowInsets.systemBars

Durum çubukları, gezinme çubukları ve altyazı çubuğunu içeren sistem çubuğu eklerinin birleşimi.

WindowInsets.systemBarsIgnoringVisibility

Görünür oldukları zamanları belirlemek için sistem çubuğu ekleri. Sistem çubukları şu anda gizliyse (kapsamlı tam ekran moduna geçilmesi nedeniyle) ana sistem çubuğu iç öğeleri boş olur, ancak bu öğeler boş olmaz.

WindowInsets.ime

Yazılım klavyesinin kapladığı alan miktarını açıklayan ek bilgiler.

WindowInsets.imeAnimationSource

Mevcut klavye animasyonundan önce yazılım klavyesinin kapladığı alan miktarını açıklayan ek veriler.

WindowInsets.imeAnimationTarget

Mevcut klavye animasyonundan sonra yazılım klavyesinin kaplayacağı alan miktarını açıklayan ek veriler.

WindowInsets.tappableElement

Gezinme kullanıcı arayüzü hakkında daha ayrıntılı bilgileri açıklayan ve "dokunmaların" uygulama tarafından değil, sistem tarafından işleneceği alan miktarını belirten bir ek türü. Hareketle gezinme özelliğine sahip şeffaf gezinme çubukları için bazı uygulama öğelerine sistem gezinme kullanıcı arayüzü üzerinden dokunulabilir.

WindowInsets.tappableElementIgnoringVisibility

Dokunulabilir öğe, görünür oldukları zamana ayarlanır. Dokunulabilir öğeler şu anda gizliyse (kapsamlı tam ekran moduna geçilmesi nedeniyle) dokunulabilir ana öğe kümeleri boş olur, ancak bu öğeler boş olmaz.

WindowInsets.systemGestures

Sistemin gezinme hareketlerini keseceği eklerin miktarını temsil eden ekler. Uygulamalar, bu hareketlerin sınırlı bir kısmının nasıl kullanılacağını Modifier.systemGestureExclusion aracılığıyla manuel olarak belirtebilir.

WindowInsets.mandatorySystemGestures

Her zaman sistem tarafından işlenecek olan ve Modifier.systemGestureExclusion aracılığıyla devre dışı bırakılamayan sistem hareketlerinin bir alt kümesi.

WindowInsets.displayCutout

Ekran kesimiyle (çentik veya küçük iğne deliği) çakışmaması için gereken aralık miktarını temsil eden ekler.

WindowInsets.waterfall

Şelale ekranının eğri alanlarını temsil eden ekler. Şelale ekranının kenarlarında, cihazın kenarlarında kaymaya başladığı kavisli alanlar vardır.

Bu türler, içeriğin gizlenmemesini sağlayan üç "güvenli" iç küme türüyle özetlenir:

Bu "güvenli" ek türleri, temel platform eklerine bağlı olarak içeriği farklı şekillerde korur:

  • Herhangi bir sistem kullanıcı arayüzünün altına çizilmemesi gereken içeriği korumak için WindowInsets.safeDrawing kullanın. Eklerin en yaygın kullanımı, sistem kullanıcı arayüzü tarafından gizlenen (kısmen veya tamamen) içeriğin çizilmesini önlemek içindir.
  • İçerikleri hareketlerle korumak için WindowInsets.safeGestures özelliğini kullanın. Bu, sistem hareketlerinin uygulama hareketleriyle (alt sayfalar, bantlar veya oyunlardaki hareketler gibi) çakışmasını önler.
  • İçeriğin görsel örtüşmemesi ve hareket çakışması olmaması için WindowInsets.safeContent'i WindowInsets.safeDrawing ve WindowInsets.safeGestures kombinasyonu olarak kullanın.

Insets kurulumu

Uygulamanızın, içeriği nerede çekeceği konusunda tam kontrol sahibi olmak için bu kurulum adımlarını izleyin. Bu adımlar olmadan, uygulamanız sistem kullanıcı arayüzünün arkasına siyah veya düz renkler çizebilir ya da yazılım klavyesi ile eşzamanlı şekilde animasyon uygulamayabilir.

  1. Activity.onCreate içinden enableEdgeToEdge() numaralı telefonu arayın. Bu çağrı, uygulamanızın sistem kullanıcı arayüzünün arkasında görüntülenmesini ister. Böylece uygulamanız, kullanıcı arayüzünü ayarlamak için bu eklerin nasıl kullanılacağını kontrol eder.
  2. Etkinliğinizin AndroidManifest.xml girişinde android:windowSoftInputMode="adjustResize" değerini ayarlayın. Bu ayar, uygulamanızın yazılım IME'sinin boyutunu ek olarak almasına olanak tanır. Bu özelliği, IME göründüğünde ve uygulamanızda kaybolurken içeriği uygun şekilde yerleştirmek ve yerleştirmek için kullanabilirsiniz.

    <!-- in your AndroidManifest.xml file: -->
    <activity
      android:name=".ui.MainActivity"
      android:label="@string/app_name"
      android:windowSoftInputMode="adjustResize"
      android:theme="@style/Theme.MyApplication"
      android:exported="true">
    

API oluşturma

Tüm ek öğelerin işlenmesini Etkinliğiniz kontrol ettiğinde, içeriğin gizlenmediğinden ve etkileşimde bulunulabilir öğelerin sistem kullanıcı arayüzüyle çakışmadığından emin olmak için Compose API'lerini kullanabilirsiniz. Bu API'ler ayrıca uygulamanızın düzenini içerideki değişikliklerle senkronize eder.

Örneğin, tüm uygulamanızın içeriğine ek öğeler uygulamanın en temel yöntemi budur:

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

    enableEdgeToEdge()

    setContent {
        Box(Modifier.safeDrawingPadding()) {
            // the rest of the app
        }
    }
}

Bu snippet, safeDrawing pencere eklerini, uygulamanın tüm içeriğinin etrafına dolgu olarak uygular. Böylece etkileşimde bulunulabilir öğelerin sistem kullanıcı arayüzüyle çakışmaması sağlanmış olsa da hiçbir uygulama, uçtan uca bir etki yaratmak için sistem kullanıcı arayüzünün arkasına çizim yapmaz. Pencerenin tamamından en iyi şekilde yararlanmak için, eklerin uygulanacağı yerleri ekran ve bileşen bazında hassas olarak ayarlamanız gerekir.

Bu ek türlerinin tamamı, API 21'e geri taşınan IME animasyonlarıyla otomatik olarak canlandırılır. Ayrıca, bu iç öğeleri kullanan tüm düzenleriniz, içe aktarılan değerler değiştikçe otomatik olarak canlandırılır.

Composable düzenlerinizi ayarlamak için bu iç küme türlerini kullanmanın iki temel yolu vardır: dolgu değiştiricileri ve ek boyut değiştiricileri.

Dolgu değiştiriciler

Modifier.windowInsetsPadding(windowInsets: WindowInsets), belirtilen pencere eklerini dolgu olarak uygular ve Modifier.padding tarafından yapılan gibi davranır. Örneğin, Modifier.windowInsetsPadding(WindowInsets.safeDrawing), güvenli çizim eklerini 4 kenarın hepsine dolgu olarak uygular.

En yaygın inset türleri için birkaç yerleşik yardımcı program yöntemi de vardır. Modifier.safeDrawingPadding(), Modifier.windowInsetsPadding(WindowInsets.safeDrawing) ile eşdeğer olan bu yöntemlerden biridir. Diğer küme türleri için de benzer değiştiriciler vardır.

Ek boyut değiştiriciler

Aşağıdaki değiştiriciler, bileşenin boyutunu eklerin boyutuna ayarlayarak bir miktar pencere içi öğe uygular:

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

windowInsets başlangıç tarafını genişlik olarak uygular (ör. Modifier.width)

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

windowInsets öğesinin son tarafını genişlik olarak uygular (ör. Modifier.width)

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

Yükseklik olarak windowInsets öğesinin üst tarafını (Modifier.height gibi) uygular

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

Yükseklik olarak windowInsets alt tarafını (Modifier.height gibi) uygular

Bu değiştiriciler, özellikle iç kısımda yer kaplayan bir Spacer boyutunu boyutlandırmak için yararlıdır:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Veri tüketimi

İçe yerleştirilmiş dolgu değiştiriciler (windowInsetsPadding ve safeDrawingPadding gibi yardımcılar), içe aktarılanların dolgu olarak uygulanan bölümünü otomatik olarak tüketir. Kompozisyon ağacında daha derine inerken iç içe yerleştirilmiş iç dolgu değiştiricileri ve iç küme boyut değiştiricileri, iç içe yerleştirilmiş dolgu değiştiricilerin bir kısmının zaten dış içe dolgu değiştiricileri tarafından kullanıldığını bilir ve çok fazla boşluk oluşmasına yol açacak şekilde eklerin aynı kısmını birden fazla kez kullanmaktan kaçınır.

Ek öğeler tüketilmişse ek boyut değiştiricileri, aynı veri parçasını birden fazla kez kullanmaktan da kaçınır. Ancak doğrudan boyutlarını değiştirdikleri için ekleri kendileri tüketmezler.

Bunun sonucunda, iç içe yerleştirme dolgu değiştiricileri her composable'a uygulanan dolgu miktarını otomatik olarak değiştirir.

Önceki LazyColumn örneğine bakıldığında, LazyColumn, imePadding değiştiricisi tarafından yeniden boyutlandırılmaktadır. LazyColumn içindeki son öğe, sistem çubuklarının alt kısmının yüksekliğine eşit olacak şekilde boyutlandırılır:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

IME kapatıldığında, IME'nin yüksekliği olmadığından imePadding() değiştiricisi dolgu uygulamaz. imePadding() değiştiricisi dolgu uygulamadığından hiçbir iç içe geçilmez ve Spacer öğesinin yüksekliği, sistem çubuklarının alt tarafının boyutu olur.

IME açıldığında, IME içinde gösterilen öğeler IME'nin boyutuyla eşleşecek şekilde canlandırılır ve imePadding() değiştiricisi, IME açılırken LazyColumn boyutunu yeniden boyutlandırmak için alt dolgu uygulamaya başlar. imePadding() değiştiricisi alt dolgu uygulamaya başladıkça bu miktarda iç dolgu da kullanmaya başlar. Bu nedenle, imePadding() değiştiricisi tarafından zaten sistem çubukları için uygulanan boşlukların bir parçası olarak Spacer yüksekliği azalmaya başlar. imePadding() değiştiricisi, sistem çubuklarından daha büyük bir alt dolgu miktarı uyguladığında Spacer yüksekliği sıfır olur.

IME kapatıldığında, değişiklikler tersten gerçekleşir: imePadding(), sistem çubuklarının alt tarafından daha az uygulandığında Spacer sıfır yüksekliğinden genişlemeye başlar, son olarak da IME tamamen canlandırıldıktan sonra Spacer, sistem çubuklarının alt tarafının yüksekliğiyle eşleşir.

Şekil 2. TextField ile uçtan uca geç sütun

Bu davranış, tüm windowInsetsPadding değiştiricileri arasındaki iletişimle gerçekleştirilir ve başka birkaç şekilde etkilenebilir.

Modifier.consumeWindowInsets(insets: WindowInsets), iç öğeleri de Modifier.windowInsetsPadding ile aynı şekilde tüketir ancak tüketilen iç öğeleri dolgu olarak uygulamaz. Bu, ek boyut değiştiricileriyle birlikte, kardeşlere belirli miktarda ekin zaten tüketildiğini belirtmek için yararlıdır:

Column(Modifier.verticalScroll(rememberScrollState())) {
    Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars))

    Column(
        Modifier.consumeWindowInsets(
            WindowInsets.systemBars.only(WindowInsetsSides.Vertical)
        )
    ) {
        // content
        Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
    }

    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
}

Modifier.consumeWindowInsets(paddingValues: PaddingValues), WindowInsets bağımsız değişkenine sahip sürüme çok benzer şekilde davranır ancak kullanımı rastgele bir PaddingValues alır. Bu, sıradan bir Modifier.padding veya sabit yükseklikte yer tutucular gibi, içe yerleştirilmiş dolgu değiştiriciler dışındaki bir mekanizma tarafından dolgu veya boşluk sağlandığında onları bilgilendirmek açısından kullanışlıdır:

@OptIn(ExperimentalLayoutApi::class)
Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) {
    // content
    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
}

Tüketim olmadan ham pencere eklerinin gerektiği durumlarda doğrudan WindowInsets değerlerini kullanın veya tüketimden etkilenmeyen eklerin PaddingValues kadarını döndürmek için WindowInsets.asPaddingValues() değerini kullanın. Bununla birlikte, aşağıdaki uyarılar nedeniyle mümkün olduğunda pencere iç öğeleri dolgu değiştiricileri ve pencere boyutu değiştiricileri kullanmayı tercih edin.

Insets ve Jetpack Compose aşamaları

Compose, alt öğeleri güncellemek ve canlandırmak için temel AndroidX temel API'lerini kullanır. Bu API'ler, iç öğeleri yöneten temel platform API'lerini kullanır. Bu platform davranışı nedeniyle, eklerin Jetpack Composer aşamalarıyla özel bir ilişkisi vardır.

Eklerin değeri, bileşim aşamasından sonra ancak düzen aşamasından önce güncellenir. Bu, bileşimdeki eklerin değerinin okunurken genellikle bir kare geciken ek değeri kullanıldığı anlamına gelir. Bu sayfada açıklanan yerleşik değiştiriciler, eklerin değerlerini düzen aşamasına kadar kullanarak gecikme yaşanacak şekilde oluşturulmuştur. Bu, içe eklenen değerlerin güncellendikçe aynı çerçevede kullanılmasını sağlar.

WindowInsets ile klavye IME animasyonları

Kapsayıcının en altına doğru kaydırılırken IME'yi otomatik olarak açıp kapatmak için bir kaydırma kapsayıcıya Modifier.imeNestedScroll() uygulayabilirsiniz.

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

        WindowCompat.setDecorFitsSystemWindows(window, false)

        setContent {
            MaterialTheme {
                MyScreen()
            }
        }
    }
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun MyScreen() {
    Box {
        LazyColumn(
            modifier = Modifier
                .fillMaxSize() // fill the entire window
                .imePadding() // padding for the bottom for the IME
                .imeNestedScroll(), // scroll IME at the bottom
            content = { }
        )
        FloatingActionButton(
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(16.dp) // normal 16dp of padding for FABs
                .navigationBarsPadding() // padding for navigation bar
                .imePadding(), // padding for when IME appears
            onClick = { }
        ) {
            Icon(imageVector = Icons.Filled.Add, contentDescription = "Add")
        }
    }
}

Klavyeye yer açmak için yukarı aşağı kayan bir kullanıcı arayüzü öğesinin gösterildiği animasyon

Şekil 1. IME animasyonları

Material 3 Bileşenleri için dahili destek

Kullanım kolaylığı sağlamak için yerleşik Material 3 composable'ların (androidx.compose.material3) tutamaçlarının birçoğu, composable'ların uygulamanıza Materyal özelliklerine göre nasıl yerleştirildiğine bağlı olarak kendiliğinden yerleşir.

Inset işleme composable'ları

Aşağıda, iç öğeleri otomatik olarak işleyen Malzeme Bileşenlerinin listesi verilmiştir.

Uygulama çubukları

İçerik kapsayıcıları

Yapı İskelesi

Varsayılan olarak Scaffold, kullanmanız ve kullanmanız için paddingValues parametresi olarak inset'ler sağlar. Ekleri Scaffold içeriği üzerinde uygulamaz; bu sorumluluk size aittir. Örneğin, bu iç öğeleri Scaffold içinde bir LazyColumn ile kullanmak için:

Scaffold { innerPadding ->
    // innerPadding contains inset information for you to use and apply
    LazyColumn(
        // consume insets as scaffold doesn't do it by default
        modifier = Modifier.consumeWindowInsets(innerPadding),
        contentPadding = innerPadding
    ) {
        items(count = 100) {
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(50.dp)
                    .background(colors[it % colors.size])
            )
        }
    }
}

Varsayılan iç öğeleri geçersiz kıl

composable'ın davranışını yapılandırmak için composable'a iletilen windowInsets parametresini değiştirebilirsiniz. Bu parametre, bunun yerine uygulanacak farklı bir window inset türü olabilir veya boş bir örnek iletilerek devre dışı bırakılabilir: WindowInsets(0, 0, 0, 0).

Örneğin, LargeTopAppBar üzerinde inset işlemesini devre dışı bırakmak için windowInsets parametresini boş bir örneğe ayarlayın:

LargeTopAppBar(
    windowInsets = WindowInsets(0, 0, 0, 0),
    title = {
        Text("Hi")
    }
)

View sistem setleriyle birlikte çalışma

Ekranınızda aynı hiyerarşide hem Görünümler hem de Oluşturma kodu olduğunda varsayılan ek öğeleri geçersiz kılmanız gerekebilir. Bu durumda, verinin hangi veride kullanıldığını, hangisinin hangi veriyi göz ardı etmesi gerektiğini net olarak belirlemeniz gerekir.

Örneğin, en dıştaki düzeniniz bir Android Görünümü düzeniyse ek öğeleri Görünüm sisteminde kullanmalı ve Oluştur için yoksaymalısınız. Alternatif olarak, en dıştaki düzeniniz composable ise Compose'da içe aktarılan öğeleri kullanıp AndroidView composable'ları buna uygun şekilde doldurmanız gerekir.

Varsayılan olarak her ComposeView, WindowInsetsCompat tüketim düzeyindeki tüm ekleri tüketir. Bu varsayılan davranışı değiştirmek için ComposeView.consumeWindowInsets değerini false olarak ayarlayın.

Kaynaklar

  • Artık Android'de. Tamamen Kotlin ve Jetpack Compose ile geliştirilmiş, tamamen işlevsel bir Android uygulaması.