From 120dc10f6a840a27e2f16af91c5181a1be7669ad Mon Sep 17 00:00:00 2001 From: Kirill Nayduik Date: Fri, 25 Jun 2021 09:04:03 +0300 Subject: [PATCH 1/3] Add TextView with ClickableSpan substring --- .../ru/touchin/extensions/CharSequence.kt | 3 ++ utils/build.gradle | 1 + .../components/utils/spans/SpanUtils.kt | 26 ++++++++++++ .../touchin/roboswag/views/ActionTextView.kt | 42 +++++++++++++++++++ views/src/main/res/values/attrs.xml | 5 +++ 5 files changed, 77 insertions(+) create mode 100644 kotlin-extensions/src/main/java/ru/touchin/extensions/CharSequence.kt create mode 100644 views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt diff --git a/kotlin-extensions/src/main/java/ru/touchin/extensions/CharSequence.kt b/kotlin-extensions/src/main/java/ru/touchin/extensions/CharSequence.kt new file mode 100644 index 0000000..bd4fe7d --- /dev/null +++ b/kotlin-extensions/src/main/java/ru/touchin/extensions/CharSequence.kt @@ -0,0 +1,3 @@ +package ru.touchin.extensions + +fun CharSequence.indexesOf(substring: String) = Regex(substring).find(this)?.let { it.range.first to it.range.last + 1 } diff --git a/utils/build.gradle b/utils/build.gradle index 2a77fe4..2a490af 100644 --- a/utils/build.gradle +++ b/utils/build.gradle @@ -1,6 +1,7 @@ apply from: "../android-configs/lib-config.gradle" dependencies { + implementation project(':kotlin-extensions') implementation "androidx.core:core" implementation "androidx.annotation:annotation" diff --git a/utils/src/main/java/ru/touchin/roboswag/components/utils/spans/SpanUtils.kt b/utils/src/main/java/ru/touchin/roboswag/components/utils/spans/SpanUtils.kt index 25b6fec..d859147 100644 --- a/utils/src/main/java/ru/touchin/roboswag/components/utils/spans/SpanUtils.kt +++ b/utils/src/main/java/ru/touchin/roboswag/components/utils/spans/SpanUtils.kt @@ -2,9 +2,14 @@ package ru.touchin.roboswag.components.utils.spans import android.text.SpannableString import android.text.Spanned +import android.text.TextPaint +import android.text.style.ClickableSpan +import android.text.style.ForegroundColorSpan import android.text.style.URLSpan import android.text.util.Linkify +import android.view.View import androidx.core.text.HtmlCompat +import ru.touchin.extensions.indexesOf /** * Convert text with 'href' tags and raw links to spanned text with clickable URLSpan. @@ -47,3 +52,24 @@ private fun SpannableString.getUrlSpans() = getSpans(0, length, URLSpan::class.j .map { UrlSpanWithBorders(it, this.getSpanStart(it), this.getSpanEnd(it)) } private data class UrlSpanWithBorders(val span: URLSpan, val start: Int, val end: Int) + +/** + * Find substring inside string (for example in TextView) and fill it with ClickableSpan + */ +fun CharSequence.getClickableSubstring(substring: String, clickAction: () -> Unit, color: Int? = null) = SpannableString(this) + .apply { + indexesOf(substring)?.let { (startSpan, endSpan) -> + setSpan(object : ClickableSpan() { + override fun onClick(widget: View) { + clickAction.invoke() + } + + override fun updateDrawState(ds: TextPaint) { + super.updateDrawState(ds) + ds.isUnderlineText = false + } + }, startSpan, endSpan, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + + color?.let { setSpan(ForegroundColorSpan(it), startSpan, endSpan, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } + } + } diff --git a/views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt b/views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt new file mode 100644 index 0000000..673fbec --- /dev/null +++ b/views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt @@ -0,0 +1,42 @@ +package ru.touchin.roboswag.views + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatTextView +import androidx.core.content.withStyledAttributes +import ru.touchin.roboswag.components.utils.movementmethods.ClickableMovementMethod +import ru.touchin.roboswag.components.utils.spans.getClickableSubstring + +class ActionTextView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : AppCompatTextView(context, attrs, defStyleAttr) { + + private var onClickAction: () -> Unit = { } + + init { + isClickable = false + isLongClickable = false + highlightColor = Color.TRANSPARENT + + movementMethod = ClickableMovementMethod + + context.withStyledAttributes(attrs, R.styleable.ActionTextView, defStyleAttr, 0) { + val actionText = getString(R.styleable.ActionTextView_actionText).orEmpty() + val actionColor = getColor(R.styleable.ActionTextView_actionColor, currentTextColor) + + text = text.getClickableSubstring( + substring = actionText, + clickAction = { onClickAction.invoke() }, + color = actionColor + ) + } + } + + fun setOnActionClickListener(onClickAction: () -> Unit) { + this.onClickAction = onClickAction + } + +} diff --git a/views/src/main/res/values/attrs.xml b/views/src/main/res/values/attrs.xml index b8dce82..49e0074 100644 --- a/views/src/main/res/values/attrs.xml +++ b/views/src/main/res/values/attrs.xml @@ -50,4 +50,9 @@ + + + + + From 1a8b57de882c9da6136d2f6d2eeb78ed87b6e984 Mon Sep 17 00:00:00 2001 From: Kirill Nayduik Date: Fri, 25 Jun 2021 09:50:20 +0300 Subject: [PATCH 2/3] Fix PR comments --- .../roboswag/components/utils/spans/SpanUtils.kt | 13 +++++++++---- .../ru/touchin/roboswag/views/ActionTextView.kt | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/utils/src/main/java/ru/touchin/roboswag/components/utils/spans/SpanUtils.kt b/utils/src/main/java/ru/touchin/roboswag/components/utils/spans/SpanUtils.kt index d859147..b69a0cd 100644 --- a/utils/src/main/java/ru/touchin/roboswag/components/utils/spans/SpanUtils.kt +++ b/utils/src/main/java/ru/touchin/roboswag/components/utils/spans/SpanUtils.kt @@ -8,6 +8,7 @@ import android.text.style.ForegroundColorSpan import android.text.style.URLSpan import android.text.util.Linkify import android.view.View +import androidx.annotation.ColorInt import androidx.core.text.HtmlCompat import ru.touchin.extensions.indexesOf @@ -56,7 +57,12 @@ private data class UrlSpanWithBorders(val span: URLSpan, val start: Int, val end /** * Find substring inside string (for example in TextView) and fill it with ClickableSpan */ -fun CharSequence.getClickableSubstring(substring: String, clickAction: () -> Unit, color: Int? = null) = SpannableString(this) +fun CharSequence.toClickableSubstringText( + substring: String, + clickAction: () -> Unit, + @ColorInt color: Int? = null, + isUnderlineText: Boolean = false +) = SpannableString(this) .apply { indexesOf(substring)?.let { (startSpan, endSpan) -> setSpan(object : ClickableSpan() { @@ -66,10 +72,9 @@ fun CharSequence.getClickableSubstring(substring: String, clickAction: () -> Uni override fun updateDrawState(ds: TextPaint) { super.updateDrawState(ds) - ds.isUnderlineText = false + ds.isUnderlineText = isUnderlineText + if (color != null) ds.color = color } }, startSpan, endSpan, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) - - color?.let { setSpan(ForegroundColorSpan(it), startSpan, endSpan, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } } } diff --git a/views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt b/views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt index 673fbec..2e79ddf 100644 --- a/views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt +++ b/views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt @@ -6,7 +6,7 @@ import android.util.AttributeSet import androidx.appcompat.widget.AppCompatTextView import androidx.core.content.withStyledAttributes import ru.touchin.roboswag.components.utils.movementmethods.ClickableMovementMethod -import ru.touchin.roboswag.components.utils.spans.getClickableSubstring +import ru.touchin.roboswag.components.utils.spans.toClickableSubstringText class ActionTextView @JvmOverloads constructor( context: Context, @@ -27,7 +27,7 @@ class ActionTextView @JvmOverloads constructor( val actionText = getString(R.styleable.ActionTextView_actionText).orEmpty() val actionColor = getColor(R.styleable.ActionTextView_actionColor, currentTextColor) - text = text.getClickableSubstring( + text = text.toClickableSubstringText( substring = actionText, clickAction = { onClickAction.invoke() }, color = actionColor From a39ec54e523783033f0e7d5461b9fdfcc039b670 Mon Sep 17 00:00:00 2001 From: Kirill Nayduik Date: Fri, 25 Jun 2021 09:53:01 +0300 Subject: [PATCH 3/3] Add isUnderlineText attribute --- .../src/main/java/ru/touchin/roboswag/views/ActionTextView.kt | 4 +++- views/src/main/res/values/attrs.xml | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt b/views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt index 2e79ddf..39d3ffd 100644 --- a/views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt +++ b/views/src/main/java/ru/touchin/roboswag/views/ActionTextView.kt @@ -26,11 +26,13 @@ class ActionTextView @JvmOverloads constructor( context.withStyledAttributes(attrs, R.styleable.ActionTextView, defStyleAttr, 0) { val actionText = getString(R.styleable.ActionTextView_actionText).orEmpty() val actionColor = getColor(R.styleable.ActionTextView_actionColor, currentTextColor) + val isUnderlineText = getBoolean(R.styleable.ActionTextView_isUnderlineText, false) text = text.toClickableSubstringText( substring = actionText, clickAction = { onClickAction.invoke() }, - color = actionColor + color = actionColor, + isUnderlineText = isUnderlineText ) } } diff --git a/views/src/main/res/values/attrs.xml b/views/src/main/res/values/attrs.xml index 49e0074..9b154af 100644 --- a/views/src/main/res/values/attrs.xml +++ b/views/src/main/res/values/attrs.xml @@ -53,6 +53,7 @@ +