#1 ๊ฐ์
๋์ ์ดํดํ๊ธฐ | Jetpack Compose | Android Developers
์ด ํ์ด์ง๋ Cloud Translation API๋ฅผ ํตํด ๋ฒ์ญ๋์์ต๋๋ค. ๋์ ์ดํดํ๊ธฐ ์ปฌ๋ ์ ์ ์ฌ์ฉํด ์ ๋ฆฌํ๊ธฐ ๋ด ํ๊ฒฝ์ค์ ์ ๊ธฐ์ค์ผ๋ก ์ฝํ ์ธ ๋ฅผ ์ ์ฅํ๊ณ ๋ถ๋ฅํ์ธ์. ์ดํดํด์ผ ํ ๋ช ๊ฐ์ง ์ฉ์ด์ ๊ฐ๋ ์ด ์
developer.android.com
์ ๊ณต์ ๋ฌธ์๋ฅผ ๋์ ์ธ์ด๋ก ์ ๋ฆฌํ๊ณ , ์ํ ์ฑ์ ๋ง๋ค์ด๋ดค๋ค.
#2 Pointer
#2-1 ํฌ์ธํฐ๋ ํ๋์จ์ด๋ค
ํ๋ฉด์ ํน์ ์ขํ๋ฅผ ์ฐ์(point) ์ ์๋ ์ฌ๋ฌผ(ํ๋์จ์ด)์ ์๋ฏธํ๋ค. ์ผ๋ฐ์ ์ผ๋ก ์๊ฐ๋ฝ์ ์๋ฏธํ๋ค. ํน์ ๊ฐค๋ญ์์ Sํ์ด ํด๋น๋๋ค. ํค๋ณด๋๋ ํฐ์น ์คํฌ๋ฆฐ์ ์ด๋ ์ขํ๋ฅผ ๊ฐ๋ฆฌํฌ(point) ์ ์์ผ๋ฏ๋ก ํฌ์ธํฐ๊ฐ ์๋๋ค.
#2-2 PointerType
package androidx.compose.ui.input.pointer
...
/**
* The device type that produces a [PointerInputChange], such as a mouse or stylus.
*/
@kotlin.jvm.JvmInline
value class PointerType private constructor(private val value: Int) {
override fun toString(): String = when (value) {
1 -> "Touch"
2 -> "Mouse"
3 -> "Stylus"
4 -> "Eraser"
else -> "Unknown"
}
companion object {
/**
* An unknown device type or the device type isn't relevant.
*/
val Unknown = PointerType(0)
/**
* Touch (finger) input.
*/
val Touch = PointerType(1)
/**
* A mouse pointer.
*/
val Mouse = PointerType(2)
/**
* A stylus.
*/
val Stylus = PointerType(3)
/**
* An eraser or an inverted stylus.
*/
val Eraser = PointerType(4)
}
}
...
์๋๋ก์ด๋์์๋ ํฌ์ธํฐ์ ์ ํ์ด ์์ 5๊ฐ์ง๋ก ๋ถ๋ฅ๋๋ค. PointerType์ ํ์ ํ PointerInputChange์ ์ธ์๋ก์ ์ ๋ฌ๋๋ค.
#3 PointerInputChange
#3-1 ์ฝ๋
package androidx.compose.ui.input.pointer
...
@Immutable
class PointerInputChange(
val id: PointerId, // ์ธ์คํด์ค ์๋ณ์ ์ํ ID. ์ค๋ณต์ด ๊ฐ๋ฅํ๋ค (์ํ ์ฑ์์ ํ์ธ ๊ฐ๋ฅ)
val uptimeMillis: Long, // PointerInputChange ์ธ์คํด์ค๊ฐ ๋ฐ์ํ ์๊ฐ
val position: Offset, // 'ํ๋ฉด ์์ ์ขํ'๊ฐ ์๋์ ์ฃผ์. ์ค๋ช
์ฐธ์กฐ
val pressed: Boolean, // ํฐ์น ์คํฌ๋ฆฐ์ ๋๋ฅด๋ PointerInputChange์ธ์ง, ๋ผ๋ PointerInputChange์ธ์ง ๋ช
์
val pressure: Float, // ํฌ์ธํฐ๊ฐ ํ๋ฉด์ ๋๋ฅด๋ ์๋ ฅ
val previousUptimeMillis: Long, // ์ง์ PointerInputChange ์ธ์คํด์ค๊ฐ ๋ฐ์ํ ์๊ฐ
val previousPosition: Offset, // ์ง์ PointerInputChange์ position
val previousPressed: Boolean, // ์ง์ PointerInputChange์ pressed
isInitiallyConsumed: Boolean, // ๋ค๋ฅธ ๊ณณ์์ ์ด๋ฏธ ์จ๋จน์ PointerInputChange์์ ๋ช
์
val type: PointerType = PointerType.Touch, // #2-2 ์ฐธ์กฐ
val scrollDelta: Offset = Offset.Zero // ๋ง์ฐ์ค ํ ์ ๊ตด๋ฆฐ ์ ๋๊ฐ
) {
...
}
...
ํฌ์ธํฐ(ํ๋์จ์ด)์ ์์ง์์ ํฐ์น ์คํฌ๋ฆฐ์ผ๋ก ๊ธฐ๋กํ์ฌ, ๋์งํธ๋ผ์ด์ง(์ํํธ์จ์ด)ํ ํด๋์ค๋ค. ๋ฐ๋ผ์, ์๋๋ก์ด๋ ๊ธฐ๊ธฐ์ ํฐ์น ์คํฌ๋ฆฐ์ ์ฌ์ฉํ ๋๋ง๋ค ๋ฌด์ํ ๋ง์ PointerInputChange ์ธ์คํด์ค๊ฐ ์์ฑ๋๋ค.
#3-2 position์ ๋ํ ์ดํด
์ฐ์ ๋ชจ๋ position(์์น)์ ๊ธฐ์ค์ ๋งจ ์ผ์ชฝ ๋งจ ์๋จ์ด๋ค. ์ฆ, ๋งจ โ์ชฝ์ ์ขํ๊ฐ (0, 0)์ด๋ค.
๋, ๊ธฐ์ค์ ์ค๋งํธํฐ ํ๋ฉด์ด ์๋๋ค! ํ๋ฉด ์ ์ปดํฌ๋ํธ๋ค์ด ๊ฐ๊ฐ ๊ณ ์ ํ ๊ธฐ์ค์ ๊ฐ์ง๊ฒ ๋๋ค. ๋ฐ๋ผ์, ์ ์ค๋งํธํฐ ํ๋ฉด ๋์๋์์๋ ๊ธฐ์ค์ด 4๊ฐ ์กด์ฌํ ๊ฒ์ด๋ค (์ 4๊ฐ์ ์ปดํฌ๋ํธ๋ค์ ๋ด๋ ๋ ์ด์์ ์ปดํฌ๋ํธ์ธ Box()๊น์ง ํฌํจํ๋ค๋ฉด 5๊ฐ).
์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ 1๋ฒ Text()์ B ๋ถ๋ถ์ ํฐ์นํ๋ฉด PointerInputChange.position์ (2, 2)์ด ๋๋ค. 1๋ฒ Text()์ ๊ธฐ์ค์ธ A์์ ์ค๋ฅธ์ชฝ์ผ๋ก 2๋งํผ ์๋์ชฝ์ผ๋ก 2๋งํผ ๋จ์ด์ ธ์๊ธฐ ๋๋ฌธ์ด๋ค. D๋? A์์ ์ค๋ฅธ์ชฝ์ผ๋ก -1๋งํผ ์๋์ชฝ์ผ๋ก -1๋งํผ ๋จ์ด์ ธ ์์ผ๋ฏ๋ก (= ์ผ์ชฝ์ผ๋ก 1๋งํผ ์์ชฝ์ผ๋ก 1๋งํผ ๋จ์ด์ ธ ์์ผ๋ฏ๋ก) (-1, -1)์ด ๋๋ค.
๊ธฐ์ค์ ์ปดํฌ๋ํธ์ ๊ฐฏ์๋งํผ ์กด์ฌํ๋ค๊ณ ํ๋ค. C์ position์, 1๋ฒ Text()๊ฐ ๊ธฐ์ค์ด๋ผ๋ฉด (12, 7)์ด๊ณ 2๋ฒ Button()์ด ๊ธฐ์ค์ด๋ผ๋ฉด (6, 0)์ด ๋๋ค.
๊ทธ๋ฐ๋ฐ C๋ D๋ 1๋ฒ Text()์ ๋ฐ๊นฅ ์์ญ์ด๋ค. 1๋ฒ Text()์ ์์ญ์ด ์๋๋ฐ๋ ์ด์งธ์ PointerInputChange ์ธ์คํด์ค๊ฐ ์์ฑ๋ ์ ์๋๊ฐ? ๋ฐ๋ก, ์ฌ์ฉ์๊ฐ ์ปดํฌ๋ํธ ๋ฐ์์ ํฐ์นํ ํ ์๊ฐ๋ฝ์ ๋๋๊ทธํ์ฌ ์ปดํฌ๋ํธ ์์ผ๋ก ๋ค์ด์ค๊ฑฐ๋, ๋ฐ๋๋ก ์ปดํฌ๋ํธ ์์์ ํฐ์นํ ํ ์๊ฐ๋ฝ์ ์ปดํฌ๋ํธ ๋ฐ๊นฅ ์์ญ์ผ๋ก์ผ๋ก ๋๋๊ทธํ ์๋ ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ PointerInputChange.position ๊ฐ์ด ์์(D)์ด๊ฑฐ๋ ์ปดํฌ๋ํธ ํฌ๊ธฐ๋ณด๋ค ํฐ ๊ฐ(C)์ด ๋ ์ ์๋ ๊ฒ์ด๋ค.
#4 PointerEvent
#4-1 ์ฝ๋
package androidx.compose.ui.input.pointer
...
/**
* Describes a pointer input change event that has occurred at a particular point in time.
*/
expect class PointerEvent internal constructor(
changes: List<PointerInputChange>,
internalPointerEvent: InternalPointerEvent?
) {
/**
* @param changes The changes.
*/
constructor(changes: List<PointerInputChange>)
/**
* The changes.
*/
val changes: List<PointerInputChange>
/**
* The state of buttons (e.g. mouse or stylus buttons) during this event.
*/
val buttons: PointerButtons
/**
* The state of modifier keys during this event.
*/
val keyboardModifiers: PointerKeyboardModifiers
/**
* The primary reason the [PointerEvent] was sent.
*/
var type: PointerEventType
internal set
}
...
(์ ์ฒด ์์ค์ฝ๋)
PointerEvent๋ PointerInputChange์ ๋ฆฌ์คํธ๋ฅผ ๋ณด์ ํ๋ฉฐ, ์กด์ฌ ๋ชฉ์ ์ PointerInputChange์ ๋์ผํ๋ค. ์ฆ, ํฌ์ธํฐ์ ์์ง์์ ๊ธฐ๋กํ๊ธฐ ์ํ ํด๋์ค๋ค. PointerInputChange์ ๋ฆฌ์คํธ๋ฅผ ๋ณด์ ํ๋ ์ด์ ๋ ๋ฉํฐ ํฐ์น์ ์กด์ฌ ๋๋ฌธ์ด๋ค. ์๊ฐ๋ฝ 1๊ฐ๋ก๋ง ํฐ์นํ๋ค๋ฉด List<PointerInputChange>์๋ ์์๊ฐ ํ๋๋ง ์กด์ฌํ๊ฒ ๋์ง๋ง, ์๊ฐ๋ฝ 2๊ฐ ์ด์์ ์ฌ์ฉํด ๋์์ ํฐ์นํ๋ ๊ฒฝ์ฐ์๋ ์์๊ฐ 2๊ฐ ์ด์ ์กด์ฌํ๊ฒ ๋๋ค.
#4-2 Modifier.pointerInput()
public fun Modifier.pointerInput(
key1: Any?,
block: suspend PointerInputScope.() -> Unit
): Modifier
pointerInput()์ PointerInputScope๋ฅผ ๋ณด์ ํ๋ค. PointerInputScope ์์ญ์๋ ํ์ ํ awaitPointerEventScope()๋ ๋ค์ ๊ฒ์๊ธ์์ ์ค๋ช
ํ detectTapGesture() ๋ฑ์ ํตํด, PointerEvent๋ฅผ ๊ฐ์งํ๊ณ ์ฒ๋ฆฌํ ์ ์๋ค. ํ ๋ง๋๋ก, PointEvent ๊ด๋ จ ํ๋ก๊ทธ๋๋ฐ์ ์ํ ๊ธฐ์ด ํ ๋๋ผ ํ ์ ์๊ฒ ๋ค.
PointerInputScope()์ suspend ํค์๋๊ฐ ๋ถ์ด ์๋ ๊ฑธ ํ์ธํ ์ ์๋ค. ์ฆ, PointerInputScope() ์์ ๋น๋๊ธฐ ์ฝ๋์ ์์ญ์ด ๋์ด Coroutines ์ฝ๋๋ค์ ์์น์ํฌ ์ ์๊ฒ ๋๋ค๋ ์๊ธฐ๋ค. ๋, ๊ทธ๋์ผ๋ง(๋น๋๊ธฐ ์ฝ๋์ ์์ญ์ด์ด์ผ๋ง) ํ๋ค. PointerEvent ์ธ์คํด์ค๋ ๋ฌด์ํ ๋ง์ด ์์ฑ๋์ด ์์์ง๋ฏ์ด pointerInput()์๊ฒ ์ ๋ฌ๋ ํ
๋ฐ, ์ด๋ฅผ ๋๊ธฐ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ๋ ค๊ณ ํ๋ฉด ํ๋ฉด์ด ์์ฒญ๋๊ฒ ๋ฒ๋ฒ
์ผ ๊ฒ์ด๋ค.
pointerInput()์ ์ ๋ฌํ ์ ์๋ ์ธ์ ์ค์ "key"๋ผ๋ ์ด๋ฆ์ ์ธ์๊ฐ ์กด์ฌํ๋ค. "key" ์๋ฆฌ์๋ ์ฃผ๋ก ๊ทธ ๊ฐ์ด ๋ณํ ์ ์๋ ๊ฐ์ฒด๊ฐ ๋ค์ด๊ฐ๋ค. ๊ทธ๋ฆฌ๊ณ Jetpack Compose์์๋ ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌ๋ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด Recomposition์ด ์ผ์ด๋๋ค (๊ผญ State.value๊ฐ ๋ณํด์ผ๋ง Recomposition์ด ์ผ์ด๋๋ ๊ฒ ์๋๋ค) . ๋ค์ ๋งํด ์ด๋ "key" ๊ฐ์ ๋ณํ์ ์ํ Compose Recomposition ๋, pointerInput()์ด ์ฌ์คํ๋๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค (๋ฌผ๋ก ์์์ ์ผ๋ก ์คํ๋ ๊ฒ์ด๋ฏ๋ก ํ๋ก๊ทธ๋๋จธ๊ฐ ํฌ๊ฒ ์ ๊ฒฝ์ธ ๋ถ๋ถ์ ์๋ ๊ฒ์ด๋ค).
pointerInput()์ Modifier.pointerInput( ... ).pointerInput( ... ).pointerInput( ... )์ฒ๋ผ ์ฌ๋ฌ ๋ฒ ๋ฉ์๋ ์ฒด์ด๋ํ ์๋ ์๋ค (#4-5 ์ฐธ์กฐ).
#4-3 AwaitPointerEventScope()
public abstract suspend fun <R> awaitPointerEventScope(
block: suspend AwaitPointerEventScope.() -> R
): R
pointerInput() ๋ด๋ถ์ ๋ฃ์ ์ ์๋ CoroutineScope()๋ค. PointerInputScope()๋ ์ด๋ฏธ ๋น๋๊ธฐ ์ฝ๋์ ์์ญ์ธ๋ฐ ๊ทธ ๋ด๋ถ์ ๋ ๊ตณ์ด ์ด CoroutineScope๋ฅผ ๋ฃ๋ ์ด์ ๋, AwaitPointerEventScope()๊ฐ ์ผ๋ฐ์ ์ธ CoroutineScope()์๋ ๋ฌ๋ฆฌ ์ฌ๋ฌ PointEvent๋ฅผ ๊ฐ์งํ๊ณ ์ ์ฝ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ ๋ฉ์๋๋ค์ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ด๋ค. ์ผ๋ฐ์ ์ธ CoroutineScope()๋ง์ ํ์ฉํ๋ฉด ๊ต์ฅํ ๋ณต์กํ ๋ก์ง์ ์ง์ผํ ํ
๋ฐ ๊ทธ๋ฌํ ์๊ณ ๋ฅผ ๋ ์ ์๋ ๊ฒ์ด๋ค.
#4-4 AwaitPointerEventScope()์ ๋ฉ์๋๋ค
๊ฐ์ฅ ๋ํ์ ์ธ ๋ฉ์๋: awaitPointerEvent()
suspend fun awaitPointerEvent(pass: PointerEventPass = PointerEventPass.Main): PointerEvent
PointerEvent๊ฐ ์ผ์ด๋ ๋๊น์ง ๋๊ธฐ(suspend)ํ๋ค๊ฐ, PointerEvent๊ฐ ๋ฐ์ํ๋ฉด ๋ฐํํ๋ค.
๋ค๋ฅธ ๋ฉ์๋๋ค
[Android] Pointer input - AwaitPointerEventScope()์ ๋ฉ์๋๋ค
#1 ๊ฐ์#1-1 ์ด์ ๊ฒ์๊ธ [Android] Pointer input - PointerInputChange, PointerEvent#1 ๊ฐ์ ๋์ ์ดํดํ๊ธฐ | Jetpack Compose | Android Developers์ด ํ์ด์ง๋ Cloud Translation API๋ฅผ ํตํด ๋ฒ์ญ๋์์ต๋๋ค. ๋์ ์ดํดํ
kenel.tistory.com
๋๋จธ์ง ๋ฉ์๋๋ค์ ์ ๊ฒ์๊ธ์์ ์ด์ด ์ค๋ช ํ๋ค.
#4-5 ๋ฉ์๋ ์ฒด์ด๋๊ณผ '์๋น'
pointerInput()์ Modifier.pointerInput( ... ).pointerInput( ... ).pointerInput( ... )์ฒ๋ผ ์ฌ๋ฌ ๋ฒ ๋ฉ์๋ ์ฒด์ด๋ํ ์๋ ์๋ค. ์๋์ ์ฝ๋๋ฅผ ๋ณด์.
Modifier
.pointerInput(Unit) {
while (true) {
val event = awaitPointerEventScope { awaitPointerEvent() }
event.changes.forEach { change ->
if(!change.isConsumed) {
println("์ฒซ๋ฒ์งธ pointerInput์์ ์ฒ๋ฆฌ: ${change.id}")
}
}
}
}
.pointerInput(Unit) {
while (true) {
val event = awaitPointerEventScope { awaitPointerEvent() }
event.changes.forEach { change ->
if(!change.isConsumed) {
println("๋๋ฒ์งธ pointerInput์์ ์ฒ๋ฆฌ: ${change.id}")
}
}
}
}
.pointerInput(Unit) {
while (true) {
val event = awaitPointerEventScope { awaitPointerEvent() }
event.changes.forEach { change ->
if(!change.isConsumed) {
println("์ธ๋ฒ์งธ pointerInput์์ ์ฒ๋ฆฌ: ${change.id}")
}
}
event.changes.forEach { it.consume() } // ์ด๋ฒคํธ ์๋น
}
}
.pointerInput(Unit) {
while (true) {
val event = awaitPointerEventScope { awaitPointerEvent() }
event.changes.forEach { change ->
if(!change.isConsumed) {
println("๋ค๋ฒ์งธ pointerInput์์ ์ฒ๋ฆฌ: ${change.id}")
}
}
}
}
while(true) { ... }
์ฐ์ ์ฝ๋ ์์ ์๋ while(true) { ... }์ ๋ํด ์ค๋ช
ํ๊ณ 2๊ฐ ์ด์์ ๋ฉ์๋ ์ฒด์ด๋ ์ค๋ช
์ผ๋ก ๋์ด๊ฐ๊ฒ ๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ฐ๋ฆฌ๋ PointerEvent๋ฅผ ํ ๋ฒ๋ง ๊ฐ์งํ๊ณ ๋ง์ง ์์ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ awaitEventScope() ๋ฐ์๋ฅผ while(true) { ... }๋ก ๊ฐ์ธ, ์๋ก ์์ฑ๋ PointerEvent ์ธ์คํด์ค์ ๊ณ์ ๋๊ธฐํ๊ฒ๋ ๋ง๋ค์ด์ค ๊ฒ์ด๋ค.
2๊ฐ ์ด์์ ๋ฉ์๋ ์ฒด์ด๋, ์ด๋ฒคํธ ์ ํ ๋ฐฉํฅ
pointerInput()์ด ์ค๋ณต ๋ฉ์๋ ์ฒด์ด๋๋๋ฉด, PointerEvent๋ pointerInput()์ด ๋ฉ์๋ ์ฒด์ด๋๋ ์์์ ์ญ๋ฐฉํฅ์ผ๋ก ์ ๋ฌ๋๋ค. ์ฆ println()์ ์ถ๋ ฅ์ "๋ค๋ฒ์งธ pointerInput์์ ... ์ธ๋ฒ์งธ pointerInput์์ ..."๊ฐ ๋ ํ
๋ค. ํ๋, ์ ๊ฐ์๊ธฐ ์ญ๋ฐฉํฅ์ธ๊ฐ?
Modifier์ ๋ฉ์๋ ์ฒด์ด๋์ ๋ ๋จผ์ ์ฒด์ด๋๋ ์ชฝ์ผ์๋ก '๋ถ๋ชจ'๋ผ ํ ์ ์๋ค. ์ด '๋ถ๋ชจ'๋ UI ์ค๊ณ์์ผ๋ก '์์'๋ณด๋ค ์ฐ์ ํ์ง๋ง, ์ด๋ฒคํธ ์ ํ์์๋งํผ์ '์์'์ ์ฐ์ ์ํจ๋ค. ์๋ํ๋ฉด Jetpack Compose๋ UI์ ๊ดํ ๋ฐํ์์ด๊ธฐ์, ๋ถ๋ชจ ์ชฝ์ผ์๋ก ์ปจํ ์ด๋ ์ญํ ์ ์ํํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๊ธฐ ๋๋ฌธ์ด๋ค. ์ปจํ ์ด๋(๋ถ๋ชจ)๋ ๊ทธ ์ ์์๋ค(์์)์ ์ด๋ฒคํธ์ ๋ํด์ ์ฐ์ ์์๋ฅผ ๊ฐ์ง ํ์๊ฐ ์๋ค. ์ปจํ ์ด๋๊ฐ ๋จผ์ ์ด๋ฒคํธ๋ฅผ (ํ์ ํ ) '์๋น'ํด๋ฒ๋ฆฌ๋ฉด ๋ด๋ถ ์์ ์์๋ค์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ๊ธฐํ๋ฅผ ์์ด๋ฒ๋ฆฌ๋ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ ์ด๋ฒคํธ '์๋น'์ ๊ดํด์๋, ๊ทธ ์ ํ ์์๊ฐ Modifier ๋ฉ์๋ ์ฒด์ด๋์ ์ญ๋ฐฉํฅ์ด ๋๋๋ก (์๋๋ก์ด๋ OS ๊ฐ๋ฐํ์ ์๋์ ์ํด) ๋ง๋ค์ด์ง ๊ฒ์ด๋ค.
'์๋น (consume)'
"๋๋ฒ์งธ pointerInput์์ ... ์ฒซ๋ฒ์งธ pointerInput์์ ..."๋ ์ถ๋ ฅ๋์ง ์์ ๊ฒ์ด๋ค. ์๋ํ๋ฉด ์ธ๋ฒ์งธ pointerInput()์์ ์ด๋ฒคํธ๋ฅผ '์๋น'ํ๊ธฐ ๋๋ฌธ์ด๋ค. '์๋น'๋ #3-1์ ์๋ PointerInputChange์ ํ๋กํผํฐ ๋ชฉ๋ก์์ ์ด๋ฏธ ํ ๋ฒ ๋ดค์๋ค. ์ด๋ค PointerInputChange์ ๋ํด ๋ด๊ฐ ์๋ํ ๋์์ ์ ๋ถ ์ฒ๋ฆฌํ๋ค๋ฉด, ๋ ์ด์ ์ด PointerInputChange๋ ํ์ฉ ๊ฐ์น๊ฐ ์๋ค. ์๋, ํ์ฉ ๊ฐ์น๊ฐ ์๋ ๊ฑธ ๋์ด์ ์ ์ฌ์ ์๋ฌ์ ์์ธ์ด ๋๋ค. ์ด๋ฐ ๊ฒฝ์ฐ PointerInputChange์ ํ๋ก๊ทธ๋๋จธ๊ฐ '์๋นํจ'์ด๋ผ๋ ๋ฑ์ง๋ฅผ ๋ถ์ด๊ณ , ์ ์ฝ๋์์์ฒ๋ผ if ๋ถ๊ธฐ๋ฌธ์ผ๋ก ์ ์ดํ ์ ์๋ค.
#5 ํฐ์น ๋ก๊ทธ ํ์ธ ์ฑ
#5-1 ๊ฐ์
PointerEvent ๋ฐ ๊ทธ ์์ PointerInputChange๋ฅผ Log ๋ฉ์์ง๋ก ๋ฐ์ ์ ์๋ ์ฑ์ ๋ง๋ค์๋ค.
#5-2 ํต์ฌ ์ฝ๋
Column(
modifier = Modifier
.fillMaxWidth()
.padding(innerPadding)
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally
) {
repeat(20) {
val componentName = "Item ${it + 1}"
Text(
text = componentName,
modifier = Modifier
.background(Color.LightGray)
.pointerInput(Unit) {
awaitPointerEventScope {
while (true) {
val event = awaitPointerEvent() // suspend ํจ์
Log.d(componentName, "PointerEvent.type = ${event.type}")
event.changes.forEach { change ->
Log.d(
componentName,
"""
PointerInputChange(
id = ${change.id},
uptimeMillis = ${change.uptimeMillis},
position = ${change.position},
pressed = ${change.pressed},
pressure = ${change.pressure},
previousUptimeMillis = ${change.previousUptimeMillis},
previousPosition = ${change.previousPosition},
previousPressed = ${change.previousPressed},
isInitiallyConsumed = ${change.isConsumed},
type = ${change.type},
scrollDelta = ${change.scrollDelta}
)
""".trimIndent()
)
}
}
}
},
fontSize = 48.sp,
)
Spacer(modifier = Modifier.height(48.dp))
}
}
PointerInputChange ๋ฐ PointerEvent๋ฅผ Log๋ฅผ ํตํด ํ์ธํ ์ ์๋ ์ฝ๋๋ค.
#5-3 ์์ฑ๋ ์ฑ
android-practice/pointer-input/PointerInputChangeLogger at master · Kanmanemone/android-practice
Contribute to Kanmanemone/android-practice development by creating an account on GitHub.
github.com
#5-2์ ์ฝ๋๋ฅผ ์ ์ฉํ ์ฑ์ด๋ค.
#6 ์์ฝ
์๊ฐ๋ฝ(ํ๋์จ์ด)์ ์์ง์์, PointerInputChange ๋๋ PointerEvent๋ก ์ฝ์ด๋ผ ์ ์๋ค.
#7 ์ด์ด์ง๋ ๊ธ
[Android] Pointer input - Gesture
#1 ๊ฐ์#1-1 ์ด์ ๊ฒ์๊ธ [Android] Pointer input - PointerInputChange, PointerEvent#1 ๊ฐ์ ๋์ ์ดํดํ๊ธฐ | Jetpack Compose | Android Developers์ด ํ์ด์ง๋ Cloud Translation API๋ฅผ ํตํด ๋ฒ์ญ๋์์ต๋๋ค. ๋์ ์ดํดํ
kenel.tistory.com
'๊นจ์ ๊ฐ๋ ๐ > Android' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android] Pointer input - Gesture (0) | 2025.02.08 |
---|---|
[Android] Pointer input - AwaitPointerEventScope()์ ๋ฉ์๋๋ค (0) | 2025.02.08 |
[Android] Jetpack Compose - Navigation์ Destination ๊ฐ ๋ฐ์ดํฐ ์ ๋ฌ (NavBackStackEntry. (0) | 2024.09.13 |
[Android] Jetpack Compose - Navigation ๊ธฐ์ด (0) | 2024.09.12 |
[Android] Jetpack Compose - ๊ฐ์ฒด ์งํฅ์ UI ๋ ์ด์ด ์ค๊ณ (0) | 2024.09.11 |