SquigglySpan.kt
TLDR
The provided file SquigglySpan.kt
is a Kotlin file in the leakcanary.internal
package. It contains the implementation of the SquigglySpan
class, which is a custom span that can be used with SpannableStringBuilder
to replace UnderlineSpan
with a squiggly line. The class also includes the necessary methods to draw the squiggly line on a Canvas
. The file also includes a companion object that provides a method to replace UnderlineSpan
instances in a SpannableStringBuilder
with instances of SquigglySpan
.
Classes
SquigglySpan
The SquigglySpan
class is a custom ReplacementSpan
that can be used to replace UnderlineSpan
in a SpannableStringBuilder
. It draws a squiggly line under the text it spans.
Methods
getSize
This method measures the text length and returns its size.
draw
This method draws the squiggly line on a Canvas
.
Companion Object
replaceUnderlineSpans
This method replaces UnderlineSpan
with instances of SquigglySpan
in a SpannableStringBuilder
.
squigglyHorizontalPath
This method constructs the path of the squiggly line.
/*
* Copyright (C) 2018 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package leakcanary.internal
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Path
import android.text.SpannableStringBuilder
import android.text.style.ReplacementSpan
import android.text.style.UnderlineSpan
import com.squareup.leakcanary.core.R
import kotlin.math.sin
import leakcanary.internal.navigation.getColorCompat
/**
* Inspired from https://github.com/flavienlaurent/spans and
* https://github.com/andyxialm/WavyLineView
*/
internal class SquigglySpan(context: Context) : ReplacementSpan() {
private val squigglyPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val path: Path
private val referenceColor: Int
private val halfStrokeWidth: Float
private val amplitude: Float
private val halfWaveHeight: Float
private val periodDegrees: Float
private var width: Int = 0
init {
val resources = context.resources
squigglyPaint.style = Paint.Style.STROKE
squigglyPaint.color = context.getColorCompat(R.color.leak_canary_leak)
val strokeWidth =
resources.getDimensionPixelSize(R.dimen.leak_canary_squiggly_span_stroke_width)
.toFloat()
squigglyPaint.strokeWidth = strokeWidth
halfStrokeWidth = strokeWidth / 2
amplitude = resources.getDimensionPixelSize(R.dimen.leak_canary_squiggly_span_amplitude)
.toFloat()
periodDegrees =
resources.getDimensionPixelSize(R.dimen.leak_canary_squiggly_span_period_degrees)
.toFloat()
path = Path()
val waveHeight = 2 * amplitude + strokeWidth
halfWaveHeight = waveHeight / 2
referenceColor = context.getColorCompat(R.color.leak_canary_reference)
}
override fun getSize(
paint: Paint,
text: CharSequence,
start: Int,
end: Int,
fm: Paint.FontMetricsInt?
): Int {
width = paint.measureText(text, start, end)
.toInt()
return width
}
override fun draw(
canvas: Canvas,
text: CharSequence,
start: Int,
end: Int,
x: Float,
top: Int,
y: Int,
bottom: Int,
paint: Paint
) {
squigglyHorizontalPath(
path,
x + halfStrokeWidth,
x + width - halfStrokeWidth,
bottom - halfWaveHeight,
amplitude, periodDegrees
)
canvas.drawPath(path, squigglyPaint)
paint.color = referenceColor
canvas.drawText(text, start, end, x, y.toFloat(), paint)
}
companion object {
fun replaceUnderlineSpans(
builder: SpannableStringBuilder,
context: Context
) {
val underlineSpans = builder.getSpans(0, builder.length, UnderlineSpan::class.java)
for (span in underlineSpans) {
val start = builder.getSpanStart(span)
val end = builder.getSpanEnd(span)
builder.removeSpan(span)
builder.setSpan(SquigglySpan(context), start, end, 0)
}
}
@Suppress("LongParameterList")
private fun squigglyHorizontalPath(
path: Path,
left: Float,
right: Float,
centerY: Float,
amplitude: Float,
periodDegrees: Float
) {
path.reset()
var y: Float
path.moveTo(left, centerY)
val period = (2 * Math.PI / periodDegrees).toFloat()
var x = 0f
while (x <= right - left) {
y = (amplitude * sin((40 + period * x).toDouble()) + centerY).toFloat()
path.lineTo(left + x, y)
x += 1f
}
}
}
}