[Android] Pointer input - Nested Scroll
#1 ์ด์ ๊ฒ์๊ธ
[Android] Pointer input - Scroll
#1 ๊ฐ์ ์คํฌ๋กค | Jetpack Compose | Android Developers์ด ํ์ด์ง๋ Cloud Translation API๋ฅผ ํตํด ๋ฒ์ญ๋์์ต๋๋ค. ์คํฌ๋กค ์ปฌ๋ ์ ์ ์ฌ์ฉํด ์ ๋ฆฌํ๊ธฐ ๋ด ํ๊ฒฝ์ค์ ์ ๊ธฐ์ค์ผ๋ก ์ฝํ ์ธ ๋ฅผ ์ ์ฅํ๊ณ ๋ถ๋ฅํ์ธ
kenel.tistory.com
์คํฌ๋กค์ ๋ํด ๋ค๋ฃฌ ์ ๊ฒ์๊ธ์์๋ถํฐ ์ด์ด์ง๋ค.
#2 ์ค์ฒฉ(Nested) ์คํฌ๋กค
#2-1 ๊ฐ์
์ค์ฒฉ(Nested) ์คํฌ๋กค์ด๋, ๋ง ๊ทธ๋๋ก ๊ฐ์ ๋ฐฉํฅ(์์ง ๋๋ ์ํ)์ผ๋ก ์คํฌ๋กค ํ๋ ์ปจํ ์ด๋ 2๊ฐ๊ฐ ๋ถ๋ชจ-์์ ๊ด๊ณ๋ฅผ ํ์ฑํ ๊ฒ์ ๋ปํ๋ค. ์ฆ verticalScroll()์ด ์ ์ฉ๋ Column() ์์ ๋ค์ verticalScroll()์ด ์ ์ฉ๋ Column()์ ๋ฃ์ผ๋ฉด, ๋ฐ์ ์๋ ๋ถ๋ชจ Column()๊ณผ ์์ ์๋ ์์ Column()์ด ์ค์ฒฉ ์คํฌ๋กค์ ์ด๋ฃจ๊ฒ ๋๋ ๊ฒ์ด๋ค.
#2-2 ์์์ ์ค์ฒฉ ์คํฌ๋กค
์ฝ๋
@Composable
private fun AutomaticNestedScroll() {
val gradient = Brush.verticalGradient(0f to Color.Gray, 1000f to Color.White)
Box(
modifier = Modifier
.background(Color.LightGray)
.height(480.dp)
.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)
)
}
}
}
}
}
verticalScroll()์ด ์ ์ฉ๋ Column() ์์ ๋ค์ verticalScroll()์ด ์ ์ฉ๋ Column()์ ๋ฃ์ ์ฝ๋๋ค. ์ด๋ ๊ฒ๋ง ํด๋ ์์์ ์ผ๋ก(= ์์์) ์ค์ฒฉ ์คํฌ๋กค์ด ์ํ๋๋ค. Jetpack Compose์์ ์ด๋ฐ ์์์ ์ผ๋ก ์ค์ฒฉ ์คํฌ๋กค์ด ๋๋ Modifier์ ํ์ฅํจ์๋ verticalScroll(), horizontalScroll(), scrollable()์ด ์๊ณ , ์ปดํฌ์ ๋ธ๋ก๋ Lazy lists, TextField()๊ฐ ์๋ค.
๊ฒฐ๊ณผ
๋ถ๋ชจ ์์ญ์ ์คํฌ๋กค ํ ๋๋ ํ๋ฒํ๊ฒ ์คํฌ๋กค ๋๋ค. ์์ ์์ญ์ ์คํฌ๋กค ํ ๋๋ ์์ ์์ญ์ด ์คํฌ๋กค ๋๋, ์์ ์์ญ์์ ๋ ์ด์ ์คํฌ๋กค ํ ์์ญ์ด ๋จ์์์ง ์์ ๊ฒฝ์ฐ์๋ง ๋ถ๋ชจ ์์ญ์ด ๋์ ์คํฌ๋กค ๋๋ค. ์ด๋ ์ฐ๋ฆฌ๊ฐ ์ผ์์์ ์ธํฐ๋ท ์น์ํ ํน์ ์ค๋งํธํฐ์ ์ฌ์ฉํ ๋ ๊ธฐ๋ํ๋ ์์ฐ์ค๋ฌ์ด ์คํฌ๋กค ์๋๊ฐ?
#2-3 ๋ช ์์ ์ค์ฒฉ ์คํฌ๋กค ์ฌ์ฉ ์๊ธฐ
์์์ ์ค์ฒฉ ์คํฌ๋กค์์ ๋ณด์๋ฏ ์์ ์ปจํ ์ด๋ → ๋ถ๋ชจ ์ปจํ ์ด๋์ ๋ฐฉํฅ์ผ๋ก ์ด๋ฒคํธ๊ฐ ์ ํ๋๊ธฐ๋ฅผ ๋ฐ๋ผ๋ ๊ฒ ์ผ๋ฐ์ ์ด๋ฉฐ ์์ฐ์ค๋ฌ์ด ๊ฒฝ์ฐ๋ค. ๊ทธ๋ ๋ค๋ฉด ๋ช ์์ ์ค์ฒฉ ์คํฌ๋กค์ ์ธ์ ์ฐ๋๊ฐ?
์ฒซ์งธ๋ก ์์ฐ์ค๋ฝ์ง ์์ ๊ฒฝ์ฐ, ๋ฐ๋ก ๋ถ๋ชจ ์ปจํ ์ด๋ → ์์ ์ปจํ ์ด๋์ ๋ฐฉํฅ์ผ๋ก ์ด๋ฒคํธ๋ฅผ ์ ํ์ํค๊ณ ์ถ์ ๋๋ค. ์์์ ์ค์ฒฉ ์คํฌ๋กค์ด ์์ ์ปจํ ์ด๋ → ๋ถ๋ชจ ์ปจํ ์ด๋์ ๋ฐฉํฅ์ผ๋ก ๊ณ ์ ๋์ด ์์ผ๋ฏ๋ก, ๋ช ์์ ์ค์ฒฉ ์คํฌ๋กค์ ํตํด ์ด ๋ฐฉํฅ์์ ๋ฒ์ด๋๋ ค๋ ๊ฒ์ด๋ค.
๋์งธ๋ก๋ ์์์ ์ค์ฒฉ ์คํฌ๋กค๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์์ ์ปจํ ์ด๋ → ๋ถ๋ชจ ์ปจํ ์ด๋์ ๋ฐฉํฅ์ ์ ์งํ์ง๋ง, ๊ทธ ์ด๋ฒคํธ ์ ํ ๊ณผ์ ์ค๊ฐ์ ํ๋ก๊ทธ๋๋จธ๊ฐ ํน์ ํ ๋ก์ง์ ๋ฃ๊ณ ์ถ์ ๊ฒฝ์ฐ๋ค.
#3 ๋ช ์์ ์ค์ฒฉ ์คํฌ๋กค Modifier
#3-1 ์ ์ ์กฐ๊ฑด
์์์ ์ค์ฒฉ ์คํฌ๋กค์ด ๋๋ ๊ตฌ์กฐ์ฌ์ผ ํ๋ค. ์ฆ, #2์ ์๋ ๊ตฌ์กฐ์ฌ์ผ ํ์ ํ Modifier.nestedScroll()์ ์ ์ฉํ ์ ์๋ค๋ ๋ง์ด๋ค. ์์์ ์ค์ฒฉ ์คํฌ๋กค์ด ๋์ง ์๋ ๊ตฌ์กฐ์ธ๋ฐ, Modifier.nestedScroll()๋ง ์ ์ฉํ๋ฉด ์คํฌ๋กค ์์ฒด๊ฐ ๋์ง ์๋๋ค.
#3-2 Modifier.nestedScroll()
fun Modifier.nestedScroll(
connection: NestedScrollConnection,
dispatcher: NestedScrollDispatcher? = null
): Modifier
Modifier.nestedScroll()์ ๋ช ์์ ์ค์ฒฉ ์คํฌ๋กค์ ์ฌ์ฉํ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ ๋๊ตฌ๋ค. Modifier.nestedScroll()์ ์ฝ๋ ์์ฒด๋ ๋ณ ๋ณผ์ผ์ด ์๋ค. ๋ช ์์ ์ค์ฒฉ ์คํฌ๋กค ๊ตฌํ์ ๋ณธ ๊ฒ์์ NestedScrollConnection์์ ์ผ์ด๋๋ค.
#3-3 NestedScrollConnection
/**
* ์ด ์ธํฐํ์ด์ค๋ฅผ Modifier.nestedScroll()์ ์ธ์๋ก ์ ๋ฌํ์ฌ
* ์ค์ฒฉ ์คํฌ๋กค ๊ณ์ธต ๊ตฌ์กฐ์ '์ฐธ์ฌ'ํ ์ ์๋ค. '์ฐธ์ฌ'ํ ์ดํ๋ถํฐ๋
* '์คํฌ๋กค ์์'์ ์คํฌ๋กค ํน์ NestedScrollDispatcher๋ฅผ ํตํด ํธ๋ฆฌ๊ฑฐ๋
* ์คํฌ๋กค ์ด๋ฒคํธ๋ฅผ '๋ถ๋ชจ'๋ก์ ์์ ํ ์ ์๋ค.
*/
@JvmDefaultWithCompatibility
interface NestedScrollConnection {
/**
* ์์์๊ฒ ์ ๋ฌ๋ ๋๋๊ทธ ์ด๋ฒคํธ์ ์ผ๋ถ ๋๋๊ทธ๋(delta)์
* ์์ (๋ถ๋ชจ)์ด ๋ฏธ๋ฆฌ '์๋น(consume)'ํ ์ ์๋๋ก ํธ์ถ๋๋ ํจ์
* ๋งค๊ฐ๋ณ์ available: ์ผ๋ง๋ ๋ฏธ๋ฆฌ '์๋น(consume)'ํ ์ ์๋์ง์ ์ด๋
* ๋งค๊ฐ๋ณ์ source: ์คํฌ๋กค ์ด๋ฒคํธ๊ฐ ์๊ฐ๋ฝ์ธ์ง ๋ง์ฐ์คํ ์ ์ํ ๊ฒ์ธ์ง ๋ฑ ๊ตฌ๋ถ
* ๋ฐํ๊ฐ: ๋ณธ ํจ์์์ '์๋น(consume)'ํ ์
*/
fun onPreScroll(available: Offset, source: NestedScrollSource): Offset = Offset.Zero
/**
* ์ฌํ ๋๋๊ทธ. ์คํฌ๋กค์ด ๋จ๋ ๋ถ๋ถ์ ์์์ผ๋ก๋ถํฐ ์ ๋ฌ๋ฐ์
* ์์ (๋ถ๋ชจ)์ด '์๋น(consume)'ํ ์ ์๋๋ก ํธ์ถ๋๋ ํจ์
* ๋งค๊ฐ๋ณ์ consumed: ๋ณธ ํจ์๊น์ง ์ค๋ ๊ณผ์ ์์ ํฉ์ฐ๋
* (๋ณต์์ onPreScroll() ๋ฐํ๊ฐ) + (์ค์ ํ๋ฉด์์ ์คํฌ๋กค) + (๋ณต์์ onPostScroll() ๋ฐํ๊ฐ)
* ๋งค๊ฐ๋ณ์ available: ์ผ๋ง๋ ๋ '์๋น(consume)'ํ ์ ์๋์ง์ ์ด๋
* ๋งค๊ฐ๋ณ์ source: ์คํฌ๋กค ์ด๋ฒคํธ๊ฐ ์๊ฐ๋ฝ์ธ์ง ๋ง์ฐ์คํ ์ ์ํ ๊ฒ์ธ์ง ๋ฑ ๊ตฌ๋ถ
* ๋ฐํ๊ฐ: ๋ณธ ํจ์์์ '์๋น(consume)'ํ ์
*/
fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset =
Offset.Zero
/**
* ์์์๊ฒ ์ ๋ฌ๋ ํ๋ง ์ด๋ฒคํธ์ ์ผ๋ถ ์๋ ฅ(velocity)์
* ์์ (๋ถ๋ชจ)์ด ๋ฏธ๋ฆฌ '์๋น(consume)'ํ ์ ์๋๋ก ํธ์ถ๋๋ ํจ์
* ๋งค๊ฐ๋ณ์ available: ์ผ๋ง๋ ๋ฏธ๋ฆฌ '์๋น(consume)'ํ ์ ์๋์ง์ ์ด๋
* ๋ฐํ๊ฐ: ๋ณธ ํจ์์์ '์๋น(consume)'ํ ์
*/
suspend fun onPreFling(available: Velocity): Velocity = Velocity.Zero
/**
* ์ฌํ ํ๋ง. ํ๋ง ์๋ ฅ(velocity)์ด ๋จ๋ ์ ๋๋ฅผ ์์์ผ๋ก๋ถํฐ ์ ๋ฌ๋ฐ์
* ์์ (๋ถ๋ชจ)์ด '์๋น(consume)'ํ ์ ์๋๋ก ํธ์ถ๋๋ ํจ์
* ๋งค๊ฐ๋ณ์ consumed: ๋ณธ ํจ์๊น์ง ์ค๋ ๊ณผ์ ์์ ํฉ์ฐ๋
* (๋ณต์์ onPreFling() ๋ฐํ๊ฐ) + (์ค์ ํ๋ฉด์์ ํ๋ง) + (๋ณต์์ onPreFling() ๋ฐํ๊ฐ)
* ๋งค๊ฐ๋ณ์ available: ์ผ๋ง๋ ๋ '์๋น(consume)'ํ ์ ์๋์ง์ ์ด๋
* ๋ฐํ๊ฐ: ๋ณธ ํจ์์์ '์๋น(consume)'ํ ์
*/
suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
return Velocity.Zero
}
}
์ด NestedScrollConnection์ 4๊ฐ์ง ํจ์๋ฅผ ํตํด, ๋ช ์์ ์ค์ฒฉ ์คํฌ๋กค์ด ๊ตฌํ๋๋ค. ๋ค๋ง, ์ฒ์์๋ ๊ฝค ๋ณต์กํ๊ธฐ ๋๋ฌธ์ ๋ช ์์ ์ค์ฒฉ ์คํฌ๋กค์ ๊ธฐ์ ๋ฅผ ์ ์ ์ ์ผ๋ก ์์๋ ํ์๊ฐ ์๋ค. ์ฃผ์์ ๊ณต์ ์ฝ๋์ ๋ถ์ด์๋ ๊ฑธ ๋ฒ์ญํ ๊ฒ์ด๋ค. ์ฌ๊ธฐ์ ๊ผผ๊ผผํ ์ฝ์ผ๋ฉฐ ์ดํดํ ํ์๋ ์๋ค. ์ด์ฐจํผ ์๋์์๋ถํฐ ์ค๋ช ํ๋ค.
#3-4 NestedScrollConnection์ ๊ธฐ์
NestedScrollConnection์ ๊ธฐ์ ๋ฅผ ํํํ๋ ๋์๋๋ก, ๋ง์น ์กํฐ๋นํฐ์ ์๋ช ์ฃผ๊ธฐ์ฒ๋ผ ์ค์ฒฉ ์คํฌ๋กค์ด ์ด๋ค ๊ณผ์ ์ผ๋ก ์ฒ๋ฆฌ๋๋์ง๋ฅผ ํํํ ๊ฒ์ด๋ค. Modifier์ verticalScroll() ๋ฐ nestedScroll()์ด ๋ถ์ ์ปดํฌ์ ๋ธ Parent2, Parent1, Child๊ฐ ์กด์ฌํ๋ค๊ณ ํด๋ณด์. Parent2 ๋ด๋ถ์๋ Parent1์ด ์๊ณ , Parent1 ๋ด๋ถ์๋ Child๊ฐ ์๋ ์ํฉ์ด๋ค. ์ด ๊ตฌ์กฐ์์ ์ค๋งํธํฐ ์ฌ์ฉ์๊ฐ Child์ ์คํฌ๋กค์ ๊ฐํ ์ํฉ์ด๋ผ ๊ฐ์ ํ๋ค.
onPreScroll() ์คํ๋ง์ ์ํ ํน๋ณํ ๋ฒ๋ธ๋ง
Jetpack Compose์์ ์ปดํฌ์ ๋ธ ๊ฐ ์ด๋ฒคํธ ์ ํ์ ์์ ๊ทธ๋ฆฌ๊ณ ๋ฉ์๋ ์ฒด์ด๋ ๋ Modifier ๊ฐ ์ด๋ฒคํธ ์ ํ์ ์์๋ ๊ฐ๊ฐ ์ค๊ณ์ ์ญ๋ฐฉํฅ, ๊ทธ๋ฌ๋๊น ์์ → ๋ถ๋ชจ ๊ทธ๋ฆฌ๊ณ (ํ์ ์ฒด์ด๋๋ ๋ฉ์๋) → (์ ์ ์ฒด์ด๋๋ ๋ฉ์๋)๋ค. ์ด๋ #2-2์์ ๋งํ, ์ฐ๋ฆฌ๊ฐ ์ผ์์ ์ผ๋ก ์น๋ธ๋ผ์ฐ์ ๋ ์ค๋งํธํฐ์ ์ฌ์ฉํ๋ฉฐ ๊ธฐ๋ํ๋ ์์ฐ์ค๋ฌ์ด ๋ฐฉํฅ์ด๋ค. ๋, ์ด๋ฅผ ์ด๋ฒคํธ ๋ฒ๋ธ๋ง(Event bubbling)์ด๋ผ๊ณ ๋ ๋ถ๋ฅธ๋ค.
๊ทธ๋ฌ๋ #2-3์์ ๋งํ๋ฏ, ๋ช ์์ ์ค์ฒฉ ์คํฌ๋กค์ (ํ๋ก๊ทธ๋๋จธ์ ์ํด ์๋์ ์ผ๋ก) ์์ฐ์ค๋ฝ์ง ์์์ผ ํ๋ค. ์ฆ ๋ถ๋ชจ๊ฐ ์์๋ณด๋ค ๋จผ์ ์ฌ์ฉ์์ ์คํฌ๋กค์ ๋ฐ์ํด์ผ ํ๋ค. ๊ทธ '๋ฐ์'์ ๋๋ณํ๋ ํจ์๊ฐ ๋ฐ๋ก onPreScroll()์ด๋ค. ๊ทธ๋ฆฌ๊ณ onPreScroll()์ ์คํ์ ๋ถ๋ชจ → ์์์ ๋ฐฉํฅ์ผ๋ก ํ๊ธฐ ์ํด์ ๊ฐ์ฅ ๊ทน๋จ์ ์์นํ ํ ๋ถ๋ชจ๋ฅผ ์ฐพ์์ผ ํ ๊ฒ์ด๋ค. ๊ทธ๋์ 'ํน๋ณํ' ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ ํตํด ๊ฐ์ฅ ๋์ ์๋ ๋ถ๋ชจ๊น์ง ๊ฐ ํ, ๋ค์ ๋์์ค๋ฉฐ ์ญ์์ผ๋ก onPreScroll()์ ์ํํ๋ค. 'ํน๋ณ'ํ๋ค๊ณ ํ ์ด์ ๋, ์ด ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ด Modifier.verticalScroll()์ด๋ Modifier.scrollable() ๋ฑ์ ๋ค๋ฅธ ๋ฉ์๋๋ค์ ๋ฌด์ํ๊ณ ์ค์ง Modifier.nestedScroll()๋ง์ ์ถ์ ํด ์ฌ๋ผ๊ฐ๊ธฐ ๋๋ฌธ์ด๋ค. ์ฆ ์ด ๊ณผ์ ์์ nestedScroll()์ด ์๋ ๋ค๋ฅธ ์คํฌ๋กค ๊ด๋ จ ๋ฉ์๋๋ค์ ์คํ๋์ง ์๋๋ค.
onPreScroll() ์คํ
์์ ์ํํ 'ํน๋ณ' ๋ฒ๋ธ๋ง์ onPreScroll()์ ์ํ ์์๋ฅผ ์์๋ด๊ธฐ ์ํ ์์ ์ด์๋ค. ๋ฒ๋ธ๋ง์ ์์ → ๋ถ๋ชจ ๋ฐฉํฅ์ผ๋ก ์ ํ๋๋๋ฐ, Jetpack Compose ๋ฐํ์์ ์ด ์์๋ฅผ ๋ค์ง์ด ๋ถ๋ชจ → ์์ ๋ฐฉํฅ์ผ๋ก onPreScroll()์ ์คํํด ๋๊ฐ๋ค. onPreScoll()์ ์์์ ์ค์ฒฉ ์คํฌ๋กค์์๋ผ๋ฉด ์์์ด ํ์ ์คํฌ๋กค์, ๋ถ๋ชจ์๊ฒ "ํน์ ์ด ์คํฌ๋กค๋ ์ค ์ผ๋ถ๋ฅผ ์๋งใ์๋น ๊ฐ ๊ฐ์ ธ๊ฐ๋('์๋น'ํ ๋)?"๋ผ๊ณ ๋ฌป๊ฒ ๋ง๋ ๋ค (์ฐธ๊ณ ๋ก "์๋น๋๋ค"๋ ๋ง์ด "๋ถ๋ชจ๊ฐ ์คํฌ๋กค๋๋ค"๋ ๋ป์ ์๋๋ค. ๋ถ๋ชจ๋ฅผ ์คํฌ๋กค์ํค๋ ค๋ฉด, onPreScroll() ๋ด๋ถ์์ ๋ณ๋๋ก scrollState.scrollBy()๋ฅผ ํธ์ถํ๋๋ก ํ๋ก๊ทธ๋๋จธ๊ฐ ๊ตฌํํ๋ฉด ๋๋ค).
์ค์ ์คํฌ๋กค
P2, P1, C์ onPreScroll()๋ค์ ์์๋๋ก ๊ฑฐ์น๊ณ ๋จ์ ์คํฌ๋กค๋์ ์ค์ ๋ก ์คํฌ๋กคํ๋ ๋ถ๋ถ์ด๋ค (๋, ์ค์ ๋ก ์คํฌ๋กค๋๋งํผ ์คํฌ๋กค๋์ด '์๋น'๋๋ค).
onPostScroll() ์คํ
์ค์ ์คํฌ๋กค์ ํ๊ณ ๋ ๋จ์ ๋ถ๋ถ์ ์ฒ๋ฆฌํ๊ธฐ ์ํ ํ์ดํ๋ค. '์คํฌ๋กค์ ํ๊ณ ๋จ์ ๋ถ๋ถ'์ด๋ผ๋ ๋ง์ ์ง๊ด์ ์ผ๋ก ๋ฉ๋๋์ง ์๋ ๋ง์ด๋ค. ๋ง์น '์๋ฆฌ์๋ ์์ฐ์ฑ' ๋ฐ์์ ์์ ํํ์ฒ๋ผ ๋๊ปด์ง์ง ์๋๊ฐ? ํ์ง๋ง ์คํฌ๋กค์ ํ๊ณ ๋ ์ ๋ง๋ก '๋จ๋' ๊ฒฝ์ฐ๊ฐ ์๋ค. ์ด๋ค ์ปจํ ์ด๋๋ฅผ ๋๊น์ง ์คํฌ๋กค ํ์์๋ ์ฌ์ฉ์๊ฐ ์๊ฐ๋ฝ์ ์ฌ์ ํ ์์ง์ด๊ณ ์๋ ๊ฒฝ์ฐ๋ฅผ ๋ ์ฌ๋ ค๋ณด์. ๊ทธ๋ฐ ๋ '์คํฌ๋กค์ด ๋จ๋๋ค'๋ผ๊ณ ํํํ๋ค.
#4 ์์ ํ์ธ์ฉ ์ํ ์ฑ
#4-1 ๊ฐ์
์์์ ์ค๋ช ํ ๋ด์ฉ์ด ์ ๋ง๋ก ๋ง๋์ง ํ์ธํ ์ ์๋ ์ํ ์ฑ์ ๋ง๋ค์ด๋ดค๋ค.
#4-2 UI ๋ถ๋ถ
// in MainActivity.kt
val p2Count = 1
val p1Count = 5
val cCount = 10
repeat(p2Count) { p2Index ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.background(Color.White)
.nestedScrollWithLogger("1")
.scrollableWithLogger("2")
.nestedScrollWithLogger("3")
.scrollableWithLogger("4")
.verticalScrollWithLogger("4.5")
.nestedScrollWithLogger("5")
.scrollableWithLogger("6")
.nestedScrollWithLogger("7")
.scrollableWithLogger("8")
) {
repeat(p1Count) { p1Index ->
Column(
modifier = Modifier
.padding(
start = 24.dp,
top = 24.dp,
bottom = 24.dp,
end = 96.dp
)
.fillMaxWidth()
.height(500.dp)
.background(Color.LightGray)
.nestedScrollWithLogger("9")
.scrollableWithLogger("10")
.nestedScrollWithLogger("11")
.scrollableWithLogger("12")
.verticalScrollWithLogger("12.5")
.nestedScrollWithLogger("13")
.scrollableWithLogger("14")
.nestedScrollWithLogger("15")
.scrollableWithLogger("16")
) {
repeat(cCount) { cIndex ->
Column(
modifier = Modifier
.padding(
start = 24.dp,
top = 24.dp,
bottom = 24.dp,
end = 96.dp
)
.height(300.dp)
.background(Color.Gray)
.nestedScrollWithLogger("17")
.scrollableWithLogger("18")
.nestedScrollWithLogger("19")
.scrollableWithLogger("20")
.verticalScrollWithLogger("20.5")
.nestedScrollWithLogger("21")
.scrollableWithLogger("22")
.nestedScrollWithLogger("23")
.scrollableWithLogger("24")
) {
SampleTexts()
}
}
}
}
}
}
์ ์ฝ๋์ ๊ฐ์ด ํ ์ปดํฌ์ ๋ธ์ nestedScroll()์ ๋ฌ ์๋ ์๋ค. ์ด ๊ฒฝ์ฐ, ๋จผ์ ๋ฉ์๋ ์ฒด์ด๋๋ nestedScroll()์ด ๋ถ๋ชจ๊ณ ๋ค์ชฝ์ ์ฒด์ด๋๋ nestedScroll()์ ์์์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
#4-3 scrollableWithLogger()
@Composable
@SuppressLint("ModifierFactoryUnreferencedReceiver")
private fun Modifier.scrollableWithLogger(id: String): Modifier {
return this.scrollable(
state = ScrollableState {
println("$id scrollable() available delta: $it")
0F
},
orientation = Orientation.Vertical
)
}
๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๋ scrollable() ํจ์.
#4-4 verticalScrollWithLogger()
/* println()์ด ์ข ๋ฆ๊ฒ ์ถ๋ ฅ๋๋ค
* ์ค์ ๋ก verticalScroll()์ด ๋ฆ๊ฒ ์ํ๋ ๊ฒ์ ์๋๋ค
* ScrollState.value์ ๋ณํ๋ฅผ LaunchedEffect๋ก ์ถ์ ํ๊ณ
* LaunchedEffect ๋ด์์ ๋น๋๊ธฐ์ ์ผ๋ก ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๊ธฐ์ ์์ฐจ๊ฐ ์๋ ๊ฒ์ด๋ค
* ๋ก๊ทธ ๋ฉ์์ง ์์ฒด๊ฐ, ์ค์ ์คํฌ๋กค์ด ๋๋ ์๊ฐ ์ถ๋ ฅ๋๊ฒ ๋ง๋ค์ด์ง์ง ์์๋ค๋ ์๊ธฐ๋ค
*/
@Composable
@SuppressLint("ModifierFactoryUnreferencedReceiver")
private fun Modifier.verticalScrollWithLogger(id: String): Modifier {
val scrollState = remember {
ScrollState(0)
}
LaunchedEffect(scrollState.value) {
println("$id verticalScroll()")
}
return this.verticalScroll(
state = scrollState
)
}
๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๋ verticalScroll() ํจ์.
#4-5 nestedScrollWithLogger()
@Composable
@SuppressLint("ModifierFactoryUnreferencedReceiver")
fun Modifier.nestedScrollWithLogger(id: String): Modifier {
return this.nestedScroll(NestedScrollConnectionWithSimpleLogger(id))
}
class NestedScrollConnectionWithSimpleLogger(private val id: String) : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
println("$id onPreScroll available delta: $available")
return Offset.Zero
}
override fun onPostScroll(
consumed: Offset, available: Offset, source: NestedScrollSource
): Offset {
println("$id onPostScroll available delta: $available")
return Offset.Zero
}
override suspend fun onPreFling(available: Velocity): Velocity {
//println("$id onPreFling available velocity: $available")
return Velocity.Zero
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
//println("$id onPostFling available velocity: $available")
return Velocity.Zero
}
}
๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๋ nestedScroll() ํจ์. ๊ฐ ํจ์์ ๋ฐํ๊ฐ์ NestedScrollConnection ์ธํฐํ์ด์ค์ ๊ธฐ๋ณธ๊ฐ์ด๋ค.
#4-6 ์คํฌ๋ฆฐ์ท
#4-7 ์ถ๋ ฅ ๊ฒฐ๊ณผ
1 onPreScroll available delta: Offset(0.0, -13.5)
3 onPreScroll available delta: Offset(0.0, -13.5)
5 onPreScroll available delta: Offset(0.0, -13.5)
7 onPreScroll available delta: Offset(0.0, -13.5)
9 onPreScroll available delta: Offset(0.0, -13.5)
11 onPreScroll available delta: Offset(0.0, -13.5)
13 onPreScroll available delta: Offset(0.0, -13.5)
15 onPreScroll available delta: Offset(0.0, -13.5)
17 onPreScroll available delta: Offset(0.0, -13.5)
19 onPreScroll available delta: Offset(0.0, -13.5)
21 onPreScroll available delta: Offset(0.0, -13.5)
23 onPreScroll available delta: Offset(0.0, -13.5)
24 scrollable() available delta: -13.575195
23 onPostScroll available delta: Offset(0.0, -13.5)
22 scrollable() available delta: -13.575195
21 onPostScroll available delta: Offset(0.0, -13.5)
20 scrollable() available delta: 0.0
19 onPostScroll available delta: Offset(0.0, 0.0)
18 scrollable() available delta: 0.0
17 onPostScroll available delta: Offset(0.0, 0.0)
16 scrollable() available delta: 0.0
15 onPostScroll available delta: Offset(0.0, 0.0)
14 scrollable() available delta: 0.0
13 onPostScroll available delta: Offset(0.0, 0.0)
12 scrollable() available delta: 0.0
11 onPostScroll available delta: Offset(0.0, 0.0)
10 scrollable() available delta: 0.0
9 onPostScroll available delta: Offset(0.0, 0.0)
8 scrollable() available delta: 0.0
7 onPostScroll available delta: Offset(0.0, 0.0)
6 scrollable() available delta: 0.0
5 onPostScroll available delta: Offset(0.0, 0.0)
4 scrollable() available delta: 0.0
3 onPostScroll available delta: Offset(0.0, 0.0)
2 scrollable() available delta: 0.0
1 onPostScroll available delta: Offset(0.0, 0.0)
20.5 verticalScroll()
Child๋ฅผ ์์ฃผ ์ด์ง๋ง ์คํฌ๋กคํ์ ๋ ์ถ๋ ฅ๋๋ ๋ก๊ทธ ๋ฉ์์ง๋ค. verticalScroll์ ๋ก๊ทธ ๋ฉ์์ง๊ฐ ์ข ๋ฆ๊ฒ ๋ฌ๋ค. ์ค์ ๋ก verticalScroll()์ด ๋ฆ๊ฒ ์ํ๋ ๊ฒ์ ์๋๋ค. ScrollState.value์ ๋ณํ๋ฅผ LaunchedEffect๋ก ์ถ์ ํ๊ณ , LaunchedEffect ๋ด์์ ๋น๋๊ธฐ์ ์ผ๋ก ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๊ธฐ์ ์์ฐจ๊ฐ ์๋ ๊ฒ์ด๋ค. ๋ก๊ทธ ๋ฉ์์ง ์์ฒด๊ฐ, ์ค์ ์คํฌ๋กค์ด ๋๋ ์๊ฐ ์ถ๋ ฅ๋๊ฒ ๋ง๋ค์ด์ง์ง ์์๋ค๋ ์๊ธฐ๋ค.
#4-8 ์ ์ฒด ์์ค์ฝ๋
android-practice/pointer-input/ScrollModifierCallOrder at master · Kanmanemone/android-practice
Contribute to Kanmanemone/android-practice development by creating an account on GitHub.
github.com