Kaydırma değiştiriciler
verticalScroll
ve horizontalScroll
değiştiriciler, kullanıcının bir öğenin sınırları maksimum boyut kısıtlamalarından büyük olduğunda öğeyi kaydırmasına imkan vermenin en basit yolunu sunar. verticalScroll
ve horizontalScroll
değiştiricileri sayesinde içerikleri çevirmenize veya belirli uzaklıkta kopyasını oluşturmanıza gerek kalmaz.
@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)) } } }
ScrollState
, kaydırma konumunu değiştirmenize veya mevcut durumunu almanıza 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ştiricisi, scrollable
ürününün kaydırma hareketlerini algılaması ancak içeriklerini dengelememesi açısından kaydırma değiştiricilerinden farklıdır. Bu değiştiricinin doğru şekilde çalışması için ScrollableState
gereklidir.
ScrollableState
oluştururken, her kaydırma adımında (hareket girişi, yumuşak kaydırma veya kaydırma ile) delta değeri piksel cinsinden ç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 doğru şekilde yayıldığından emin olmak için tüketilen kaydırma mesafesi miktarını döndürmelidir.
Aşağıdaki snippet, hareketleri algılar ve bir ofset için sayısal bir değer görüntüler, ancak herhangi bir öğeyi dengelemez:
@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()) } }
İç içe kaydırma
Oluşturma işlemi,birden fazla öğenin tek bir kaydırma hareketine tepki verdiği iç içe yerleştirilmiş kaydırmayı destekler. İç içe kaydırmanın tipik bir örneği, başka bir listenin içindeki bir listedir. Daha karmaşık bir durum ise daraltılan araç çubuğudur.
Otomatik iç içe kaydırma
Basit iç içe kaydırma sizin herhangi bir işlem yapmanıza gerek yoktur. Kaydırma işlemi başlatan hareketler, çocuklardan üst öğelere otomatik olarak aktarılır. Böylece, çocuk daha fazla kaydırma yapamadığında, hareket kendi üst öğesi tarafından işlenir.
Otomatik iç içe kaydırma özelliği, oluşturma özelliğinin 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 kaydırma desteğine sahip üst öğelere yaydığı anlamına gelir.
Aşağıdaki örnekte, verticalScroll
değiştiricisinin de uygulandığı bir kapsayıcı içinde kendisine verticalScroll
değiştirici 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) ) } } } } }
nestedScroll
değiştiricisini kullanma
Birden çok öğe arasında gelişmiş bir koordine 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 kaydırma desteği bulunur. Bununla birlikte, Box
veya Column
gibi otomatik olarak kaydırılamayan composable'lar için bu tür bileşenlerdeki kaydırma deltaları, iç içe kaydırma sisteminde yayılmaz ve deltalar NestedScrollConnection
öğesine 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 destek sağlamak üzere nestedScroll
özelliğini kullanabilirsiniz.
İç içe kaydırma birlikte çalışabilirlik (Compose 1.2.0'dan itibaren)
Kaydırılabilir View
öğelerini, kaydırılabilir composable'ların içine veya tersi şekilde iç içe yerleştirmeye çalıştığınızda sorunlarla karşılaşabilirsiniz.
En dikkat çekici olanlar, alt öğeyi kaydırıp başlangıç veya bitiş sınırlarına ulaştığınızda ve üst öğenin kaydırmayı tamamlamasını beklediğinizde gerçekleşir. Ancak bu beklenen davranış gerçekleşmeyebilir veya beklendiği gibi çalışmayabilir.
Bu sorun, kaydırılabilir composable'larda yerleşik olarak bulunan beklentilerin bir sonucudur.
Kaydırılabilir composable'ların "nested-scroll-by-default" kuralı vardır. Bu, tüm kaydırılabilir container'ların hem NestedScrollConnection
üzerinden üst öğe olarak hem de NestedScrollDispatcher
aracılığıyla alt öğe olarak iç içe kaydırma zincirine katılması gerektiği anlamına gelir.
Ardından, alt öğe sınırdayken üst öğe için iç içe yerleştirilmiş bir kaydırma yürütür. Örneğin, bu kural Oluştur Pager
ve Oluştur LazyRow
öğelerini birlikte sorunsuzca kullanmanızı sağlar. Ancak birlikte çalışabilirlik kaydırması ViewPager2
veya RecyclerView
ile yapılırken NestedScrollingParent3
uygulanmadığından, alt öğeden üst öğeye sürekli kaydırma yapmak mümkün değildir.
Kaydırılabilir View
öğeleri ile her iki yönde iç içe yerleştirilmiş kaydırılabilir composable'lar arasında iç içe kaydırma birlikte çalışabilirlik API'sini etkinleştirmek için aşağıdaki senaryolarda, iç içe yerleştirilmiş kayan birlikte çalışabilirlik API'sini kullanarak bu sorunları hafifletebilirsiniz.
ComposeView
adlı alt öğeyi içeren, iş birliği yapan bir üst (View
)
İşbirliği yapan üst View
, hâlihazırda NestedScrollingParent3
yöntemini uygulayan ve bu nedenle, işbirliği yapan iç içe yerleştirilmiş bir alt composable'dan kaydırma deltaları alabilen üst öğedir. ComposeView
bu durumda bir alt öğe olarak hareket eder ve NestedScrollingChild3
'i (dolaylı olarak) uygulamalıdır.
İşbirliği yapan üst iş ortaklarına örnek olarak androidx.coordinatorlayout.widget.CoordinatorLayout
verilebilir.
Kaydırılabilir View
üst container'ları ve iç içe 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
üst öğesi ile bir Compose alt öğesi arasında iç içe kaydırma birlikte çalışabilirliği sağlayan NestedScrollConnection
özelliğine izin verir ve bu özelliği hatırlar.View
Bu, bir nestedScroll
değiştiriciyle birlikte kullanılmalıdır. Oluşturma tarafında iç içe kaydırma özelliği varsayılan olarak etkinleştirildiğinden, View
tarafında iç içe kaydırmayı etkinleştirmek ve Views
ile composable'lar arasına gerekli birleştirici mantığı eklemek için bu bağlantıyı kullanabilirsiniz.
Aşağıdaki örnekte gösterilen, CoordinatorLayout
, CollapsingToolbarLayout
ve bir "child composable"ın kullanımı sık görülen bir kullanım alanıdı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 Bölümünüzde, çocuğunuz için composable'ı ve gerekli NestedScrollConnection
öğelerini ayarlamanız gerekir:
open class MainActivity : ComponentActivity() { @OptIn(ExperimentalComposeUiApi::class) 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, Oluşturma tarafında iç içe yerleştirilmiş kaydırma birlikte çalışabilirlik API'sinin uygulanması ele alınmaktadır (alt AndroidView
içeren bir üst composable'ınız olduğunda). AndroidView
, View
kaydırma alt öğesi olarak kullanıldığı için NestedScrollDispatcher
özelliğinin yanı sıra NestedScrollingParent3
öğesini de hem Compose kaydırma üst öğesi için de alt öğe olarak görev yapar hem de NestedScrollingParent3
uygular. Böylece Compose üst öğesi iç içe yerleştirilmiş kaydırılabilir bir alt öğeden (View
) iç içe yerleştirilmiş kaydırma deltaları alabilir.
Aşağıdaki örnekte, bu senaryoda Daraltılabilen bir Oluştur araç çubuğu ile birlikte iç içe kaydırma birlikte çalışabilirliğini nasıl gerçekleştirebileceğ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, scrollable
değiştiricisiyle API'yi 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ükleme ve kapatma davranışı elde etmek için iç içe yerleştirilmiş kayan 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()
'in, eklediğiniz öğeye bir NestedScrollConnection
yükleyeceğini unutmayın. NestedScrollConnection
, deltaları Oluşturma düzeyinden View
düzeyine iletmekten sorumludur. Bu, öğenin iç içe 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'lar için, bu tür bileşenlerdeki kaydırma deltaları iç içe kaydırma sisteminde yayılmaz ve deltalar, rememberNestedScrollInteropConnection()
tarafından sağlanan NestedScrollConnection
değerine ulaşmaz. Dolayısıyla, bu deltalar üst View
bileşenine ulaşmaz. Bu sorunu çözmek için bu tür iç içe yerleştirilmiş composable'lar için kaydırılabilir değiştiriciler ayarladığınızdan emin olun. Daha ayrıntılı bilgi için önceki İç içe kaydırma bölümüne bakabilirsiniz.
ComposeView
alt yayıncısı olan, iş birliği yapmayan bir üst (View
)
İşbirliği yapmayan Görünüm, View
tarafında gerekli NestedScrolling
arayüzlerinin uygulanmadığı Görünümdür. Bunun, bu Views
ile iç içe kaydırma birlikte çalışabilirliğinin kullanıma hazır olarak çalışmayacağı anlamına geldiğini unutmayın. İşbirliği yapmayan Views
: RecyclerView
ve ViewPager2
.
Sizin için önerilenler
- Not: Bağlantı metni JavaScript kapalıyken görüntülenir
- Hareketleri anlama
CoordinatorLayout
öğesini Compose'a taşıyın- Oluşturma işleminde görünümleri kullanma