İç içe yerleştirilmiş kaydırma değiştiriciler

İç içe yerleştirilmiş kaydırma, birbirinin içinde yer alan birden fazla kaydırma bileşeninin tek bir kaydırma hareketine tepki vererek ve kaydırma deltalarını (değişikliklerini) ileterek birlikte çalıştığı bir sistemdir.

İç içe yerleştirilmiş kaydırma sistemi, kaydırılabilir ve hiyerarşik olarak bağlantılı (genellikle aynı üst öğeyi paylaşarak) bileşenler arasında koordinasyon sağlar. Bu sistem, kaydırma kapsayıcılarını birbirine bağlar ve aralarında yayılıp paylaşılan kaydırma deltalarıyla etkileşime olanak tanır.

Compose, composable'lar arasında iç içe yerleştirilmiş kaydırmayı yönetmenin birden fazla yolunu sunar. İç içe yerleştirilmiş kaydırmanın tipik bir örneği, 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 yerleştirilmiş kaydırma için herhangi bir işlem yapmanız gerekmez. Kaydırma işlemini başlatan hareketler, alt öğelerden üst öğelere otomatik olarak yayılır. Böylece alt öğe daha fazla kaydırılamadığında hareket, üst öğesi tarafından işlenir.

Otomatik iç içe yerleştirilmiş 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. Bu, kullanıcı iç içe yerleştirilmiş bileşenlerin iç alt öğesini kaydırdığında önceki değiştiricilerin, kaydırma deltalarını iç içe yerleştirilmiş kaydırma desteği olan üst öğelere yaydığı anlamına gelir.

Aşağıdaki örnekte, verticalScroll değiştiricisinin uygulandığı bir kapsayıcıda verticalScroll değiştiricisinin uygulandığı öğ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)
                    )
                }
            }
        }
    }
}

İç içe yerleştirilmiş iki dikey kaydırma kullanıcı arayüzü öğesi, iç öğenin içindeki ve dışındaki hareketlere yanıt veriyor.
Şekil 1. İç öğenin içindeki ve dışındaki hareketlere yanıt veren, iç içe yerleştirilmiş iki dikey kaydırma kullanıcı arayüzü öğesi.

nestedScroll değiştiricisini kullanma

Birden fazla öğe arasında gelişmiş koordineli kaydırma oluşturmanız gerekiyorsa nestedScroll değiştiricisi, iç içe yerleştirilmiş bir kaydırma hiyerarşisi tanımlayarak size daha fazla esneklik sağlar. Ö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ılamayan composable'larda, bu tür bileşenlerdeki kaydırma deltaları iç içe yerleştirilmiş kaydırma sisteminde yayılmaz ve deltalar NestedScrollConnection veya üst bileşene ulaşmaz. Bu sorunu çözmek için nestedScroll kullanarak özel bileşenler de dahil olmak üzere diğer bileşenlere bu tür bir destek sağlayabilirsiniz.

İç 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. Örneğin, kaydırılabilir bileşenler ve değiştiriciler ya da 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 (örneğin, bir 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ı
Şekil 2. İç içe yerleştirilmiş kaydırma döngüsünün aşamaları.

İlk aşamada (kaydırma öncesi), tetikleyici etkinlik farklarını alan bileşen, bu etkinlikleri hiyerarşi ağacı üzerinden en üstteki üst öğeye gönderir. Delta etkinlikleri daha sonra aşağı doğru yayılır. Yani deltalar, en üstteki üst öğeden iç içe yerleştirilmiş kaydırma döngüsünü başlatan alt öğeye doğru yayılır.

Kaydırma öncesi aşama: yukarı gönderme
Şekil 3. Kaydırma öncesi aşama: yukarı gönderme.

Bu sayede, iç içe yerleştirilmiş kaydırma üst öğeleri (nestedScroll veya kaydırılabilir değiştiriciler kullanan bileşenler), düğümün kendisi tüketmeden önce delta ile bir işlem yapabilir.

Kaydırma öncesi aşama - aşağı doğru kabarcık oluşturma
Şekil 4. Kaydırma öncesi aşama: Aşağı doğru kabarcık oluşturma.

Düğüm tüketimi aşamasında, düğümün kendisi, üst öğeleri tarafından kullanılmayan tüm deltaları 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ı
Şekil 5. Düğüm tüketim aşaması.

Bu aşamada çocuk, kalan parşömenin tamamını veya bir kısmını kullanmayı seçebilir. Kalan her şey, kaydırma sonrası aşamadan geçmek üzere geri gönderilir.

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

Kaydırma sonrası aşama: yukarı gönderme
Şekil 6. Kaydırma sonrası aşama: yukarı gönderme.

Kaydırma sonrası aşama, kaydırma öncesi aşamaya benzer şekilde çalışır. Bu aşamada, üst öğelerden herhangi biri tüketmeyi veya tüketmemeyi seçebilir.

Kaydırma sonrası aşama: aşağı doğru kabarcık oluşturma
Şekil 7. Kaydırma sonrası aşama: aşağı doğru kabarcık oluşturma.

Kaydırmaya benzer şekilde, sürükleme hareketi bittiğinde kullanıcının niyeti, kaydırılabilir kapsayıcıyı fırlatmak (animasyon kullanarak kaydırmak) için kullanılan bir hıza dönüştürülebilir. Kaydırma hareketi de 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: kaydırma öncesi, düğüm tüketimi ve kaydırma sonrası. Kaydırma animasyonunun yalnızca dokunma hareketiyle ilişkili olduğunu ve erişilebilirlik 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 deltaların kesilmesi, tüketilmesi ve tüketiminin raporlanması anlamına gelir. Compose, iç içe yerleştirilmiş kaydırma sisteminin işleyişini ve bu sistemle doğrudan etkileşimi etkileyecek bir dizi araç sunar. Ö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çları kullanabilirsiniz.

İç içe yerleştirilmiş kaydırma döngüsü, bir düğüm zinciri üzerinde işlem yapan bir sistemse nestedScroll değiştiricisi, bu değişiklikleri yakalayıp bunlara müdahale etmenin ve zincirde yayılan verileri (kaydırma deltaları) etkilemenin bir yoludur. Bu değiştirici, hiyerarşinin herhangi bir yerine yerleştirilebilir ve ağaçtaki iç içe yerleştirilmiş kaydırma değiştirici örnekleriyle iletişim kurarak bu kanal üzerinden bilgi paylaşabilir. Bu değiştiricinin yapı taşları NestedScrollConnection ve NestedScrollDispatcher.

NestedScrollConnection, iç içe yerleştirilmiş kaydırma döngüsünün aşamalarına yanıt vermenin ve iç içe yerleştirilmiş kaydırma sistemini etkilemenin bir yolunu sunar. Dört geri çağırma yönteminden oluşur. Her biri, tüketim aşamalarından birini temsil eder: kaydırma öncesi/sonrası ve kaydırma ö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, yayılan delta hakkında da bilgi verir: Söz konusu aşama için available delta ve önceki aşamalarda tüketilen consumed delta. Deltaları hiyerarşide yukarı doğru yaymayı durdurmak isterseniz 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 çağırmalar, 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ı kullanmak ve 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ırmayı özelleştirmeyle ilgili kullanım alanlarının çoğunda, yeni delta göndermek yerine mevcut deltalara tepki vermek için dağıtıcı yerine NestedScrollConnection kullanılır. Diğer kullanım şekilleri için NestedScrollDispatcherSample sayfasına bakın.

Kaydırırken resmi yeniden boyutlandırma

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

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

Bu snippet, dikey kaydırma konumuna göre bir LazyColumn içindeki resmin yeniden boyutlandırılmasını gösterir. Kullanıcı aşağı kaydırdıkça resim küçülür, yukarı kaydırdıkça büyür ve 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 yakalamak için NestedScrollConnection kullanır.
  • onPreScroll, kaydırma farkına göre resim boyutundaki değişikliği hesaplar.
  • currentImageSize durum değişkeni, resmin geçerli boyutunu depolar. minImageSize ve maxImageSize. imageScale arasında kısıtlanan bu boyut, currentImageSize değerinden türetilir.
  • LazyColumn, currentImageSize temel alınarak dengelenir.
  • Image, hesaplanan ölçeği uygulamak için graphicsLayer değiştiricisini kullanır.
  • graphicsLayer içindeki translationY, resmin ölçeklendirilirken dikey olarak ortalanmış kalmasını sağlar.

Sonuç

Önceki snippet, kaydırma sırasında ölçeklenen bir resim efektiyle sonuçlanır:

Şekil 8. Kaydırma sırasında ölçeklenen resim efekti.

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

Kaydırılabilir View öğelerini kaydırılabilir composable'ların içine yerleştirmeye çalıştığınızda veya tam tersini yaptığınızda sorunlarla karşılaşabilirsiniz. En belirgin olanlar, alt öğeyi kaydırıp başlangıç veya bitiş sınırlarına ulaştığınızda ve üst öğenin kaydırma işlemini devralmasını beklediğinizde ortaya çıkar. Ancak bu beklenen davranış gerçekleşmeyebilir veya beklendiği gibi çalışmayabilir.

Bu sorun, kaydırılabilir composable'larda yerleşik olarak bulunan beklentilerden kaynaklanır. Kaydırılabilir bileşenlerin "varsayılan olarak iç içe yerleştirilmiş kaydırma" kuralı vardır. Bu kural, kaydırılabilir tüm 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. Ardından, alt öğe sınıra ulaştığında üst öğe için iç içe yerleştirilmiş bir kaydırma işlemi gerçekleştirir. Örneğin bu kural, Compose Pager ve Compose LazyRow'un birlikte iyi çalışmasını sağlar. Ancak birlikte çalışabilirlik kaydırma işlemi ViewPager2 veya RecyclerView ile yapıldığında, bunlar NestedScrollingParent3'yi uygulamadığı için alt öğeden üst öğeye sürekli kaydırma mümkün değildir.

Kaydırılabilir View öğeleri ile kaydırılabilir composable'lar arasında iç içe yerleştirilmiş kaydırma birlikte çalışabilirlik API'sini etkinleştirmek için her iki yönde de iç içe yerleştirilmiş kaydırma birlikte çalışabilirlik API'sini kullanarak aşağıdaki senaryolarda bu sorunları azaltabilirsiniz.

ComposeView alt öğesini içeren, birlikte çalışan bir View üst öğesi

İş birliği yapan bir üst öğe View, NestedScrollingParent3'i zaten uygulayan ve bu nedenle iş birliği yapan iç içe yerleştirilmiş bir alt composable'dan kaydırma deltaları alabilen bir öğedir. ComposeView bu durumda alt sınıf olarak hareket eder ve (dolaylı olarak) NestedScrollingChild3 sınıfını uygulaması gerekir. İş birliği yapan ebeveynlere örnek olarak androidx.coordinatorlayout.widget.CoordinatorLayout verilebilir.

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

rememberNestedScrollInteropConnection(), NestedScrollingParent3 uygulayan bir View üst öğe ile Compose alt öğesi arasında iç içe yerleştirilmiş kaydırma birlikte çalışabilirliğini sağlayan NestedScrollConnection öğesine izin verir ve bunu hatırlar. Bu, nestedScroll değiştiricisiyle birlikte kullanılmalıdır. İç içe yerleştirilmiş kaydırma, Compose 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 composable'lar arasına gerekli birleştirme mantığını ekleyebilirsiniz.

Sık kullanılan bir kullanım alanı, bu örnekte gösterilen CoordinatorLayout, CollapsingToolbarLayout ve alt bileşeni kullanmaktır:

<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'ınızda alt composable'ınızı ve gerekli NestedScrollConnection öğesini 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())
                        }
                    }
                }
            }
        }
    }
}

Alt öğe AndroidView içeren bir üst composable

Bu senaryoda, alt öğe içeren bir üst composable'ınız olduğunda, Compose tarafında iç içe yerleştirilmiş kaydırma birlikte çalışabilirlik API'sinin uygulanması ele alınmaktadır. AndroidView. AndroidView, kaydırılabilir bir Compose üst öğesinin alt öğesi olarak davrandığı için NestedScrollDispatcher'yi, kaydırılabilir bir View alt öğesinin üst öğesi olarak davrandığı için de NestedScrollingParent3'yi uygular. Oluşturma üst öğesi, iç içe yerleştirilmiş kaydırılabilir alt öğeden iç içe yerleştirilmiş kaydırma deltalarını alabilir View.

Aşağıdaki örnekte, bu senaryoda iç içe yerleştirilmiş kaydırma birlikte çalışabilirliğini ve Compose daraltılabilir araç çubuğunu nasıl kullanabileceğ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(
            title = {},
            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(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 iç içe yerleştirilmiş kaydırma birlikte çalışabilirlik API'sinin, başarılı bir sürükleme ve kapatma davranışı elde etmek için 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, farkları Compose düzeyinden View düzeyine iletmekten sorumludur. Bu, öğenin iç içe yerleştirilmiş kaydırmaya katılmasını sağlar ancak öğelerin otomatik olarak kaydırılmasını sağlamaz. Box veya Column gibi otomatik olarak kaydırılamayan composable'larda, bu tür bileşenlerdeki kaydırma deltaları iç içe yerleştirilmiş kaydırma sisteminde yayılmaz ve deltalar rememberNestedScrollInteropConnection() tarafından sağlanan NestedScrollConnection değerine ulaşmaz. Bu nedenle, bu deltalar üst View bileşenine ulaşmaz. Bu sorunu çözmek için bu tür iç içe yerleştirilmiş composable'lara kaydırılabilir değiştiriciler de ayarladığınızdan emin olun. Daha ayrıntılı bilgi için iç içe yerleştirilmiş kaydırma ile ilgili önceki bölüme bakabilirsiniz.

ComposeView alt öğesini içeren, işbirliği yapmayan bir üst öğe View

Uyumlu olmayan bir görünüm, View tarafında gerekli NestedScrolling arayüzlerini uygulamayan bir görünümdür. Bu durum, iç içe yerleştirilmiş kaydırma özelliğinin bu Views ile birlikte çalışabilirliğinin hazır olarak sunulmadığı anlamına gelir. İşbirliği yapmayan Views: RecyclerView ve ViewPager2.

Ek kaynaklar