이 글은 깡쌤의 안드로이드 프로그래밍 책을 보고 작성하였습니다.
Spannable은 뷰가 아니라 문자열 데이터를 표현하기 위한 클래스입니다. 이 Spannable을 이용해 TextView의 문자열 데이터를 다양하게 출력할 수 있는데 예제와 함께 알아보겠습니다.
안드로이드에서 문자열의 기초는 String 클래스가 아닌 CharSequence입니다. 그 이유는 문자열이 데이터뿐만이 아니라, UI 정보까지 포함해야 하기 때문입니다. 위의 그림의 클래스들은 문자열 데이터를 표현합니다. 이 클래스들을 이용해서 TextView를 꾸미는데, 그 방법은 Editable, SpannableString, SpannableStringBuilder 클래스의 문자열 데이터와 문자열 데이터의 UI 정보인 Spannable을 함께 표현하는 것입니다.
Layout의 xml 파일에서 TextView를 꾸미면 되지 않나 생각할 수도 있지만 다음의 예제를 보면 왜 Spannable을 사용하는지에 대하여 알 수 있습니다.
Hello World
위의 TextView를 안드로이드 앱에서 출력한다고 가정하겠습니다. H라는 글자는 굵게, W라는 글자는 붉은색으로 표현되어 있는데, 이를 만약 xml 파일에서 해당 TextView의 속성으로 예시와 함께 변경하려 한다면 모든 글자가 변경되지 하나의 글자가 변경되지는 않습니다.
결국, UI를 뷰의 설정으로 표현하지 않고, 데이터를 표현하는 정보가 담긴 데이터소를 가지고, 뷰는 그 정보를 참조해서 화면에 출력하는 방식이 필요합니다. 그래서 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
}
}
위 코드의 순서는 다음과 같습니다(간단한 예제입니다)
위 코드의 결과는 다음과 같습니다.
라이브러리에서 문자열을 다양하게 꾸미기 위해 다음과 같은 Span 클래스를 제공합니다.