본문 바로가기

Kotlin/안드로이드 공부

Spans - 문자열 중 숫자에만 텍스트 효과 적용하기

728x90

Spans : 마크업 객체로 text 를 style 하는데 사용

 

  • SpannedString : 텍스트 변경 불가. 마크업 변경 불가
  • SpannableString :  텍스트 변경 불가. 마크업 변경 가능.
  • SpannableStringBuilder :  텍스트 변경 가능. 마크업 변경 가능.

 

1. 레이아웃에서 textView 의 bufferType 속성을 spannable 로 설정한다. ( normal/spannable/editable )

 

2. 코드 상에서 코드를 구현한다.

 

val ssb = SpannableStringBuilder ("문자열")

binding.textView.text = ssb.apply {
	setSpan(적용할 효과, 효과의 시작 지점, 효과의 끝나는 지점, 문자열 추가 시 span 값 적용 유무)
}

 

1) 적용할 효과

ForegroundColorSpan : 전경 색상값

BackgroundColorSpan : 배경 색상값

UnderlineSpan : 밑줄

ClickableSpan : 문자열 클릭 이벤트

AbsoluteSizeSpan : 크기 변경

RelativeSizeSpan : 크기 변경

ImageSpan : 이미지 데이터

StyleSpan : 스타일

URLSpan : URL 하이퍼 링크화 및 이벤트

 

2) 효과의 시작 인덱스

 

3) 효과의 마지막 인덱스

 

4) 문자열 추가시 span 값 적용 유무

span이 적용된 곳에 왼쪽 또는 오른쪽에 문자열이 추가될 때 어떻게 처리할지

 

Spanned.SPAN_EXCLUSIVE_EXCLUSIVE : 왼쪽, 오른쪽에 새로운 텍스트가 추가될 때 Span 적용 x

Spanned.SPAN_EXCLUSIVE_INCLUSIVE : 왼쪽 x 오른쪽 o

Spanned.SPAN_INCLUSIVE_EXCLUSIVE : 왼쪽 o 오른쪽 x

Spanned.SPAN_INCLUSIVE_INCLUSIVE : 왼쪽 o 오른쪽 o

 


적용

다국어 문자열 리소스의 숫자 부분에만 텍스트 효과(색상 변경)를 적용하고 싶었다.

 

다국어 옵션과 숫자 자릿수의 변경에 따라 효과를 적용할 인덱스 범위가 변경되기 때문에

문자열에서 숫자가 시작하는 부분숫자가 끝나는 부분의 인덱스 값을 찾아 넣어주었다.

 

(단, 문자열에서 숫자가 한 번만 사용되는 경우임)

 

val firstNumIndex = ssb.indexOfFirst { char -> Character.isDigit(char) }
val lastNumIndex = ssb.indexOfLast { char -> Character.isDigit(char) }

 

 

숫자 시작, 끝 지점 인덱스 구하기 (*띄어쓰기 포함)

 

ex) "총 3번" 

firstNumIndex = 2

lastNumIndex = 2

 

ex) "총 100번"

firstNumIndex = 2

lastNumIndex = 4

 

ex) "Total 100"

firstNumIndex = 6

lastNumIndex = 8

 

최종 코드

 

val ssb = SpannableStringBuilder(
    getString(
        R.string.count,
        Count
    )
)
val firstNumIndex = ssb.indexOfFirst { char -> Character.isDigit(char) }
val lastNumIndex = ssb.indexOfLast { char -> Character.isDigit(char) }

binding.textView.text = ssb.apply {
    setSpan(
        ForegroundColorSpan(
            ContextCompat.getColor(
                this,
                R.color.RED
            )
        ), firstNumIndex, lastNumIndex + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
    )
}