Skip to content

Commit

Permalink
test(SpannedString): 확장함수 테스트
Browse files Browse the repository at this point in the history
  • Loading branch information
sh1mj1 committed Dec 1, 2024
1 parent e9c73c5 commit 13e7c78
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,16 @@ class TypeResultViewHolder(private val binding: ItemTypeResultBinding) :
private fun bindTypeResultView(
typeResultView: TypeResultView,
matchedTypeResource: MatchedTypeResource,
) = SpannedString(typeResultView.text).drawable(
iconDrawable = matchedTypeResource.iconDrawable,
iconSize = (typeResultView.textView.textSize * 1.2).toInt(),
fullText = typeResultView.text,
).style(
targetWord = matchedTypeResource.typeName,
fullText = typeResultView.text,
).color(
fullText = typeResultView.text,
targetWord = matchedTypeResource.matchedResult,
color = matchedTypeResource.matchedResultColor,
)
): SpannedString =
SpannedString(typeResultView.text).drawable(
iconDrawable = matchedTypeResource.iconDrawable,
iconSize = (typeResultView.textView.textSize * 1.2).toInt(),
).style(
targetWord = matchedTypeResource.typeName,
).color(
targetWord = matchedTypeResource.matchedResult,
color = matchedTypeResource.matchedResultColor,
)
}

private data class MatchedTypeResource(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,105 +4,41 @@ import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.text.Spannable
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.SpannedString
import android.text.style.ForegroundColorSpan
import android.text.style.ImageSpan
import android.text.style.StyleSpan

fun SpannableString.drawable(
fullText: String,
iconDrawable: Drawable?,
/**
* 잦은 체이닝을 사용한다면 SpannableString 의 확장 함수를 사용하세요
*/
fun SpannedString.drawable(
iconDrawable: Drawable,
iconSize: Int,
) {
val iconIndex = fullText.indexOf("|")
delimiter: String = "|",
): SpannedString {
val spannable = SpannableString(this)
val iconIndex = indexOf(delimiter)
if (iconIndex != -1) {
val drawable =
iconDrawable?.apply {
setBounds(0, 0, iconSize, iconSize)
}
val imageSpan = ImageSpan(drawable!!, ImageSpan.ALIGN_BOTTOM)
setSpan(imageSpan, iconIndex, iconIndex + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}

fun SpannableString.color(
targetWord: String,
color: Int,
fullText: String,
): SpannableString {
val startIndex = fullText.indexOf(targetWord)
if (startIndex != -1) {
this.setSpan(
ForegroundColorSpan(color),
startIndex,
startIndex + targetWord.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
iconDrawable.setBounds(0, 0, iconSize, iconSize)
spannable.setSpan(
ImageSpan(iconDrawable, ImageSpan.ALIGN_BOTTOM),
iconIndex,
iconIndex + 1,
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
return this
return SpannedString(spannable)
}

fun SpannableString.style(
targetWord: String,
fullText: String,
): SpannableString {
val startIndex = fullText.indexOf(targetWord)
fun SpannedString.style(targetWord: String): SpannedString {
val spannable = SpannableString(this)
val startIndex = indexOf(targetWord)
if (startIndex != -1) {
this.setSpan(
spannable.setSpan(
StyleSpan(Typeface.BOLD),
startIndex,
startIndex + targetWord.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
return this
}

fun SpannableStringBuilder.color(
fullText: String,
colorTargetWord: String,
color: Int,
) {
val colorStartIndex = fullText.indexOf(colorTargetWord)
if (colorStartIndex != -1) {
setSpan(
ForegroundColorSpan(color),
colorStartIndex,
colorStartIndex + colorTargetWord.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
}

fun SpannableStringBuilder.style(
fullText: String,
fontStyleTargetWord: String,
) {
val fontStyleStartIndex = fullText.indexOf(fontStyleTargetWord)
if (fontStyleStartIndex != -1) {
setSpan(
StyleSpan(Typeface.BOLD),
fontStyleStartIndex,
fontStyleStartIndex + fontStyleTargetWord.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
}

fun SpannedString.drawable(
iconDrawable: Drawable?,
iconSize: Int,
fullText: String,
): SpannedString {
val spannable = SpannableString(this)
val iconIndex = fullText.indexOf("|")
if (iconIndex != -1) {
iconDrawable?.setBounds(0, 0, iconSize, iconSize)
spannable.setSpan(
ImageSpan(iconDrawable!!, ImageSpan.ALIGN_BOTTOM),
iconIndex,
iconIndex + 1,
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
Expand All @@ -112,10 +48,9 @@ fun SpannedString.drawable(
fun SpannedString.color(
targetWord: String,
color: Int,
fullText: String,
): SpannedString {
val spannable = SpannableString(this)
val startIndex = fullText.indexOf(targetWord)
val startIndex = indexOf(targetWord)
if (startIndex != -1) {
spannable.setSpan(
ForegroundColorSpan(color),
Expand All @@ -127,19 +62,48 @@ fun SpannedString.color(
return SpannedString(spannable)
}

fun SpannedString.style(
targetWord: String,
fullText: String,
): SpannedString {
val spannable = SpannableString(this)
val startIndex = fullText.indexOf(targetWord)
fun SpannableString.drawable(
iconDrawable: Drawable,
iconSize: Int,
delimiter: String = "|",
): SpannableString {
val iconIndex = indexOf(delimiter)
if (iconIndex != -1) {
val drawable =
iconDrawable.apply {
setBounds(0, 0, iconSize, iconSize)
}
val imageSpan = ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM)
setSpan(imageSpan, iconIndex, iconIndex + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
return this
}

fun SpannableString.style(targetWord: String): SpannableString {
val startIndex = indexOf(targetWord)
if (startIndex != -1) {
spannable.setSpan(
this.setSpan(
StyleSpan(Typeface.BOLD),
startIndex,
startIndex + targetWord.length,
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
return SpannedString(spannable)
return this
}

fun SpannableString.color(
targetWord: String,
color: Int,
): SpannableString {
val startIndex = indexOf(targetWord)
if (startIndex != -1) {
this.setSpan(
ForegroundColorSpan(color),
startIndex,
startIndex + targetWord.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
return this
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package poke.rogue.helper.presentation.util
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.text.SpannableString
import android.text.SpannedString
import android.text.style.ForegroundColorSpan
import android.text.style.ImageSpan
import android.text.style.StyleSpan
Expand All @@ -26,18 +27,19 @@ class SpannableStringExtensionsKtTest {
}

@Test
fun `아이콘이 버티컬 라인 위치에 drawable 스팬이 정확히 추가된다`() {
val fullText = "Fire is | strong against Grass"
fun `SpannableString에 아이콘이 버티컬 라인 위치에 정확히 추가된다`() {
val delimiter = "|"
val fullText = "Fire is $delimiter strong against Grass"
val spannable = SpannableString(fullText)

val iconSize = 50

spannable.drawable(fullText, mockDrawable, iconSize)
spannable.drawable(iconDrawable = mockDrawable, iconSize = iconSize, delimiter = delimiter)

// getImageSpans only
val spans = spannable.getSpans(0, spannable.length, ImageSpan::class.java)
val imageSpan = spans[0] ?: error("ImageSpan not found")
spans.size shouldBe 1

val imageSpan = spans[0]
val start = spannable.getSpanStart(imageSpan)
val end = spannable.getSpanEnd(imageSpan)

Expand All @@ -46,18 +48,18 @@ class SpannableStringExtensionsKtTest {
}

@Test
fun `컬러 스팬이 strong 위치에 적용된다`() {
fun `SpannableString에 컬러 스팬이 특정 단어 위치에 정확히 적용된다`() {
val fullText = "Fire is strong against Grass"
val targetWord = "Grass"
val color = 0xFFFF0000.toInt() // Red color

val spannable = SpannableString(fullText)
spannable.color(targetWord, color, fullText)
spannable.color(targetWord, color)

// getColoredSpan Only
val spans = spannable.getSpans(0, spannable.length, ForegroundColorSpan::class.java)
val colorSpan = spans[0] ?: error("ColorSpan not found")
spans.size shouldBe 1

val colorSpan = spans[0]
val start = spannable.getSpanStart(colorSpan)
val end = spannable.getSpanEnd(colorSpan)

Expand All @@ -66,17 +68,17 @@ class SpannableStringExtensionsKtTest {
}

@Test
fun `폰트 스타일 스팬이 Fire 위치에 적용된다`() {
fun `SpannableString에 폰트 스타일 스팬이 특정 단어 위치에 정확히 적용된다`() {
val fullText = "Fire is strong against Grass"
val targetWord = "Fire"

val spannable = SpannableString(fullText)
spannable.style(targetWord, fullText)
spannable.style(targetWord)

// getStyleSpan Only
val spans = spannable.getSpans(0, spannable.length, StyleSpan::class.java)
val styleSpan = spans[0] ?: error("StyleSpan not found")
spans.size shouldBe 1

val styleSpan = spans[0]
val start = spannable.getSpanStart(styleSpan)
val end = spannable.getSpanEnd(styleSpan)

Expand All @@ -85,4 +87,71 @@ class SpannableStringExtensionsKtTest {

styleSpan.style shouldBe Typeface.BOLD
}

@Test
fun `SpannedString에 아이콘이 버티컬 라인 위치에 정확히 추가된다`() {
val delimiter = "|"
val fullText = "Fire is $delimiter strong against Grass"
val spannedString = SpannedString(fullText)

val iconSize = 50
val updatedSpanned =
spannedString.drawable(
iconDrawable = mockDrawable,
iconSize = iconSize,
delimiter = delimiter,
)

val spans = updatedSpanned.getSpans(0, updatedSpanned.length, ImageSpan::class.java)
spans.size shouldBe 1

val imageSpan = spans[0]
val start = updatedSpanned.getSpanStart(imageSpan)
val end = updatedSpanned.getSpanEnd(imageSpan)

fullText.indexOf("|") shouldBe start
fullText.indexOf("|") + 1 shouldBe end
}

@Test
fun `SpannedString에 컬러 스팬이 특정 단어 위치에 정확히 적용된다`() {
val fullText = "Fire is strong against Grass"
val targetWord = "Grass"
val color = 0xFFFF0000.toInt() // Red color

val spannedString = SpannedString(fullText)
val updatedSpanned = spannedString.color(targetWord, color)

val spans =
updatedSpanned.getSpans(0, updatedSpanned.length, ForegroundColorSpan::class.java)
spans.size shouldBe 1

val colorSpan = spans[0]
val start = updatedSpanned.getSpanStart(colorSpan)
val end = updatedSpanned.getSpanEnd(colorSpan)

fullText.indexOf(targetWord) shouldBe start
fullText.indexOf(targetWord) + targetWord.length shouldBe end
}

@Test
fun `SpannedString에 폰트 스타일 스팬이 특정 단어 위치에 정확히 적용된다`() {
val fullText = "Fire is strong against Grass"
val targetWord = "Fire"

val spannedString = SpannedString(fullText)
val updatedSpanned = spannedString.style(targetWord)

val spans = updatedSpanned.getSpans(0, updatedSpanned.length, StyleSpan::class.java)
spans.size shouldBe 1

val styleSpan = spans[0]
val start = updatedSpanned.getSpanStart(styleSpan)
val end = updatedSpanned.getSpanEnd(styleSpan)

fullText.indexOf(targetWord) shouldBe start
fullText.indexOf(targetWord) + targetWord.length shouldBe end

styleSpan.style shouldBe Typeface.BOLD
}
}

0 comments on commit 13e7c78

Please sign in to comment.