Compose'da pencere ekleri

Android platformu, durum çubuğu ve gezinme çubuğu gibi sistem kullanıcı arayüzünün çizilmesinden sorumludur. Bu sistem kullanıcı arayüzü, kullanıcının hangi uygulamayı kullandığına bakılmaksızın gösterilir. WindowInsets, uygulamanızın doğru alanı çizmesini ve kullanıcı arayüzünüzün sistem kullanıcı arayüzü tarafından gizlenmemesini sağlamak için sistem kullanıcı arayüzü hakkında bilgi sağlar.

Sistem çubuklarının arkasından çizim yapmak için baştan sona gitme
Şekil 1. Sistem çubuklarının arkasından çizim yapmak için baştan sona gitme

Varsayılan olarak, uygulamanızın kullanıcı arayüzü, durum çubuğu ve gezinme çubuğu gibi sistem kullanıcı arayüzünde gösterilmekle sınırlıdır. Böylece uygulama içeriğinizin sistem kullanıcı arayüzü öğeleri tarafından gizlenmemesi sağlanır.

Bununla birlikte, uygulamaların, sistem kullanıcı arayüzünün de görüntülendiği bu alanlarda gösterilmesini etkinleştirmesini öneririz. Bu, daha sorunsuz bir kullanıcı deneyimi sağlar ve uygulamanızın, mevcut pencere alanından tam olarak yararlanmasına olanak tanır. Bu, uygulamaların, özellikle yazılım klavyesini gösterip gizlerken sistem kullanıcı arayüzüyle birlikte canlandırmasına da olanak tanır.

Bu bölgelerde görüntülemeyi etkinleştirme ve sistem kullanıcı arayüzünün arkasında içerik görüntülemeyi etkinleştirme, uçtan uca geçiş olarak adlandırılır. Bu sayfada farklı ek türleri, uçtan uca geçişi nasıl etkinleştireceğiniz ve kullanıcı arayüzünüzü canlandırmak ve uygulamanızın bazı bölümlerinin anlaşılmaz hale getirmekten kaçınmak için Inset API'lerini nasıl kullanacağınız hakkında bilgi edineceksiniz.

Ek temel bilgiler

Uygulamanız uçtan uca ilerlediğinde, önemli içerik ve etkileşimlerin sistem kullanıcı arayüzü tarafından gizlenmediğ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 nereye yerleştirildiğiyle ilgili bilgiler ek birimleriyle belirtilir.

Sistem kullanıcı arayüzünün her bir bölümü, boyutunu ve yerleştirildiği yeri açıklayan karşılık gelen bir ek türüne sahiptir. Örneğin, durum çubuğu ek öğeleri durum çubuğunun boyutunu ve konumunu belirtirken, gezinme çubuğu ek öğeleri gezinme çubuğunun boyutunu ve konumunu belirtir. Her 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 taraflarından ne kadar uzağa uzandığını belirtir. Bu nedenle, bu tür sistem kullanıcı arayüzüyle çakışmayı önlemek için uygulama kullanıcı arayüzünün bu tutarla girilmesi gerekir.

Aşağıdaki yerleşik Android ek türleri WindowInsets aracılığıyla kullanılabilir:

WindowInsets.statusBars

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

WindowInsets.statusBarsIgnoringVisibility

Görünür oldukları zamanları durum çubuğu ekler. Durum çubukları şu anda gizliyse (kapsamlı tam ekran moduna geçilmesi nedeniyle) ana durum çubuğu ekleri boş olur ancak bu ekler 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 bulunmasına bağlı olarak çalışma zamanında değişebilir.

WindowInsets.navigationBarsIgnoringVisibility

Görünür oldukları zamanlara ilişkin gezinme çubuğu ekler. Gezinme çubukları şu anda gizliyse (kapsamlı tam ekran moduna geçilmesi nedeniyle) ana gezinme çubuğu ekleri boş olur, ancak bu ekler boş olmaz.

WindowInsets.captionBar

Üst başlık çubuğu gibi serbest biçimli bir pencerede sistem kullanıcı arayüzü pencere süslemesini açıklayan ek.

WindowInsets.captionBarIgnoringVisibility

Altyazı çubuğu, ne zaman görünür olacağına eklenir. Altyazı çubukları şu anda gizliyse ana altyazı çubuğu ek kısımları boş olur ancak bu ek kümeler boş olmaz.

WindowInsets.systemBars

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

WindowInsets.systemBarsIgnoringVisibility

Görünür oldukları zamanlara ilişkin sistem çubuğu ekler. Sistem çubukları şu anda gizliyse (kapsamlı tam ekran moduna geçilmesi nedeniyle) ana sistem çubuğu ekleri boş olur, ancak bu ekler boş olmaz.

WindowInsets.ime

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

WindowInsets.imeAnimationSource

Yazılım klavyesinin, geçerli klavye animasyonundan önce kapladığı alan miktarını açıklayan ekler.

WindowInsets.imeAnimationTarget

Yazılım klavyesinin, geçerli klavye animasyonundan sonra kaplayacağı alan miktarını açıklayan ek bilgiler.

WindowInsets.tappableElement

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

WindowInsets.tappableElementIgnoringVisibility

Dokunulabilir öğe, görünür oldukları zamanlara eklenir. Dokunulabilir öğeler şu anda gizliyse (etkileyici tam ekran moduna girmesi nedeniyle) dokunulabilir ana öğe ek ayarları boş olur ancak bu ek öğeler boş olmaz.

WindowInsets.systemGestures

Sistemin gezinme hareketlerine müdahale edeceği eklerin miktarını temsil eden ekler. Uygulamalar, Modifier.systemGestureExclusion aracılığıyla bu hareketlerin sınırlı bir kısmının işlenmesini manuel olarak belirtebilir.

WindowInsets.mandatorySystemGestures

Sistem tarafından her zaman işlenecek ve Modifier.systemGestureExclusion üzerinden devre dışı bırakılamayan sistem hareketlerinin bir alt kümesi.

WindowInsets.displayCutout

Ekran kesimi (çentik veya iğne deliği) ile çakışmayı önlemek için gereken boşluk miktarını temsil eden ek değerler.

WindowInsets.waterfall

Şelale görüntüsünün kavisli alanlarını temsil eden ek kısımlar. Şelale görüntüsünde, ekranın kenarlarında cihazın kenarları boyunca kaymaya başladığı kavisli alanlar bulunuyor.

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

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

  • Hiç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 şekilde kullanıldığı durum, sistem kullanıcı arayüzü tarafından (kısmen veya tamamen) gizlenen çizim içeriğinin önlenmesidir.
  • Hareketlerle içerikleri korumak için WindowInsets.safeGestures özelliğini kullanın. Bu, sistem hareketlerinin uygulama hareketleriyle (ör. alt sayfalar, bantlar veya oyunlardakiler) çakışmasını önler.
  • İçeriğin görsel çakışma ve hareket çakışması olmaması için WindowInsets.safeDrawing ve WindowInsets.safeGestures ile birlikte WindowInsets.safeContent kullanın.

Insets kurulumu

Uygulamanızın içerik çektiği yerler üzerinde tam kontrol sahibi olmak için bu kurulum adımlarını izleyin. Bu adımlar kullanılmadığında, uygulamanız sistem kullanıcı arayüzünün arkasına siyah veya düz renkler çizebilir ya da yazılım klavyesiyle eşzamanlı olarak animasyon uygulamayabilir.

  1. Activity.onCreate bölgesinde enableEdgeToEdge() adlı iş ortağını arayın. Bu çağrı, uygulamanızın sistem kullanıcı arayüzünün arkasında görüntülenmesini ister. Daha sonra uygulamanız, bu eklerin kullanıcı arayüzünü ayarlamak için 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'nin boyutunu ek olarak almasına olanak tanır. IME'nin uygulamanızda görünüp kaybolması durumunda içerikleri uygun şekilde doldurmak ve yerleştirmek için bu boyutu 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

Etkinliğiniz tüm eklerin işlenmesini kontrol ettiğinde, içeriğin bulanıklaştırılmadığından ve etkileşimli öğelerin sistem kullanıcı arayüzüyle çakışmadığından emin olmak için Compose API'lerini kullanabilirsiniz. Bu API'ler, uygulamanızın düzenini gelen değişikliklerle senkronize eder.

Örneğin, eklemeleri uygulamanızın tamamının içeriğine 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, uygulama içeriğinin tamamının etrafına dolgu olarak safeDrawing pencere içerilerini uygular. Bu şekilde, etkileşimde bulunulabilir öğelerin sistem kullanıcı arayüzüyle çakışmaması sağlanır. Bununla birlikte, hiçbir uygulama, uçtan uca efekt elde etmek için sistem kullanıcı arayüzünün arkasına geçmez. Pencerenin tamamından tam olarak yararlanmak için eklerin uygulandığı yerlerde ekran veya bileşen bazında ince ayar yapmanız gerekir.

Bu iç içe aktarma türlerinin hepsi, API 21'e geri taşınan IME animasyonlarıyla otomatik olarak canlandırılır. Uzantıyla, bu içe aktarmaları kullanan tüm düzenleriniz de inset değerleri değiştikçe otomatik olarak canlandırılır.

Oluşturulabilir düzenlerinizi ayarlamak için bu iç içe aktarma türlerini kullanmanın iki temel yolu vardır: dolgu değiştiricileri ve iç içe yerleştirilmiş boyut değiştiricileri.

Dolgu değiştiriciler

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

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

Inset boyut değiştiricileri

Aşağıdaki değiştiriciler, bileşenin boyutunu iç kümelerin boyutu olacak şekilde ayarlayarak pencere içe aktarma miktarını uygular:

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

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

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

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

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

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

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

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

Bu değiştiriciler, özellikle ek öğeler alanını kaplayan bir Spacer boyutunu boyutlandırmak için yararlıdır:

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

Inset tüketimi

Inset dolgu değiştiricileri (windowInsetsPadding ve safeDrawingPadding gibi yardımcılar), eklerin dolgu olarak uygulanan bölümünü otomatik olarak tüketir. Kompozisyon ağacında daha derinlere inerken, iç içe yerleştirilmiş dolgu değiştiricileri ve inset boyut değiştiricileri, eklerin bir kısmının dış içe doğru dolgu değiştiricileri tarafından zaten kullanıldığını bilir. Bu nedenle, iç içe eklenmiş öğelerin aynı kısmını bir defadan fazla kullanmaktan kaçının, bu da çok fazla ekstra alana yol açar.

Inset boyutu değiştiricileri, ek öğeler önceden kullanıldıysa aynı alt öğe bölümünü bir kereden fazla kullanmaktan da kaçınır. Ancak, boyutlarını doğrudan değiştirdikleri için ekleri kendilerinin tüketmezler.

Sonuç olarak, iç içe dolgu değiştiricileri her bir composable'a uygulanan dolgu miktarını otomatik olarak değiştirir.

Önceki LazyColumn örneğine baktığımızda LazyColumn, imePadding değiştiricisi tarafından yeniden boyutlandırılmaktadır. LazyColumn içindeki son öğenin boyutu, sistem çubuklarının alt yüksekliği kadardır:

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

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

IME açıldığında, IME'nin içe aktarması, IME'nin boyutuna uyacak şekilde canlandırılır ve imePadding() değiştiricisi, IME açılırken LazyColumn öğesini yeniden boyutlandırmak için alt dolguyu uygulamaya başlar. imePadding() değiştiricisi alt dolgu uygulamaya başladığında, bu miktarı da tüketmeye başlar. Dolayısıyla, imePadding() değiştiricisi sistem çubuklarına yönelik boşluğun bir parçası olarak Spacer yüksekliği düşmeye 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 ters yönde gerçekleşir: imePadding(), sistem çubuklarının alt tarafından daha az uygulandığında Spacer yüksekliği sıfır yükseklikten genişletilmeye başlar. Sonunda, IME animasyonu tamamen çıkarıldıktan sonra Spacer, sistem çubuklarının alt tarafının yüksekliğiyle eşleşir.

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

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

Modifier.consumeWindowInsets(insets: WindowInsets), içe aktarmaları Modifier.windowInsetsPadding ile aynı şekilde tüketir ancak tüketilen ekleri dolgu olarak uygulamaz. Bu, belirli miktarda ekin zaten tüketildiğini kardeşlere belirtmek için boyut değiştiricilerle birlikte kullanıldığında faydalı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 davranır ancak tüketilmesi için rastgele bir PaddingValues gerekir. Bu, dolgu veya boşluk normal bir Modifier.padding ya da sabit yükseklik aralayıcıları gibi içerideki dolgu değiştiricilerinden farklı bir mekanizma tarafından sağlandığında çocukları bilgilendirmek açısından yararlıdır:

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

Tüketim yapılmadan ham pencere eklerinin gerektiği durumlarda doğrudan WindowInsets değerlerini kullanın veya tüketimden etkilenmeyen PaddingValues değerini döndürmek için WindowInsets.asPaddingValues() kullanın. Ancak, aşağıdaki uyarılardan dolayı, mümkün olduğunda pencere iç içe aktarma değiştiricilerini ve pencere iç içe aktarma boyut değiştiricilerini kullanmayı tercih edin.

Insets ve Jetpack Compose aşamaları

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

Eklerin değeri beste aşamasından sonra, ancak düzen aşamasından önce güncellenir. Bu, bileşimdeki iç kümelerin değerinin okunmasında genellikle bir kare gecikmeli ek değerlerin kullanıldığı anlamına gelir. Bu sayfada açıklanan yerleşik değiştiriciler, eklerin değerlerini düzen aşamasına kadar geciktirecek şekilde oluşturulmuştur. Bu da, eklenen değerlerin güncellendikleri zaman aynı çerçevede kullanılmasını sağlar.

WindowInsets içeren klavye IME animasyonları

Kapsayıcının alt kısmına kaydırılırken IME'yi otomatik olarak açıp kapatmak için kaydırma kapsayıcısına 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")
        }
    }
}

Klavye yer açmak için yukarı ve aşağı kaydırılan kullanıcı arayüzü öğesini gösteren animasyon

Şekil 1. IME animasyonları

Material 3 Bileşenleri için inset desteği

Kullanım kolaylığı açısından, yerleşik Material 3 composable'ların (androidx.compose.material3) birçoğu, composable'ların uygulamanıza Material özelliklerine göre nasıl yerleştirildiklerine bağlı olarak eklenerek yerleştiriliyor.

Inset işleme composable'ları

Aşağıda, inset'leri otomatik olarak işleyen Malzeme Bileşenlerinin listesi yer almaktadır.

Uygulama çubukları

İçerik kapsayıcıları

İskele

Varsayılan olarak Scaffold, tüketmeniz ve kullanmanız için paddingValues parametresi olarak inset'ler sağlar. Scaffold, ek hükümleri içeriğe uygulamaz. Bu sorumluluk size aittir. Örneğin, bu ek öğ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 ek değerlerini 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 pencere iç kümesi türü olabilir veya boş bir örnek iletilerek devre dışı bırakılabilir: WindowInsets(0, 0, 0, 0).

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

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

Görüntüleme sistemi ekleriyle birlikte çalışma

Ekranınızda aynı hiyerarşide hem Görünümler hem de Oluştur kodu varsa varsayılan ekleri geçersiz kılmanız gerekebilir. Bu durumda, hangi tarafın ekleri kullanması gerektiğini ve hangisinin bunları göz ardı etmesi gerektiğini açıkça belirtmeniz gerekir.

Örneğin, en dıştaki düzeniniz Android View düzeniyse Görünüm sistemindeki ek öğeleri kullanmalı ve Oluşturma için bunları yoksaymalısınız. Alternatif olarak, en dıştaki düzeniniz bir composable ise Compose'da ek öğeleri kullanmalı ve AndroidView composable'ı buna uygun şekilde doldurmalısınız.

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

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