Jetapck Compose 去除點擊水波紋效果

問題:Jetpack Compose 中使用 Material 包中的控件,點擊默認會有水波紋效果。如何去除這個點擊水波紋效果呢?
看下 Modifier.clickable 的簽名:

fun Modifier.clickable(
    interactionSource: MutableInteractionSource,
    indication: Indication?,
    enabled: Boolean = true,
    onClickLabel: String? = null,
    role: Role? = null,
    onClick: () -> Unit
)

其實就是 indication 這個參數決定的。

針對局部單個點擊去除水波紋效果,我們只需要將該參數設置爲 null 即可。
針對全局如何設置呢? indication 參數默認值是 indication = LocalIndication.current。顯然這是通過 CompositionLocal 的方式傳遞下來的。那麼我們只需要自己定義一個 Indication,在上層把這個屬性值給替換掉即可。比如需要對整個 Activity 生效,就在該 Activity 的根組合項中替換,如果需要整個應用生效,可以在主題中進行替換。

先看一下默認的 Indication 是如何實現的:

val LocalIndication = staticCompositionLocalOf<Indication> {
    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)
        }
    }
}

其實在這個文件中已經實現了一個 NoIndication, 只不過是 private 的:

private object NoIndication : Indication {
    private object NoIndicationInstance : IndicationInstance {
        override fun ContentDrawScope.drawIndication() {
            drawContent()
        }
    }

    @Composable
    override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
        return NoIndicationInstance
    }
}

這裏面涉及到了兩個接口:

@Stable
interface Indication {

    @Composable
    fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance
}

interface IndicationInstance {

    fun ContentDrawScope.drawIndication()
}

看起來,邏輯很清晰,自定義一個 Indication 需要兩步:

  1. 實現IndicationInstance 接口,實現 ContentDrawScope.drawIndication() 方法。並把這個對象返回。看方法名,應該就是繪製點擊效果。默認實現中使用到了 interactionSource ,根據點擊的狀態不同,繪製出不同的效果。
  2. 實現 Indication 的接口,實現 rememberUpdatedInstance 方法,返回上面的 IndicationInstance 類型的對象。

有了理論基礎,我們實操一下,去掉點擊水波紋效果,也分爲兩步:

  1. 定義一個無點擊效果的 Indication。直接把源碼中的那個私有的照抄就行了,讓我們應用可用。
object NoIndication: Indication {
    private object NoIndicationInstance: IndicationInstance {
        override fun ContentDrawScope.drawIndication() {
            drawContent()
        }

    }

    @Composable
    override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
        return NoIndicationInstance
    }

}
  1. 應用這個 Indication
@Composable
fun CustomTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Dynamic color is available on Android 12+
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit
) {
    ...
    MaterialTheme(
        colorScheme = colorScheme,
        typography = Typography,
        content = {
            CompositionLocalProvider(LocalIndication provides NoIndication) {
                content()
            }
        }
    )
}

Done !!!

相信有了這個理論基礎,我們完全可以自定義自己想要的點擊效果了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章