From 745f8d32b58d2f1132493c4e70a3701b065c69bf Mon Sep 17 00:00:00 2001 From: arkon Date: Thu, 9 Jan 2020 22:13:25 -0500 Subject: [PATCH] Use OutlineSpan approach from CarlosEsco/Neko to avoid infinite redraws Based on work by @arsonistAnt: https://github.com/CarlosEsco/Neko/commit/1876f850f6177b756ef623c9baedd65241a424d5 --- .../ui/reader/PageIndicatorTextView.kt | 42 ++++++-------- .../eu/kanade/tachiyomi/widget/OutlineSpan.kt | 57 +++++++++++++++++++ 2 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/OutlineSpan.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/PageIndicatorTextView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/PageIndicatorTextView.kt index a93803972..de9868b12 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/PageIndicatorTextView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/PageIndicatorTextView.kt @@ -2,15 +2,14 @@ package eu.kanade.tachiyomi.ui.reader import android.annotation.SuppressLint import android.content.Context -import android.graphics.Canvas import android.graphics.Color -import android.graphics.Paint -import androidx.appcompat.widget.AppCompatTextView import android.text.Spannable import android.text.SpannableString import android.text.style.ScaleXSpan import android.util.AttributeSet import android.widget.TextView +import androidx.appcompat.widget.AppCompatTextView +import eu.kanade.tachiyomi.widget.OutlineSpan /** * Page indicator found at the bottom of the reader @@ -20,19 +19,8 @@ class PageIndicatorTextView( attrs: AttributeSet? = null ) : AppCompatTextView(context, attrs) { - private val fillColor = Color.rgb(235, 235, 235) - private val strokeColor = Color.rgb(45, 45, 45) - - override fun onDraw(canvas: Canvas) { - textColorField.set(this, strokeColor) - paint.strokeWidth = 4f - paint.style = Paint.Style.STROKE - super.onDraw(canvas) - - textColorField.set(this, fillColor) - paint.strokeWidth = 0f - paint.style = Paint.Style.FILL - super.onDraw(canvas) + init { + setTextColor(fillColor) } @SuppressLint("SetTextI18n") @@ -42,20 +30,26 @@ class PageIndicatorTextView( val currText = " $text " // Also add a bit of spacing between each character, as the stroke overlaps them - val finalText = SpannableString(currText.asIterable().joinToString("\u00A0")) + val finalText = SpannableString(currText.asIterable().joinToString("\u00A0")).apply { + // Apply text outline + setSpan(spanOutline, 1, length-1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - for (i in 1..finalText.lastIndex step 2) { - finalText.setSpan(ScaleXSpan(0.1f), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + for (i in 1..lastIndex step 2) { + setSpan(ScaleXSpan(0.2f), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } } super.setText(finalText, TextView.BufferType.SPANNABLE) } private companion object { - // We need to use reflection to set the text color instead of using [setTextColor], - // otherwise the view is invalidated inside [onDraw] and there's an infinite loop - val textColorField = TextView::class.java.getDeclaredField("mCurTextColor").apply { - isAccessible = true - }!! + private val fillColor = Color.rgb(235, 235, 235) + private val strokeColor = Color.rgb(45, 45, 45) + + // A span object with text outlining properties + val spanOutline = OutlineSpan( + strokeColor = strokeColor, + strokeWidth = 4f + ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/OutlineSpan.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/OutlineSpan.kt new file mode 100644 index 000000000..79b205777 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/OutlineSpan.kt @@ -0,0 +1,57 @@ +package eu.kanade.tachiyomi.widget + +import android.graphics.Canvas +import android.graphics.Paint +import android.text.style.ReplacementSpan +import androidx.annotation.ColorInt +import androidx.annotation.Dimension + +/** + * Source: https://github.com/santaevpavel + * + * A class that draws the outlines of a text when given a stroke color and stroke width. + */ +class OutlineSpan( + @ColorInt private val strokeColor: Int, + @Dimension private val strokeWidth: Float +) : ReplacementSpan() { + + override fun getSize( + paint: Paint, + text: CharSequence, + start: Int, + end: Int, + fm: Paint.FontMetricsInt? + ): Int { + return paint.measureText(text.toString().substring(start until end)).toInt() + } + + override fun draw( + canvas: Canvas, + text: CharSequence, + start: Int, + end: Int, + x: Float, + top: Int, + y: Int, + bottom: Int, + paint: Paint + ) { + val originTextColor = paint.color + + paint.apply { + color = strokeColor + style = Paint.Style.STROKE + this.strokeWidth = this@OutlineSpan.strokeWidth + } + canvas.drawText(text, start, end, x, y.toFloat(), paint) + + paint.apply { + color = originTextColor + style = Paint.Style.FILL + } + + canvas.drawText(text, start, end, x, y.toFloat(), paint) + } + +}