Android jetpack compose 创建新拟态风格控件

前几天开发新程序的时候,选择了jetpack最新的组件compose来构建应用程序的界面。但是因为compose面世不久,网上基本找不到太多相关的资料,想找一个新拟态风格的控件库但是找不到,也就只能自己写一个。

效果图:

Android jetpack compose 创建新拟态风格控件_第1张图片

 这里以输入框为例,其他的空间做成新拟态的原理是一样的。

一、原理

其实新拟态的原理是很简单的,大致就是给控件加两个阴影,显示出光线明暗的变化,如上图所示,左上加的是白色的阴影,右下加的是灰色的阴影。

二、面临的困难

在compose里面,api实际上是进一步封装的,改变控件的modifier与阴影相关的api只有:

@Stable
public fun Modifier.shadow(
    elevation: Dp,
    shape: Shape,
    clip: Boolean
): Modifier

这样一个。

看到elevation这个单词,应该不会感到陌生,这种阴影效果就是在Android5.0,apiLevel 21引入material design时引入的效果。

这种阴影效果只会出现在控件的右下角,没办法在控件的任意位置添加阴影,而且这种阴影效果是没办法指定颜色的。比如下图中按键的阴影:

Android jetpack compose 创建新拟态风格控件_第2张图片

 三、实现的思路

可能谷歌官方也知道compose的功能比现有Android framework的功能还是稍显羸弱,所以在绘图方面,compose的Canvas是可以直接借助底层的Android framework绘图工具来绘制的。也就是:

public inline fun DrawScope.drawIntoCanvas(
    block: (Canvas) → Unit
): Unit

这个api来调用底层的canvas,然后使用画笔,设置好阴影相关的底层属性,然后在canvas上面绘制两个阴影。

四、效果实现

修改控件的外观需要用到Modifier,所以一般习惯把实现封装成Modifier的扩展函数:

@RequiresApi(Build.VERSION_CODES.O)
fun Modifier.drawColoredShadow(
    color: Color,
    alpha: Float = 0.2f,
    borderRadius: Dp = 0.dp,
    shadowRadius: Dp = 20.dp,
    offsetX: Dp = 0.dp,
    offsetY: Dp = 0.dp,
    roundedRect: Boolean = true
) = this.drawBehind {
    
}

其中参数的含义:

color:阴影的颜色;

alpha:阴影的不透明度;

borderRadius:绘制的圆角矩形阴影的圆角半径;

shadowRadius:阴影所使用的高斯模糊算法的模糊半径,这里高斯模糊算法,就是把一个点的色彩用周围一定范围内的图像的色彩来取均值(或者加权均值)。这个范围的大小就是半径了。

offsetX:阴影在水平方向的偏移,大于零则向右偏移,小于零则向左。

offsetY:阴影在竖直方向的偏移,大于零则向下偏移,小于零则向上。

roundedRect:是否两边都是半圆的圆角矩形。

之所以这个地方调用了this.drawBehind,是因为底层Android framework里面的阴影是依附于图形的。也就是说,设置好了阴影,你只有画一个图形,才能显示出图形的阴影。而我们并不想让这个图形显示出来而且不让它影响控件的触发事件,所以这个时候就要调用drawBehind把阴影依附的图形绘制到控件的下面(z轴方向,类似于前端css里面的after伪类)。

首先初始化两个颜色,一个是阴影依附的图形的颜色,这个地方为了不显示出来,这里直接把不透明度alpha值设置为0f,第二个是阴影的颜色:

val transparentColor = android.graphics.Color.toArgb(color.copy(alpha = .0f).value.toLong())
    val shadowColor = android.graphics.Color.toArgb(color.copy(alpha = alpha).value.toLong())

然后调用drawIntoCanvas来调用底层android framework的canvas:

this.drawIntoCanvas {
        
    }

在drawIntoCanvas里面,初始化一个画笔对象:

val paint = Paint()
        val frameworkPaint = paint.asFrameworkPaint()

设置好画笔颜色和阴影效果:

frameworkPaint.color = transparentColor
        frameworkPaint.setShadowLayer(
            shadowRadius.toPx(),
            offsetX.toPx(),
            offsetY.toPx(),
            shadowColor
        )

上面这段代码中的setShadowLayer就是Android 底层设置阴影的api,第一个参数是阴影的半径,第二个是水平偏移,第三个是竖直偏移,第四个是阴影的颜色。

最后用画笔在canvas上面画一个跟控件尺寸一样,而且透明的图形,阴影就显示出来了:

it.drawRoundRect(
            0f,
            0f,
            this.size.width,
            this.size.height,
            if(roundedRect) this.size.height / 2 else borderRadius.toPx(),
            if(roundedRect) this.size.height / 2 else borderRadius.toPx(),
            paint
        )

在需要新拟态风的控件Modifier上调用这个扩展函数即可:

TextField(value = password, onValueChange = { password = it }, modifier = Modifier
                        .fillMaxWidth()
                        .background(Color.Transparent)
                        .padding(horizontal = 30.dp)
                        .drawColoredShadow(
                            Color.Black,
                            0.1f,
                            borderRadius = 25.dp,
                            shadowRadius = 10.dp,
                            offsetX = passwordAnimation.value.dp,
                            offsetY = passwordAnimation.value.dp
                        )
                        .drawColoredShadow(
                            Color.White,
                            0.9f,
                            borderRadius = 25.dp,
                            shadowRadius = 10.dp,
                            offsetX = (-passwordAnimation.value).dp,
                            offsetY = (-passwordAnimation.value).dp
                        )
                )

你可能感兴趣的:(Android,compose,android)