这一节主要了解一下Compose中的加水印,在开发中,有时候我们需要对应用界面做水印效果,水印是指覆盖在应用界面上的半透明标识或图案,通常用于版权声明、内容归属标记或安全防护,Compose中水印效果实现方式,简单总结如下:
场景:
1. 版权保护与内容归属
图片编辑类App在未保存作品上添加 “草稿” 水印;
文档阅读类App在页面显示用户账号或机构名称,防止截图外泄。
2. 安全与保密需求
敏感信息追踪:在内部系统添加包含用户ID、时间戳的动态水印,便于追溯泄露源头。
防截屏保护:部分金融类App通过水印提示 “截屏可能泄露信息”,增强安全警示。
3. 品牌与视觉识别
强化品牌认知:在产品演示界面、宣传材料中添加品牌logo水印,提升曝光度。
区分版本属性:免费版App添加 “试用版” 水印,付费版则隐藏,作为功能区分标识。
栗子:
使用LazyGrid
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp@Composable
fun WatermarkLazyGrid(markText: String,textStyle: TextStyle = TextStyle(fontSize = 14.sp,fontWeight = FontWeight.Normal,color = Color(0.5f, 0.5f, 0.5f, 0.3f)),columns: Int = 3,rows: Int = 5,rotation: Float = -25f,content: @Composable BoxScope.() -> Unit,
) {Box(modifier = Modifier.fillMaxSize()) {content()LazyVerticalGrid(columns = GridCells.Fixed(columns),modifier = Modifier.fillMaxSize().pointerInput(Unit) {awaitPointerEventScope {while (true) {awaitPointerEvent()}}}) {items(columns * rows) { index ->Box(modifier = Modifier.fillMaxSize().graphicsLayer {rotationZ = rotation},contentAlignment = Alignment.Center) {Text(text = markText,style = textStyle,modifier = Modifier.padding(16.dp))}}}}
}调用:WatermarkLazyGrid("HelloWorld") {FixedGridExample()}
使用Canvas
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.rotate
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.sp@Composable
fun WatermarkOverlay(text: String,alpha: Float = 0.1f,textSize: androidx.compose.ui.unit.TextUnit = 18.sp,angle: Float = 30f,content: @Composable BoxScope.() -> Unit,
) {val textMeasurer = rememberTextMeasurer()Canvas(modifier = Modifier.fillMaxSize()) {val canvasWidth = size.widthval canvasHeight = size.heightval textLayoutResult = textMeasurer.measure(text = text,style = TextStyle(fontSize = textSize, color = Color.Black.copy(alpha = alpha)))val textWidth = textLayoutResult.size.width.toFloat()val textHeight = textLayoutResult.size.height.toFloat()val xStep = textWidth * 2val yStep = textHeight * 2for (x in -canvasWidth.toInt()..canvasWidth.toInt() step xStep.toInt()) {for (y in -canvasHeight.toInt()..canvasHeight.toInt() step yStep.toInt()) {rotate(degrees = angle, pivot = Offset(x.toFloat(), y.toFloat())) {drawText(textMeasurer = textMeasurer,text = text,topLeft = Offset(x.toFloat(), y.toFloat()))}}}}
}
用Modifier.graphicsLayer
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.sp@Composable
fun WatermarkPlay(markText: String,textStyle: TextStyle = TextStyle(fontSize = 14.sp,fontWeight = FontWeight.Normal,color = Color(0.5f, 0.5f, 0.5f, 0.3f)),columns: Int = 3,rows: Int = 5,rotation: Float = -25f,content: @Composable BoxScope.() -> Unit,
) {Box(modifier = Modifier.fillMaxSize()) {content()BoxWithConstraints(modifier = Modifier.fillMaxSize()) {val boxWidth = constraints.maxWidth.toFloat()val boxHeight = constraints.maxHeight.toFloat()val horizontalSpacing = boxWidth / (columns + 1)val verticalSpacing = boxHeight / (rows + 1)for (row in 0 until rows) {for (col in 0 until columns) {val x = horizontalSpacing * (col + 1)val y = verticalSpacing * (row + 1)Text(text = markText,style = textStyle,modifier = Modifier.offset { IntOffset(x.toInt(), y.toInt()) }.graphicsLayer {rotationZ = rotationalpha = 0.3f // 透明度})}}}}
}使用:
WatermarkPlay("HelloWorld") {FixedGridExample()}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.Card@Composable
fun FixedGridExample() {val items = List(6) { "Item $it" }LazyVerticalGrid(columns = GridCells.Fixed(3), // 固定3列contentPadding = PaddingValues(20.dp),verticalArrangement = Arrangement.spacedBy(4.dp),horizontalArrangement = Arrangement.spacedBy(12.dp)) {items(items) { item ->Card(onClick = {},modifier = Modifier.height(100.dp)) {Box(contentAlignment = Alignment.Center,modifier = Modifier.fillMaxSize()) {Text(item)}}}}
}
总结:
1. Canvas方案:
适合复杂图案或高性能需求场景
需要手动处理文本测量和绘制
2. Modifier方案:
代码最简洁,利用Compose现有API
适合简单文本水印场景
3. LazyGrid方案:
布局最灵活,支持滚动和动态内容
适合需要响应式布局的场景
比Canvas和Modifier方案略重
注意:
1 透明度与可读性平衡,水印过深会影响主内容,过浅则起不到作用,建议使用alpha=0.1 ~ 0.2的半透明效果.
2 水印若覆盖在可点击元素上,可能拦截触摸事件,使用pointerInput(Unit) {}让水印层忽略触摸事件.