사용자 동작 중에서 누르는 동작에 대한 내용을 소개
High-level 단계에서 처리하는 방법이 중심으로 작성됨
Tap(Click): 누르고 떼기
Double tap: Tap 이후 일정 시간 안에 Tap
Press: 누르고 있기
Long press: 일정 시간 Press
가장 기본적인 클릭 처리 방법
// 컴포넌트 크기 만큼 터치 가능
Modifier
.size(100.dp)
.background(Color.Cyan)
.clickable {}
// 컴포넌트 크기에서 10.dp 만큼 줄어든 영역만 터치 가능
Modifier
...
.padding(10.dp)
.clickable{}
padding 속성 이후에 선언하면 패딩이 적용된 영역만 터치가 적용된다.
combinedClickable은 clickable에서 더블 클릭과 길게 누르기가 추가됐다.
Modifier
.combinedClickable(
onLongClick = {},
onDoubleClick = {},
onClick = {}
)
기본적으로 더블 클릭과 길게 누르기의 시간은 각각 0.3, 0.4초로 설정되어 있다.
만약 시간을 바꾸려면 ViewConfiguration 인터페이스를 구현해야 한다.
구현한 클래스를 LocalViewConfiguration에 설정하면 시간을 변경할 수 있다.
// 커스텀 ViewConfriguration
// touchSlop는 8f가 기본으로 되어 있다.
private class CustomViewConfiguration : ViewConfiguration {
override val doubleTapMinTimeMillis: Long
get() = 1000
override val doubleTapTimeoutMillis: Long
get() = 2000
override val longPressTimeoutMillis: Long
get() = 3000
override val touchSlop: Float
get() = 8f
}
// 커스텀 클래스를 설정해준다.
CompositionLocalProvider(
LocalViewConfiguration provides CustomViewConfiguration()
) {
// 테스트 확인용 텍스트
var text by remember { mutableStateOf("") }
Text(
modifier = Modifier
.combinedClickable(
onLongClick = { text = "Long Clicked" },
onDoubleClick = { text = "Double Clicked" },
onClick = { text = "Clicked" }
),
text = text,
)
}
선택하는 컴포넌트를 구현할 때 사용하는 Modifier 속성
clickable로 비슷한 동작을 만들 수 있고, 실제 내부 코드를 보면 clickable을 사용하고 있다.
selectable을 사용하는 이유는 UI 접근성과 테스트 때문으로 자세한 내용은 공식 문서 참조
clickable와 달리 선택된 상태를 전달해야 한다.
Modifier
.selectable(
selected = true or false,
onClick = { },
)
toggleable은 다른 속성과 달리 콜백 함수로 토글 상태를 전달받는다.
toggleable도 clickable로 구현이 되지만, not 연산이 자동이라 좀 더 편하다.
toggleable은 내부적으로 밑에서 나올 triStateToggleable을 사용한다.
// onValueChange은 항상 value에 not 연산 결과가 나온다.
// 이 경우에는 false가 나온다.
Modifier
.toggleable(
value = true,
onValueChange = { state -> },
)
내부적으로 clickable을 사용한다.
toggleable와 다른 점은 toggleable은 콜백으로 Boolean 타입을 주지만
triStateToggleable은 ToggleableState라는 enum 클래스를 반환한다.
ToggleableState는 On, Off, Indeterminate로 이루어져 있다.
토글 기능을 만들 때 On, Off 외에 다른 상태를 사용하고 싶을 때 사용할 수 있다.
Modifier
.triStateToggleable(
state = ToggleableState.Indeterminate,
onClick = { },
)
Ripple은 버튼을 눌렀을 때 살짝 어두워지는 효과를 말한다.
위에 나온 속성을 적용하면 기본 효과가 적용되는데, 디자인에 따라 변경해야할 수도 있다.
크게 2가지 방법으로 직접 만들거나 Indication을 적용하는 방법이 있다.
직접 만드는 방법은 말 그대로 클릭 상태에 따라 background 색상을 바꾸는 방법이다.
디자인에 따라 쉬울 수도 있지만, 컴포넌트마다 따로 만들어야 하는 문제가 있다.
반면 Indication을 사용하는 방법은 여러 컴포넌트에 사용할 수 있고 전체에 적용할 수도 있다.
InteractionSource: 사용자 상호작용 정보를 가지는 인터페이스, 속성마다 존재
Indication: InteractionSource에서 전달받은 정보로 시각적인 효과를 만듬
먼저 Indication을 만든다. 색상은 빨간색으로 만들었다.
private class CustomIndication: Indication {
private class CustomIndicationInstance(
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.Red.copy(alpha = 0.8f), size = size)
} else if (isHovered.value || isFocused.value) {
drawRect(color = Color.Red.copy(alpha = 0.8f), size = size)
}
}
}
@Composable
override fun rememberUpdatedInstance(
interactionSource: InteractionSource
): IndicationInstance {
val isPressed = interactionSource.collectIsPressedAsState()
val isHovered = interactionSource.collectIsHoveredAsState()
val isFocused = interactionSource.collectIsFocusedAsState()
return remember(interactionSource) {
CustomIndicationInstance(isPressed, isHovered, isFocused)
}
}
}
컴포넌트 하나에 적용하려면 컴포넌트 파라미터 중에 indication에 추가하면 된다.
Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = CustomIndication(),
onClick = {}
)
컴포넌트 전체에 적용하려면 CompositionLocalProvider를 사용한다.
CompositionLocalProvider(
LocalIndication provides CustomIndication()
) {
...
Modifier
.clickable {}
...
}
더 자세한 내용은 공식 문서 참조