Kaydır

Kaydırma değiştiricileri

verticalScroll ve horizontalScroll değiştiricileri, içeriğinin sınırları maksimum boyut kısıtlamalarından daha büyük olduğunda kullanıcının bir öğeyi kaydırmasına izin vermenin en basit yolunu sağlar. verticalScroll ve horizontalScroll değiştiricileriyle içerikleri çevirmeniz veya kaydırmanız gerekmez.

@Composable
private fun ScrollBoxes() {
    Column(
        modifier = Modifier
            .background(Color.LightGray)
            .size(100.dp)
            .verticalScroll(rememberScrollState())
    ) {
        repeat(10) {
            Text("Item $it", modifier = Modifier.padding(2.dp))
        }
    }
}

Kaydırma hareketlerine yanıt veren basit bir dikey liste

ScrollState, kaydırma konumunu değiştirmenize veya mevcut durumunu öğrenmenize olanak tanır. Varsayılan parametrelerle oluşturmak için rememberScrollState() değerini kullanın.

@Composable
private fun ScrollBoxesSmooth() {
    // Smoothly scroll 100px on first composition
    val state = rememberScrollState()
    LaunchedEffect(Unit) { state.animateScrollTo(100) }

    Column(
        modifier = Modifier
            .background(Color.LightGray)
            .size(100.dp)
            .padding(horizontal = 8.dp)
            .verticalScroll(state)
    ) {
        repeat(10) {
            Text("Item $it", modifier = Modifier.padding(2.dp))
        }
    }
}

Kaydırılabilir değiştirici

scrollable değiştirici, kaydırma hareketlerini algılayıp deltaları yakalamasına rağmen içeriğini otomatik olarak kaydırmadığından kaydırma değiştiricilerden farklıdır.scrollable Bunun yerine, bu değiştiricinin doğru çalışması için gereken ScrollableState aracılığıyla kullanıcıya devredilir.

ScrollableState işlevini oluştururken, her kaydırma adımında (jest girişi, yumuşak kaydırma veya fırlatma ile) piksel cinsinden delta değeriyle çağrılacak bir consumeScrollDelta işlevi sağlamanız gerekir. Bu işlev, scrollable değiştiricisine sahip iç içe yerleştirilmiş öğeler olduğunda etkinliğin düzgün şekilde dağıtılmasını sağlamak için kullanılan kaydırma mesafesini döndürmelidir.

Aşağıdaki snippet, hareketleri algılar ve bir ofset için sayısal bir değer gösterir ancak hiçbir öğeyi kaydırmaz:

@Composable
private fun ScrollableSample() {
    // actual composable state
    var offset by remember { mutableStateOf(0f) }
    Box(
        Modifier
            .size(150.dp)
            .scrollable(
                orientation = Orientation.Vertical,
                // Scrollable state: describes how to consume
                // scrolling delta and update offset
                state = rememberScrollableState { delta ->
                    offset += delta
                    delta
                }
            )
            .background(Color.LightGray),
        contentAlignment = Alignment.Center
    ) {
        Text(offset.toString())
    }
}

Parmak basışını algılayan ve parmağın konumunun sayısal değerini gösteren bir kullanıcı arayüzü öğesi

İç içe yerleştirilmiş kaydırma

İç içe kaydırma, birbirinin içinde bulunan birden fazla kaydırma bileşeninin tek bir kaydırma hareketine tepki vererek ve kaydırma deltalarını (değişiklikleri) iletişime geçirerek birlikte çalıştığı bir sistemdir.

İç içe yerleştirilmiş kaydırma sistemi, kaydırılabilir ve hiyerarşik olarak bağlı bileşenler arasında koordinasyon sağlar (genellikle aynı ebeveyni paylaşarak). Bu sistem, kaydırılabilir kapsayıcıları birbirine bağlar ve aralarında dağıtılan ve paylaşılan kaydırılabilir deltalarla etkileşime izin verir.

Oluşturma, bileşenler arasında iç içe yerleştirilmiş kaydırma işlemini yönetmenin birden fazla yolunu sunar. İç içe yerleştirilmiş kaydırma için tipik bir örnek, başka bir listenin içindeki listedir. Daha karmaşık bir örnek ise daraltılabilir araç çubuğudur.

Otomatik iç içe kaydırma

Basit iç içe kaydırma için herhangi bir işlem yapmanız gerekmez. Kaydırma işlemi başlatan hareketler, alt öğelerden üst öğelere otomatik olarak iletilir. Böylece, alt öğe daha fazla kaydıramadığında hareket, üst öğesi tarafından işlenir.

Otomatik iç içe kaydırma, Compose'un bazı bileşenleri ve değiştiricileri tarafından desteklenir ve kullanıma hazır olarak sunulur: verticalScroll, horizontalScroll, scrollable, Lazy API'leri ve TextField. Yani kullanıcı, iç içe yerleştirilmiş bileşenlerin içteki bir çocuğunu kaydırdığında önceki değiştiriciler, kaydırma deltalarını iç içe yerleştirilmiş kaydırma desteğine sahip ebeveynlere iletir.

Aşağıdaki örnekte, verticalScroll değiştiricisi de uygulanmış bir kapsayıcı içinde verticalScroll değiştiricisi uygulanmış öğeler gösterilmektedir.

@Composable
private fun AutomaticNestedScroll() {
    val gradient = Brush.verticalGradient(0f to Color.Gray, 1000f to Color.White)
    Box(
        modifier = Modifier
            .background(Color.LightGray)
            .verticalScroll(rememberScrollState())
            .padding(32.dp)
    ) {
        Column {
            repeat(6) {
                Box(
                    modifier = Modifier
                        .height(128.dp)
                        .verticalScroll(rememberScrollState())
                ) {
                    Text(
                        "Scroll here",
                        modifier = Modifier
                            .border(12.dp, Color.DarkGray)
                            .background(brush = gradient)
                            .padding(24.dp)
                            .height(150.dp)
                    )
                }
            }
        }
    }
}

İç öğenin içinde ve dışındaki hareketlere yanıt veren iki iç içe yerleştirilmiş dikey kaydırmalı kullanıcı arayüzü öğesi

nestedScroll değiştiricisini kullanma

Birden fazla öğe arasında gelişmiş koordineli bir kaydırma oluşturmanız gerekiyorsa nestedScroll değiştirici, iç içe yerleştirilmiş bir kaydırma hiyerarşisi tanımlayarak size daha fazla esneklik sunar. Önceki bölümde belirtildiği gibi, bazı bileşenlerde yerleşik iç içe yerleştirilmiş kaydırma desteği bulunur. Ancak Box veya Column gibi otomatik olarak kaydırılabilir olmayan bileşenlerde, bu tür bileşenlerdeki kaydırma deltaları iç içe yerleştirilmiş kaydırma sisteminde yayılmaz ve deltalar NestedScrollConnection'ye veya üst bileşene ulaşmaz. Bu sorunu çözmek için özel bileşenler de dahil olmak üzere diğer bileşenlere bu tür bir destek vermek üzere nestedScroll öğesini kullanabilirsiniz.

İç içe yerleştirilmiş kaydırma döngüsü

İç içe yerleştirilmiş kaydırma döngüsü, iç içe yerleştirilmiş kaydırma sisteminin parçası olan tüm bileşenler (veya düğümler) aracılığıyla hiyerarşi ağacında yukarı ve aşağı gönderilen kaydırma deltalarının akışıdır (ör. kaydırılabilir bileşenler ve değiştiriciler veya nestedScroll kullanılarak).

İç içe yerleştirilmiş kaydırma döngüsünün aşamaları

Kaydırılabilir bir bileşen tarafından bir tetikleyici etkinlik (ör. hareket) algılandığında, gerçek kaydırma işlemi tetiklenmeden önce oluşturulan deltalar iç içe yerleştirilmiş kaydırma sistemine gönderilir ve üç aşamadan geçer: kaydırma öncesi, düğüm tüketimi ve kaydırma sonrası.

İç içe yerleştirilmiş kaydırma döngüsünün aşamaları

İlk kaydırma öncesi aşamada, tetikleyici etkinlik deltalarını alan bileşen bu etkinlikleri hiyerarşi ağacı üzerinden en üstteki üst öğeye gönderir. Ardından delta etkinlikleri aşağı doğru yayılır. Yani deltalar, en kökteki üst öğeden iç içe kaydırma döngüsünü başlatan alt öğeye doğru yayılır.

Kaydırma öncesi aşama - dağıtım

Bu, iç içe yerleştirilmiş kaydırma ebeveynlerine (nestedScroll veya kaydırılabilir değiştiriciler kullanan kompozisyonlar), düğümün kendisi tarafından tüketilmeden önce delta ile bir şeyler yapma fırsatı verir.

Kaydırma öncesi aşama - aşağı doğru kabarcıklanma

Düğüm tüketimi aşamasında, düğümün kendisi, üst öğeleri tarafından kullanılmayan tüm deltayı kullanır. Bu, kaydırma hareketinin gerçekten yapıldığı ve görünür olduğu zamandır.

Düğüm tüketim aşaması

Bu aşamada çocuk, kalan kaydırma alanının tamamını veya bir kısmını tüketmeyi seçebilir. Kalanlar, kaydırma sonrası aşamadan geçmek için tekrar yukarı gönderilir.

Son olarak, kaydırma sonrası aşamada, düğümün kendisinin tüketmediği her şey, tüketilmeleri için atalarına tekrar gönderilir.

Kaydırma sonrası aşama - dağıtım

Kaydırma sonrası aşama, ebeveynlerin reklamı görüntülemeyi seçip seçemeyeceğini belirleyebildiği kaydırma öncesi aşamaya benzer şekilde çalışır.

Kaydırma sonrası aşama - aşağı doğru kabarcıklanma

Kaydırma işlemine benzer şekilde, bir sürükleme hareketi sona erdiğinde kullanıcının amacı, kaydırılabilir kapsayıcıyı fırlatmak (animasyon kullanarak kaydırma yapmak) için kullanılan bir hıza dönüştürülebilir. Kaydırma da iç içe yerleştirilmiş kaydırma döngüsünün bir parçasıdır ve sürükleme etkinliği tarafından oluşturulan hızlar benzer aşamalardan geçer: fırlatma öncesi, düğüm tüketimi ve fırlatma sonrası. Fling animasyonunun yalnızca dokunma hareketiyle ilişkili olduğunu ve a11y veya donanım kaydırma gibi diğer etkinlikler tarafından tetiklenmeyeceğini unutmayın.

İç içe yerleştirilmiş kaydırma döngüsüne katılma

Döngüye katılım, hiyerarşi boyunca delta tüketimini durdurma, tüketme ve raporlama anlamına gelir. Oluşturma, iç içe yerleştirilmiş kaydırma sisteminin nasıl çalıştığını ve doğrudan onunla nasıl etkileşime geçileceğini etkileyen bir araç grubu sağlar. Örneğin, kaydırılabilir bir bileşen kaydırmaya başlamadan önce kaydırma deltalarıyla bir işlem yapmanız gerektiğinde bu araçlardan yararlanabilirsiniz.

İç içe yerleştirilmiş kaydırma döngüsü bir düğüm zincirinde çalışan bir sistemse nestedScroll değiştirici, bu değişiklikleri durdurup bunlara müdahale etmenin ve zincirde dağıtılan verileri (kaydırma deltaları) etkilemenin bir yoludur. Bu değiştirici, hiyerarşinin herhangi bir yerine yerleştirilebilir ve bu kanal üzerinden bilgi paylaşabilmek için ağaçtaki iç içe yerleştirilmiş kaydırma değiştirici örnekleriyle iletişim kurar. Bu değiştiricinin yapı taşları NestedScrollConnection ve NestedScrollDispatcher'dır.

NestedScrollConnection, iç içe yerleştirilmiş kaydırma döngüsünün aşamalarına yanıt verme ve iç içe yerleştirilmiş kaydırma sistemini etkileme yöntemi sağlar. Her biri tüketim aşamalarından birini temsil eden dört geri çağırma yönteminden oluşur: kaydırma öncesi/sonrası ve fırlatma öncesi/sonrası:

val nestedScrollConnection = object : NestedScrollConnection {
    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
        println("Received onPreScroll callback.")
        return Offset.Zero
    }

    override fun onPostScroll(
        consumed: Offset,
        available: Offset,
        source: NestedScrollSource
    ): Offset {
        println("Received onPostScroll callback.")
        return Offset.Zero
    }
}

Her geri çağırma, dağıtılan delta hakkında da bilgi verir: Söz konusu aşama için available delta ve önceki aşamalarda tüketilen consumed delta. Herhangi bir noktada deltaların hiyerarşide yayılmasını durdurmak isterseniz bunu yapmak için iç içe yerleştirilmiş kaydırma bağlantısını kullanabilirsiniz:

val disabledNestedScrollConnection = remember {
    object : NestedScrollConnection {
        override fun onPostScroll(
            consumed: Offset,
            available: Offset,
            source: NestedScrollSource
        ): Offset {
            return if (source == NestedScrollSource.SideEffect) {
                available
            } else {
                Offset.Zero
            }
        }
    }
}

Tüm geri aramalar NestedScrollSource türü hakkında bilgi sağlar.

NestedScrollDispatcher, iç içe yerleştirilmiş kaydırma döngüsünü başlatır. Bir dağıtıcıyı kullanmak ve dağıtıcının yöntemlerini çağırmak döngüyü tetikler. Kaydırılabilir kapsayıcılar, hareketler sırasında yakalanan deltaları sisteme gönderen yerleşik bir dağıtıcıya sahiptir. Bu nedenle, iç içe yerleştirilmiş kaydırma özelliğini özelleştirmenin çoğu kullanım alanında, yeni deltalar göndermek yerine mevcut deltalara tepki vermek için dağıtıcı yerine NestedScrollConnection kullanılır. Daha fazla kullanım alanı için NestedScrollDispatcherSample bölümüne bakın.

Kaydırma sırasında resmi yeniden boyutlandırma

Kullanıcı sayfayı kaydırırken resmin kaydırma konumuna göre boyutunun değiştiği dinamik bir görsel efekt oluşturabilirsiniz.

Resmi kaydırma konumuna göre yeniden boyutlandırma

Bu snippet'te, dikey kaydırma konumuna göre bir LazyColumn içindeki resmin yeniden boyutlandırılması gösterilmektedir. Kullanıcı aşağı kaydırdığında resim küçülür, yukarı kaydırdığında ise büyür. Bu sırada resim, tanımlanan minimum ve maksimum boyut sınırları içinde kalır:

@Composable
fun ImageResizeOnScrollExample(
    modifier: Modifier = Modifier,
    maxImageSize: Dp = 300.dp,
    minImageSize: Dp = 100.dp
) {
    var currentImageSize by remember { mutableStateOf(maxImageSize) }
    var imageScale by remember { mutableFloatStateOf(1f) }

    val nestedScrollConnection = remember {
        object : NestedScrollConnection {
            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                // Calculate the change in image size based on scroll delta
                val delta = available.y
                val newImageSize = currentImageSize + delta.dp
                val previousImageSize = currentImageSize

                // Constrain the image size within the allowed bounds
                currentImageSize = newImageSize.coerceIn(minImageSize, maxImageSize)
                val consumed = currentImageSize - previousImageSize

                // Calculate the scale for the image
                imageScale = currentImageSize / maxImageSize

                // Return the consumed scroll amount
                return Offset(0f, consumed.value)
            }
        }
    }

    Box(Modifier.nestedScroll(nestedScrollConnection)) {
        LazyColumn(
            Modifier
                .fillMaxWidth()
                .padding(15.dp)
                .offset {
                    IntOffset(0, currentImageSize.roundToPx())
                }
        ) {
            // Placeholder list items
            items(100, key = { it }) {
                Text(
                    text = "Item: $it",
                    style = MaterialTheme.typography.bodyLarge
                )
            }
        }

        Image(
            painter = ColorPainter(Color.Red),
            contentDescription = "Red color image",
            Modifier
                .size(maxImageSize)
                .align(Alignment.TopCenter)
                .graphicsLayer {
                    scaleX = imageScale
                    scaleY = imageScale
                    // Center the image vertically as it scales
                    translationY = -(maxImageSize.toPx() - currentImageSize.toPx()) / 2f
                }
        )
    }
}

Kodla ilgili önemli noktalar

  • Bu kod, kaydırma etkinliklerini durdurmak için bir NestedScrollConnection kullanır.
  • onPreScroll, kaydırma farkına göre görüntü boyutundaki değişikliği hesaplar.
  • currentImageSize durum değişkeni, resmin mevcut boyutunu depolar. Bu boyut, minImageSize ile maxImageSize. imageScale arasında kısıtlanır ve currentImageSize'ten türetilir.
  • LazyColumn, currentImageSize'a göre ofsetlenir.
  • Image, hesaplanan ölçeği uygulamak için bir graphicsLayer değiştirici kullanır.
  • graphicsLayer içindeki translationY, ölçeklendirilirken resmin dikey olarak ortada kalmasını sağlar.

Sonuç

Önceki snippet, kaydırma sırasında ölçeklendirilen resim efekti oluşturur:

Şekil 1. Kaydırma sırasında ölçeklendirilen resim efekti.

İç içe kaydırma birlikte çalışabilirliği

Kaydırılabilir View öğelerini kaydırılabilir bileşenlere veya tam tersini yerleştirmeye çalışırken sorunla karşılaşabilirsiniz. En belirgin olanlar, çocuğu kaydırarak başlangıç veya bitiş sınırlarına ulaştığınızda ve ebeveynin kaydırmayı devralmasını beklediğinizde ortaya çıkar. Ancak bu beklenen davranış gerçekleşmeyebilir veya beklendiği gibi çalışmayabilir.

Bu sorun, kaydırılabilir bileşenlere yerleştirilen beklentilerden kaynaklanır. Kaydırılabilir bileşenler "varsayılan olarak iç içe yerleştirilmiş kaydırma" kuralına sahiptir. Bu, tüm kaydırılabilir kapsayıcıların hem NestedScrollConnection aracılığıyla üst öğe hem de NestedScrollDispatcher aracılığıyla alt öğe olarak iç içe yerleştirilmiş kaydırma zincirine katılması gerektiği anlamına gelir. Alt öğe, sınırda olduğunda üst öğe için iç içe yerleştirilmiş bir kaydırma işlemi gerçekleştirir. Örneğin, bu kural Pager Oluştur ve LazyRow Oluştur'un birlikte iyi çalışmasına olanak tanır. Ancak ViewPager2 veya RecyclerView ile birlikte birlikte çalışabilirlik kaydırma işlemi yapılırken, NestedScrollingParent3 uygulanmadığından alt öğeden üst öğeye sürekli kaydırma yapılamaz.

Kaydırılabilir View öğeleri ile kaydırılabilir bileşenler arasında iç içe yerleştirilmiş kaydırma birlikte çalışabilirlik API'sini etkinleştirmek için aşağıdaki senaryolarda bu sorunları azaltmak amacıyla iç içe yerleştirilmiş kaydırma birlikte çalışabilirlik API'sini kullanabilirsiniz.

ComposeView adlı bir çocuğu olan View adlı işbirliği yapan bir ebeveyn

İşbirliği yapan bir üst öğe View, halihazırda NestedScrollingParent3 uygulayan ve bu nedenle işbirliği yapan iç içe yerleştirilmiş bir alt öğe bileşiğinden kaydırma deltaları alabilen bir öğedir. ComposeView bu durumda çocuk gibi davranır ve NestedScrollingChild3'i (dolaylı olarak) uygulaması gerekir. androidx.coordinatorlayout.widget.CoordinatorLayout, işbirliği yapan bir ebeveyn örneğidir.

Kaydırılabilir View üst kapsayıcılar ile iç içe yerleştirilmiş kaydırılabilir alt bileşenler arasında iç içe yerleştirilmiş kaydırma birlikte çalışabilirliğine ihtiyacınız varsa rememberNestedScrollInteropConnection()'i kullanabilirsiniz.

rememberNestedScrollInteropConnection(), NestedScrollingParent3 uygulayan bir View üst öğesi ile bir Oluştur alt öğesi arasında iç içe yerleştirilmiş kaydırma birlikte çalışabilirliğini sağlayan NestedScrollConnection değerini sağlar ve hatırlar. Bu, nestedScroll değiştiricisiyle birlikte kullanılmalıdır. İç içe yerleştirilmiş kaydırma, Oluşturma tarafında varsayılan olarak etkinleştirildiğinden bu bağlantıyı kullanarak hem View tarafında iç içe yerleştirilmiş kaydırmayı etkinleştirebilir hem de Views ile bileşenler arasında gerekli bağlantı mantığını ekleyebilirsiniz.

Sık kullanılan bir kullanım alanı, CoordinatorLayout, CollapsingToolbarLayout ve bir alt öğe bileşimini kullanmaktır. Bu örnekte bu kullanım alanı gösterilmektedir:

<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:fitsSystemWindows="true">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <!--...-->

        </com.google.android.material.appbar.CollapsingToolbarLayout>

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

Etkinliğinizde veya Fragment'inizde, alt bileşeninizi ve gerekli NestedScrollConnection öğelerini ayarlamanız gerekir:

open class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<ComposeView>(R.id.compose_view).apply {
            setContent {
                val nestedScrollInterop = rememberNestedScrollInteropConnection()
                // Add the nested scroll connection to your top level @Composable element
                // using the nestedScroll modifier.
                LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop)) {
                    items(20) { item ->
                        Box(
                            modifier = Modifier
                                .padding(16.dp)
                                .height(56.dp)
                                .fillMaxWidth()
                                .background(Color.Gray),
                            contentAlignment = Alignment.Center
                        ) {
                            Text(item.toString())
                        }
                    }
                }
            }
        }
    }
}

Bir AndroidView alt öğesi içeren üst öğe

Bu senaryoda, bir alt öğe AndroidView içeren üst bir derlenebilir öğeniz olduğunda, derleme tarafında iç içe yerleştirilmiş kaydırma birlikte çalışabilirlik API'sinin uygulanması ele alınmaktadır. AndroidView, kaydırmalı bir üst öğenin çocuğu olarak NestedScrollDispatcher ve kaydırmalı bir alt öğenin üst öğesi olarak NestedScrollingParent3 uygular.View Oluşturma üst öğesi, iç içe yerleştirilmiş bir kaydırılabilir alt öğeden View iç içe yerleştirilmiş kaydırma deltaları alabilir.

Aşağıdaki örnekte, bu senaryoda iç içe kaydırma birlikte çalışabilirliğini ve daraltılabilir bir Oluştur araç çubuğunu nasıl elde edebileceğiniz gösterilmektedir:

@Composable
private fun NestedScrollInteropComposeParentWithAndroidChildExample() {
    val toolbarHeightPx = with(LocalDensity.current) { ToolbarHeight.roundToPx().toFloat() }
    val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }

    // Sets up the nested scroll connection between the Box composable parent
    // and the child AndroidView containing the RecyclerView
    val nestedScrollConnection = remember {
        object : NestedScrollConnection {
            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                // Updates the toolbar offset based on the scroll to enable
                // collapsible behaviour
                val delta = available.y
                val newOffset = toolbarOffsetHeightPx.value + delta
                toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
                return Offset.Zero
            }
        }
    }

    Box(
        Modifier
            .fillMaxSize()
            .nestedScroll(nestedScrollConnection)
    ) {
        TopAppBar(
            modifier = Modifier
                .height(ToolbarHeight)
                .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) }
        )

        AndroidView(
            { context ->
                LayoutInflater.from(context)
                    .inflate(R.layout.view_in_compose_nested_scroll_interop, null).apply {
                        with(findViewById<RecyclerView>(R.id.main_list)) {
                            layoutManager = LinearLayoutManager(context, VERTICAL, false)
                            adapter = NestedScrollInteropAdapter()
                        }
                    }.also {
                        // Nested scrolling interop is enabled when
                        // nested scroll is enabled for the root View
                        ViewCompat.setNestedScrollingEnabled(it, true)
                    }
            },
            // ...
        )
    }
}

private class NestedScrollInteropAdapter :
    Adapter<NestedScrollInteropAdapter.NestedScrollInteropViewHolder>() {
    val items = (1..10).map { it.toString() }

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): NestedScrollInteropViewHolder {
        return NestedScrollInteropViewHolder(
            LayoutInflater.from(parent.context)
                .inflate(R.layout.list_item, parent, false)
        )
    }

    override fun onBindViewHolder(holder: NestedScrollInteropViewHolder, position: Int) {
        // ...
    }

    class NestedScrollInteropViewHolder(view: View) : ViewHolder(view) {
        fun bind(item: String) {
            // ...
        }
    }
    // ...
}

Bu örnekte, API'yi scrollable değiştiricisiyle nasıl kullanabileceğiniz gösterilmektedir:

@Composable
fun ViewInComposeNestedScrollInteropExample() {
    Box(
        Modifier
            .fillMaxSize()
            .scrollable(rememberScrollableState {
                // View component deltas should be reflected in Compose
                // components that participate in nested scrolling
                it
            }, Orientation.Vertical)
    ) {
        AndroidView(
            { context ->
                LayoutInflater.from(context)
                    .inflate(android.R.layout.list_item, null)
                    .apply {
                        // Nested scrolling interop is enabled when
                        // nested scroll is enabled for the root View
                        ViewCompat.setNestedScrollingEnabled(this, true)
                    }
            }
        )
    }
}

Son olarak bu örnekte, başarılı bir sürükle ve kapatma davranışı elde etmek için iç içe yerleştirilmiş kaydırma birlikte çalışabilirlik API'sinin BottomSheetDialogFragment ile nasıl kullanıldığı gösterilmektedir:

class BottomSheetFragment : BottomSheetDialogFragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        val rootView: View = inflater.inflate(R.layout.fragment_bottom_sheet, container, false)

        rootView.findViewById<ComposeView>(R.id.compose_view).apply {
            setContent {
                val nestedScrollInterop = rememberNestedScrollInteropConnection()
                LazyColumn(
                    Modifier
                        .nestedScroll(nestedScrollInterop)
                        .fillMaxSize()
                ) {
                    item {
                        Text(text = "Bottom sheet title")
                    }
                    items(10) {
                        Text(
                            text = "List item number $it",
                            modifier = Modifier.fillMaxWidth()
                        )
                    }
                }
            }
            return rootView
        }
    }
}

rememberNestedScrollInteropConnection(), eklediğiniz öğeye bir NestedScrollConnection yükler. NestedScrollConnection, deltaları Oluşturma seviyesinden View seviyesine aktarmaktan sorumludur. Bu, öğenin iç içe yerleştirilmiş kaydırma özelliğine katılmasını sağlar ancak öğelerin otomatik olarak kaydırılmasını etkinleştirmez. Box veya Column gibi otomatik olarak kaydırılabilir olmayan bileşenlerde, bu bileşenlerdeki kaydırma deltaları iç içe yerleştirilmiş kaydırma sisteminde yayılmaz ve deltalar rememberNestedScrollInteropConnection() tarafından sağlanan NestedScrollConnection bileşenine ulaşmaz. Bu nedenle, bu deltalar üst View bileşenine de ulaşamaz. Bu sorunu çözmek için, bu tür iç içe yerleştirilmiş bileşenlerde de kaydırılabilir değiştiricileri ayarladığınızdan emin olun. Daha ayrıntılı bilgi için İç içe yerleştirilmiş kaydırma ile ilgili önceki bölüme bakabilirsiniz.

ComposeView adlı bir çocuğu olan View adlı işbirliği yapmayan ebeveyn

İşbirliği yapmayan bir Görüntüleme, View tarafında gerekli NestedScrolling arayüzlerini uygulamayan bir görüntülemedir. Bu, bu Views ile iç içe kaydırma birlikte çalışabilirliğinin kutudan çıktığı anda çalışmadığı anlamına gelir. İşbirliği yapmayan Views, RecyclerView ve ViewPager2'dir.