clickable의 기본 클릭 효과 살펴보기
이전 글에서 clickable의 기본 클릭 효과가 매우 어색한 것을 보았다. 이것이 왜 어색하게 느껴졌는지 구현체를 보면서 살펴보도록 하자. 만약 우리가 clickable { } 형태로 클릭 이벤트를 처리한다면 이 내부에서는 DefaultDebugIndication이란 클릭 효과를 사용한다. 그리고 이 클릭 효과는 아래와 같이 구현되어 있다.
private object DefaultDebugIndication : Indication {
private class DefaultDebugIndicationInstance(
private val isPressed: State<Boolean>,
private val isHovered: State<Boolean>,
private val isFocused: State<Boolean>,
) : IndicationInstance {
override fun ContentDrawScope.drawIndication() {
drawContent()
if (isPressed.value) {
drawRect(color = Color.Black.copy(alpha = 0.3f), size = size)
} else if (isHovered.value || isFocused.value) {
drawRect(color = Color.Black.copy(alpha = 0.1f), size = size)
}
}
}
@Composable
override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
val isPressed = interactionSource.collectIsPressedAsState()
val isHovered = interactionSource.collectIsHoveredAsState()
val isFocused = interactionSource.collectIsFocusedAsState()
return remember(interactionSource) {
DefaultDebugIndicationInstance(isPressed, isHovered, isFocused)
}
}
}
위 코드를 보면 다음의 3가지 이벤트를 감지한다. interactionSource로부터 collectIsPressedAsState를 통해 눌림 효과를 감지하고, collectIsHoveredAsState를 통해 왔다갔다 하는 효과를 감지하며, collectAsFocusedAsState를 통해 컴포저블이 포커싱 되었는지에 대해 감지한다.
그리고 만약 눌림 효과가 발생했을 때는 drawRect(color = Color.Black.copy(alpha = 0.3f), size = size) 를 통해 alpha(불투명도)가 0.3인 검은색 사각형을 눌린 컴포저블 위에 그리고, hovered 되거나 포커스 되었을 때는 alpha가 0.1인 검은색 사각형을 해당 이벤트가 발생한 컴포저블 위에 그리는 형태로 Indication을 만든다.
이로 인해 아래와 같이 뚝뚝 끊기는 것 같은 효과가 나타나게 된다.
이를 해결하기 위해서는 불투명도(alpha)를 줄여 Indication의 색상을 조금 더 연하게 하거나 눌렸을 때 alpha값이 서서히 변화하도록 애니메이션을 주어야 한다.
애니메이션까지 다루기에는 글이 복잡해지므로, 이번 글에서는 새로운 Indication을 간단히 구현하는 방법에 대해서만 살펴본다.
clickable에 클릭 효과 커스텀 하게 설정하기
clickable에 클릭 효과를 커스텀하게 설정하기 위해서는 다음 clickable 함수를 사용해야 한다. 이 clickable 함수에서 중요한 파라미터는 interationSource, indication, onClick 세가지이다.
fun Modifier.clickable(
interactionSource: MutableInteractionSource,
indication: Indication?,
enabled: Boolean = true,
onClickLabel: String? = null,
role: Role? = null,
onClick: () -> Unit
)
interactionSource는 유저의 이벤트를 받아오는데 필요한 객체이며, indication은 클릭 효과를 만들 수 있는 객체, onClick은 클릭이 일어났을 때 동작을 설정할 수 있는 파라미터이다.
Indication 상속해 커스텀 클릭 효과 만들기
먼저 CustomIndication부터 다음과 같이 만든다.
object CustomIndication : Indication {
private class DefaultDebugIndicationInstance(
private val isPressed: State<Boolean>,
) : IndicationInstance {
override fun ContentDrawScope.drawIndication() {
drawContent()
if (isPressed.value) {
drawRect(color = Color.Gray.copy(alpha = 0.1f), size = size)
}
}
}
@Composable
override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
val isPressed = interactionSource.collectIsPressedAsState()
return remember(interactionSource) {
DefaultDebugIndicationInstance(isPressed)
}
}
}
이 CustomIndication은 기존 DefaultDebugIndication에 비해 클릭 이벤트를 처리하는 성능이 좋다 이유는 유저가 화면을 누른 이벤트만 처리하기 때문이다. 또한 화면이 눌렸을 때, 위에 alpha값이 0.1인 회색(Gray)을 그려서 이전보다 클릭 효과가 자연스러워진다.
CustomIndication clickable에 적용하기
Custom Indication은 Clickable에 다음과 같이 적용될 수 있다.
Box(
modifier = Modifier
.size(300.dp)
.background(Color.Blue)
.clickable(
interactionSource = remember{ MutableInteractionSource() },
indication = CustomIndication
) {
Toast.makeText(baseContext, "조세영의 Kotlin World", Toast.LENGTH_SHORT).show()
}
)
여기서 interactionSource는 한 번만 만들어져야 하고, Recompose될 때마다 매번 새로 만들어지면 안되므로 remember을 통해 한번 만들어지도록 한다. 이후 indication에 위해서 만든 CustomIndication 객체를 넘긴 후 클릭 이벤트를 설정한다. 그려면 클릭 이벤트는 다음과 같이 보여진다.
이전 클릭 효과에 비해 매우 자연스러워 진 것을 볼 수 있다.
위와 같이 Indication을 상속하는 객체를 만들어 클릭 효과를 자연스럽게 만들 수 있는 다양한 방법이 존재한다. 한 번 시도해보자.