jetpack compose 使用svg实现异形背景

908 阅读1分钟

作为一个开发人员, 要是不是满足产品经理\ui设计师的各种bt需求. 最近做的产品,需要做一个卡片类型的ui,特别之处在于背景不是长方形的,而是上下边是带有弧度. 首先想到的方案是使用.9的png, 不过考虑到png做横向拉伸后弧度会变形.所以想到第二种方案就是使用svg作为背景, 在绘制背景时动态生成svg,达到适配不同宽度的背景图. 至于能不能实现,还需要做下实验

  1. 首先,既然作为背景大小需要适配组件的大小, 那么就动态拼装svg字符串 按照个人传统, 还是假设svg是按照360宽度设计的
fun svgBg(color: String = "#ffffff", alpha: Float = 1f) =
    fun(width: Int, height: Int): String {
      val hIn360 = height * 360 / width
      val scale = width * 1f / 360f
      return """
        <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="360" height="${hIn360}"
          viewBox="0 0 360 ${hIn360}">
          <g transform="matrix(${scale}, 0, 0, ${(scale)}, 0, 0)">
              <path
                   d="M 16 0 s 78.25 -5 164.5 -5 s 164.5 5 164.5 5 a 16 16 0 0 1 16 16 v ${kotlin.math.max(0,hIn360-37)} a 16 16 0 0 1 -16 16 s -78.25 -5 -164.5 -5 s -164.5 5 -164.5 5 a 16 16 0 0 1 -16 -16 V 16 a 16 16 0 0 1 16 -16 Z" transform="translate(0 5)"
                   style="fill: ${color}; fill-opacity: ${alpha};" />
          </g>
          </svg>
      """.trimIndent()
    }
  1. 其次, 自然就需要有代码来解析这段字符串, 来获取需要绘制到canvas上的数据.通过万能的互联网,找到一个可以解析svg的工具 implementation 'com.caverock:androidsvg-aar:1.4'. 而且这个库可以把svg直接绘制到canvas上或者生成drawable,真是太方便了:).
  2. 最后, 需要实现Modifier的一个扩展方法, 让svg背景的使用符合compose的习惯
fun Modifier.svgStringBackground(render: (Int, Int) -> String) = composed {
  this.drawBehind {
    this.drawIntoCanvas {
      val svg = SVG.getFromString(render(this.size.width.toInt(), (this.size.height).toInt()))

      // create a drawable from svg
      val drawable = PictureDrawable(svg.renderToPicture())
      drawable?.setBounds(0, 0, this.size.width.toInt(), this.size.height.toInt())
      drawable?.draw(it.nativeCanvas)
    }
  }
}

到这基本就齐活了, 来把这几步组合起来试试看效果吧!

LazyColumn(
            state = lazyListState,
            modifier =
            Modifier.wrapContentWidth()) {
            items(100) { index ->
                Box(
                    modifier =
                    Modifier.fillMaxWidth()
                        .height(100.dp)
                        .svgStringBackground(svgBg("#00aaaa", 0.7f))) { Text("item$index content") }
            }
        }

效果看起来还算凑合, 如下:

截图 (3).png

最后还有几点不足:

  1. 在实际应用中,需要调整svg模板的参数, 需要对svg格式有足够的知识
  2. 到目前为止,所使用的svg库不支持filter标签,所以这种方式,还不能支持阴影等特效.