【Android】String字符串 转 矢量Path 踩坑记录

1.背景

接到个需求是矢量图里的文字需要根据系统语言自动适配,这就需要把String字符串转为路径,再绘制出来。

2.String转Path

这一步比较简单,直接问GitHub Copilot马上就有了如下方法:

fun stringToPath(text: String, textSize: Float): Path {
    // 创建Paint对象并设置字体大小
    val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
      this.textSize = textSize
    }

    // 创建Path对象
    val path = Path()
    // 获取文本的边界框
    val bounds = Rect()
    paint.getTextBounds(text, 0, text.length, bounds)

    // 计算路径的起始点,使文本居中
    val x = -bounds.left.toFloat()
    val y = -bounds.top.toFloat()

    // 将文本转换为路径
    paint.getTextPath(text, 0, text.length, x, y, path)
    return path
  }

路径数据获取正常。

3. Path转SVG

这一步就麻烦了,问了GitHub Copilot、Kimi、ChatGPT,给出的基本都是一样的逻辑,这是Kimi给出的,这里只需要把打印出来的字符保存为.svg格式就能直接用浏览器打开,或者拷贝Path数据到Android Studio的XML矢量图的vector标签替换Path参数就能预览。



fun pathToSvg(path: Path): String {
    val pathMeasure = PathMeasure(path, false)
    val svgBuilder = StringBuilder()
    svgBuilder.append("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n")
    svgBuilder.append("<path d=\"")

    val pos = FloatArray(2)
    val tan = FloatArray(2)

    while (pathMeasure.nextContour()) {
        pathMeasure.getPosTan(0f, pos, tan)
        svgBuilder.append("M ${pos[0]} ${pos[1]}")

        val length = pathMeasure.length
        var distance = 0f
        val step = 1f // 设置步长为1,可以根据需要调整
        while (distance < length) {
            pathMeasure.getPosTan(distance, pos, tan)
            svgBuilder.append(" L ${pos[0]} ${pos[1]}")
            distance += step
        }
    }

    svgBuilder.append("\" />\n")
    svgBuilder.append("</svg>")
    return svgBuilder.toString()
}

返回的结果会丢失第一个字母,或者汉字封口异常。一直怀疑的PathMeasure问题,想寻找其他方法或者其他支持库,都没能实现,只能回来调试参数:

首先把Path放到自定义View的onDraw方法,显示正常:
canvas.drawPath(stringToPath(“User用户”, 50f), mHandlePaint!!)
说明转换没问题,那就是Path数据量化问题。

从单个字母输入发现返回SVG数据为空,这才发现是循环问题。pathMeasure.nextContour()这个方法直接跳过了第一条路径。正确循环如下:

 // 将路径转换为SVG格式的字符串
  fun pathToSvg(path: Path): String {
    val pathMeasure = PathMeasure(path, false)
    val svgBuilder = StringBuilder()
    svgBuilder.append("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n")
    svgBuilder.append("<path d=\"")

    val pos = FloatArray(2)
    val tan = FloatArray(2)
    Log.d("TAG", "pathMeasure.length: ${pathMeasure.length}")

    while (pathMeasure.length > 0) {
      pathMeasure.getPosTan(0f, pos, tan)
      svgBuilder.append("M ${pos[0]} ${pos[1]}")

      val length = pathMeasure.length
      var distance = 0f
      val step = 2f // 设置步长为1,可以根据需要调整
      while (distance < length) {
        pathMeasure.getPosTan(distance, pos, tan)
        svgBuilder.append(" L ${pos[0]} ${pos[1]}")
        distance += step
      }
      pathMeasure.nextContour()
    }

    svgBuilder.append("\" />\n")
    svgBuilder.append("</svg>")
    return svgBuilder.toString()
  }

问题解决,收工。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值