Created
February 12, 2018 15:09
-
-
Save shekibobo/f09c81f61d14eb99af2ced5d0eef6bf7 to your computer and use it in GitHub Desktop.
Collection of Extensions for String and SpannableString
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.collectiveidea.util.string | |
import android.graphics.Paint | |
import android.graphics.Typeface | |
import android.text.TextPaint | |
import android.text.style.MetricAffectingSpan | |
class CustomTypefaceSpan(private val typeface: Typeface) : MetricAffectingSpan() { | |
override fun updateDrawState(drawState: TextPaint) = apply(drawState) | |
override fun updateMeasureState(paint: TextPaint) = apply(paint) | |
private fun apply(paint: Paint) { | |
val oldTypeface = paint.typeface | |
val oldStyle = oldTypeface?.style ?: 0 | |
val fakeStyle = oldStyle and typeface.style.inv() | |
if (fakeStyle and Typeface.BOLD != 0) { | |
paint.isFakeBoldText = true | |
} | |
if (fakeStyle and Typeface.ITALIC != 0) { | |
paint.textSkewX = -0.25f | |
} | |
paint.typeface = typeface | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.collectiveidea.util.string | |
import android.content.Context | |
import android.support.v4.content.res.ResourcesCompat | |
import android.text.Html | |
import android.text.Spannable | |
import android.text.SpannableString | |
import android.text.Spanned | |
import android.text.style.AbsoluteSizeSpan | |
import android.text.style.ForegroundColorSpan | |
import android.text.style.StyleSpan | |
import android.text.style.TextAppearanceSpan | |
import com.collectiveidea.exampleapp.R | |
import timber.log.Timber | |
// Wrap a backwards compatible implementation of `Html.fromHtml()` as a string extension. | |
fun String.fromHtml(): Spanned { | |
return if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.N) { | |
@Suppress("DEPRECATION") | |
Html.fromHtml(this) | |
} else { | |
Html.fromHtml(this, Html.FROM_HTML_MODE_LEGACY) | |
} | |
} | |
// Return the string if it is not null or blank, otherwise return null. | |
// Example usage: | |
// textView.text = nullableOrBlankOrValidString.presence() ?: "Default Filler Text" | |
fun String?.presence(): String? { | |
return if (isNullOrBlank()) null else this | |
} | |
// Converts a regular `String` to a `SpannableString` | |
fun String.spannable(): SpannableString = SpannableString(this) | |
// Applies a `ForegroundColorSpan` to the receiver with the provided color at | |
// the indices of the provided substring. | |
// If the receiver does not contain the matching substring, warn, and ignore. | |
// Returns the receiver to allow for chaining. | |
fun SpannableString.colorSpan(substring: String, color: Int): SpannableString { | |
return applySpan(substring, ForegroundColorSpan(color)) | |
} | |
// Applies a `TextAppearanceSpan` to the receiver with the provided `TextAppearance` | |
// style at the indices of the provided substring. | |
// If the receiver does not contain the matching substring, warn, and ignore. | |
// Returns the receiver to allow for chaining. | |
fun SpannableString.textAppearanceSpan( | |
context: Context, | |
substring: String, | |
style: Int | |
): SpannableString { | |
val span = TextAppearanceSpan(context, style) | |
return applySpan(substring, span).also { | |
// Handle typeface being set by font resource | |
val fontName = span.family | |
?.removePrefix("res/font/") | |
?.replaceAfter(".", "") | |
?.replace(".", "") ?: "" | |
val res = fontName.presence()?.let { context.getResId(it, R.font::class.java) } ?: -1 | |
if (res != -1) { fontSpan(context, substring, res) } | |
} | |
} | |
// Applies a `StyleSpan` to the receiver with the provided `Style` value | |
// at the indices of the provided substring. | |
// If the receiver does not contain the matching substring, warn, and ignore. | |
// Returns the receiver to allow for chaining. | |
fun SpannableString.styleSpan(substring: String, style: Int): SpannableString { | |
return applySpan(substring, StyleSpan(style)) | |
} | |
// Applies a `AbsoluteSizeSpan` to the receiver with the provided size | |
// at the indices of the provided substring | |
fun SpannableString.sizeSpan(substring: String, size: Int): SpannableString { | |
return applySpan(substring, AbsoluteSizeSpan(size)) | |
} | |
// Applies a `CustomTypefaceSpan` to the receiver with the provided font resource | |
// at the indices of the provided substring. | |
// If the receiver does not contain the matching substring, warn, and ignore. | |
// Returns the receiver to allow for chaining. | |
fun SpannableString.fontSpan(context: Context, substring: String, fontRes: Int): SpannableString { | |
if (fontRes == -1) { | |
Timber.w("Font resource not found: -1") | |
return this | |
} | |
val typeface = ResourcesCompat.getFont(context, fontRes) | |
return typeface?.let { applySpan(substring, CustomTypefaceSpan(typeface)) } ?: this | |
} | |
// Applies the provided span to the recevier at the indices of the provided substring. | |
// If the receiver does not contain the matching substring, warn, and ignore. | |
// Returns the receiver to allow for chaining. | |
fun SpannableString.applySpan(substring: String, span: Any): SpannableString { | |
val start = indexOf(substring, 0, true) | |
if (start == -1) { | |
Timber.w("Cannot apply span <$span>: <$this> does not contain substring <$substring>") | |
return this | |
} | |
val end = start + substring.length | |
setSpan(span, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE) | |
return this | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment