[안드로이드] Spannable을 사용해 문자열 꾸미기

hee09·2021년 10월 19일
1

안드로이드

목록 보기
7/20
post-thumbnail
post-custom-banner

이 글은 깡쌤의 안드로이드 프로그래밍 책을 보고 작성하였습니다.

1. Spannable

Spannable은 뷰가 아니라 문자열 데이터를 표현하기 위한 클래스입니다. 이 Spannable을 이용해 TextView의 문자열 데이터를 다양하게 출력할 수 있는데 예제와 함께 알아보겠습니다.

1.1 Spannable의 필요성

안드로이드에서 문자열의 기초는 String 클래스가 아닌 CharSequence입니다. 그 이유는 문자열이 데이터뿐만이 아니라, UI 정보까지 포함해야 하기 때문입니다. 위의 그림의 클래스들은 문자열 데이터를 표현합니다. 이 클래스들을 이용해서 TextView를 꾸미는데, 그 방법은 Editable, SpannableString, SpannableStringBuilder 클래스의 문자열 데이터와 문자열 데이터의 UI 정보인 Spannable을 함께 표현하는 것입니다.


Layout의 xml 파일에서 TextView를 꾸미면 되지 않나 생각할 수도 있지만 다음의 예제를 보면 왜 Spannable을 사용하는지에 대하여 알 수 있습니다.

Hello World

위의 TextView를 안드로이드 앱에서 출력한다고 가정하겠습니다. H라는 글자는 굵게, W라는 글자는 붉은색으로 표현되어 있는데, 이를 만약 xml 파일에서 해당 TextView의 속성으로 예시와 함께 변경하려 한다면 모든 글자가 변경되지 하나의 글자가 변경되지는 않습니다.

결국, UI를 뷰의 설정으로 표현하지 않고, 데이터를 표현하는 정보가 담긴 데이터소를 가지고, 뷰는 그 정보를 참조해서 화면에 출력하는 방식이 필요합니다. 그래서 Spannable이 사용됩니다.

1.2 Spannable 적용

Spannable이 적용된 TextView에 이미지를 넣고 원하는 글자를 굵게 처리하는 과정을 소스코드로 알아보겠습니다.

<!-- bufferType을 Spannable로 적용 -->
<TextView
    android:id="@+id/spanView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:bufferType="spannable" />

우선 TextView가 Spannable을 참조해서 화면에 출력하려면 buffertype라는 속성을 지정해야 합니다. TextView의 bufferType 기본값은 "normal"로 Spannable의 값을 적용하지 않는다는 의미입니다.


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val spanView = findViewById<TextView>(R.id.spanView)
        // 해당 TextView의 키 이동을 처리하는 MovementMethod를 지정한다
        // ScrollingMovementMethod : 텍스트 버퍼를 스크롤하여 이동을 해석하는 이동 방법
        spanView.movementMethod = ScrollingMovementMethod()

        // SpannableStringBuilder에 들어갈 데이터
        val data = "복수초 \n img \n 이른봄 설산에서 만나는 복수초는 모드 야생화 찍사들의 로망이 아닐까 싶다"
        // SpannableStringBuilder 객체 생성
        val builder: SpannableStringBuilder = SpannableStringBuilder(data)
        // img라는 곳에 이미지를 출력하기 위한 인덱스
        var startIndex = data.indexOf("img")
        // img라는 글자가 존재한다면
        if(startIndex > -1) {
            // 끝나는 인덱스 구하기
            val endIndex = startIndex + "img".length
            // 이미지 얻어오기
            val dr: Drawable? = ResourcesCompat.getDrawable(resources, R.drawable.img1, null)
            // 이미지 크기 지정(lfet, top, right, bottom)
            dr?.setBounds(0, 0, dr.intrinsicWidth, dr.intrinsicHeight)
            // ImageSpan 생성
            val span: ImageSpan = ImageSpan(dr!!)
            // Span 정보를 SpannableStringBuilder의 setSpan 메서드를 이용해 설정
            builder.setSpan(span, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        }

        // 복수초라는 글자를 꾸미기위한 복수초글자 인덱스
        startIndex = data.indexOf("복수초")
        if(startIndex < -1) {
            val endIndex = startIndex + "복수초".length
            // 텍스트 스타일을 지정하기 위한 Span 생성
            val styleSpan: StyleSpan = StyleSpan(Typeface.BOLD)
            // 사이즈를 변경하기 위한 Span 생성
            val sizeSpan: RelativeSizeSpan = RelativeSizeSpan(2.0f)
            // span 지정
            builder.setSpan(styleSpan, startIndex, endIndex + 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
            builder.setSpan(styleSpan, startIndex, endIndex + 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        }
        
        // TextView의 텍스트로 SpannableStringBuilder 설정
        spanView.text = builder
    }
}

위 코드의 순서는 다음과 같습니다(간단한 예제입니다)

  • SpannableStringBuilder의 객체를 생성하며 이를 적용할 문자열을 생성자로 넣습니다.
  • 꾸미려는 문자의 인덱스 번호를 얻어옵니다(위의 예제에서는 복수초 또는 img 글자)
  • 문자를 꾸미기 위한 Span 클래스의 객체(ImageSpan, StyleSpan 등)를 생성합니다.
  • SpannableStringBuilder의 setSpan을 사용해 만든 Span을 적용하면 TextView가 꾸며집니다.

위 코드의 결과는 다음과 같습니다.

라이브러리에서 문자열을 다양하게 꾸미기 위해 다음과 같은 Span 클래스를 제공합니다.

  • ForegroundColorSpan : 전경 색상값 적용
  • BackgroundColorSpan : 배경 색상값 적용
  • UnderlineSpan : 밑줄 적용
  • ClickableSpan : 문자열 클릭 이벤트 적용
  • AbsoluteSizeSpan : 크기 변경 적용
  • ImageSpan : 이미지 데이터 적용
  • RelativeSizeSpan : 크기 적용
  • StyleSpan : 스타일 적용
  • URLSpan : URL 링크 모양과 클릭 이벤트 적용
profile
되새기기 위해 기록
post-custom-banner

0개의 댓글