시작하며
이 글에서는 ripple 효과를 일으키는 요인들과 ripple 효과를 없애는 방법들을 알아본다.
clickable 분석
fun Modifier.clickable(
interactionSource: MutableInteractionSource?,
indication: Indication?,
enabled: Boolean = true,
onClickLabel: String? = null,
role: Role? = null,
onClick: () -> Unit
) = clickableWithIndicationIfNeeded(
interactionSource = interactionSource,
indication = indication
) { intSource, indicationNodeFactory ->
ClickableElement(
interactionSource = intSource,
indicationNodeFactory = indicationNodeFactory,
enabled = enabled,
onClickLabel = onClickLabel,
role = role,
onClick = onClick
)
}
오늘의 주 목적인 ripple 효과를 제거하는데 관여하는 것만 살펴보면 interactionSource와 indication이 있고 나머지는 관여하지 않는 것으로 보인다. 그렇다면 이 두 가지를 변경해 준다면 ripple 효과를 제거할 수 있는 것이다.
- InteractionSource
먼저 interactionSource를 변경하는 방법이다. 형식인 MutableInteractionSource를 클릭해 알아보면
@Stable
interface MutableInteractionSource : InteractionSource {
suspend fun emit(interaction: Interaction)
fun tryEmit(interaction: Interaction): Boolean
}
fun MutableInteractionSource(): MutableInteractionSource = MutableInteractionSourceImpl()
@Stable
private class MutableInteractionSourceImpl : MutableInteractionSource {
override val interactions = MutableSharedFlow<Interaction>(
extraBufferCapacity = 16,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
override suspend fun emit(interaction: Interaction) {
interactions.emit(interaction)
}
override fun tryEmit(interaction: Interaction): Boolean {
return interactions.tryEmit(interaction)
}
}
emit과 tryEmit 함수, MutableSharedFlow<Interaction> 형식의 interactions를 내장하고 있다.
• emit은 상호작용이 성공했다면 ripple 효과 같은 피드백을 발생시킨다.
• tryEmit은 상호작용을 기록하는 MutableSharedFlow에 상호작용을 전달하는 역할을 한다.
• MutableSharedFlow<Interaction>은 전달받은 상호작용을 기록해 둔다.
emit의 호출이 성공하면 화면에 ripple 효과 같은 피드백이 나타나기에 이를 토대로 ripple 효과를 없애는 MutableInteractionSource를 만들어보자.
class NoRippleInteractionSource : MutableInteractionSource {
override val interactions = emptyFlow<Interaction>()
override suspend fun emit(interaction: Interaction) { }
override fun tryEmit(interaction: Interaction): Boolean = true
}
emit을 빈 함수로 만들어 상호작용에 대한 피드백을 없애고 상호작용을 Flow에 추가하는 tryEmit은 반환값을 true로 고정한다.
- Indication
이번에는 Indication을 변경하는 방법이다. indication의 설명을 보면 다음과 같다.
indication to be shown when modified element is pressed. By default, indication from LocalIndication will be used. Pass null to show no indication, or current value from LocalIndication to show theme default
LocalIndication을 사용하는데 없다면 null을 통과하고 있다면 현재 값을 사용한다고 설명하는데 이를 토대로 LocalIndication을 없도록 설정하거나 파라미터에 null을 입력해 주면 된다.
앱 전체에서 ripple 효과를 제거할 것이라면 LocalIndication을 설정해 주면 되지만 그게 아니라면 아래처럼 설정해 주는 것이 좋다.
indication = null
사용 방법
먼저 클릭 이벤트가 있는 컴포넌트에서 사용하는 방법이다.
@Composable
fun Button(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
shape: Shape = ButtonDefaults.shape,
colors: ButtonColors = ButtonDefaults.buttonColors(),
elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),
border: BorderStroke? = null,
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
interactionSource: MutableInteractionSource? = null,
content: @Composable RowScope.() -> Unit
) {
// ...
}
예시로 일반적인 Button이 있다. 해당 컴포넌트는 클릭 이벤트가 있으며 interactionSource도 있다. 이러한 컴포넌트는 자체적으로 클릭 이벤트를 내장하고 있기에 indication을 별도로 설정해 줄 수 없다. 따라서 emit을 없앤 interactionSource를 입력해 주어야만 ripple 효과가 사라진다.
다음으로는 modifier에서 .clickable을 사용하는 경우이다.
1)
Modifier.clickable(
interactionSource = MutableInteractionSource(),
indication = null,
onClick = { }
)
2)
Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = { }
)
3)
Modifier.clickable(
interactionSource = NoRippleInteractionSource(),
indication = null,
onClick = { }
)
• 1번은 동작하지 않는다. remember를 사용하라고 권장하는 경고가 뜨기 때문에 2번처럼 remember로 감싸주어야 동작한다.
• 2번은 재구성 시에 remember로 인해 MutableInteractionSource가 재사용된다. 참고로 이때 indication만 입력해 주고 interactionSource를 입력해주지 않는다면 오류가 뜨며 반대로 indication만 입력해 주고 interactionSource를 입력하지 않는다면 역시 오류가 뜨기 때문에 입력할 때는 반드시 두 가지를 모두 입력하거나 둘 다 입력하지 않아야 한다.
• 3번을 사용한다며 재구성할 때마다 interactionSource가 새롭게 생성된다. 그렇다면 remember로 감싸면 되지 않느냐인데 굳이 그렇게 하는 것보다 표준 api를 사용하는 2번을 사용하는 것이 낫다.
마지막으로 필자가 사용하는 확장 함수로 제작해서 사용하는 방법이다.
fun Modifier.noRippleClickable(
enabled: Boolean = true,
onClick: () -> Unit
): Modifier = this.clickable(
enabled = enabled,
interactionSource = null,
indication = null,
onClick = onClick
)
Reference
사용자 상호작용 처리 | Jetpack Compose | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. 사용자 상호작용 처리 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 사용자 인터페이스 구성요소는
developer.android.com