이번에 하고 있는 프로젝트에서 TextView에 '더보기' 기능을 사용하려 했다.
저 TextView를 클릭하게 되면, 2줄이 지나 숨겨져 있던 텍스트들이 쨘 하고 나타나게 되는 기능이다.
내가 사용했던 방법은 AppCompatTextView를 상속받아서 이를 커스텀해서 사용 하는 방식이다.
일단, 전체 코드는 다음과 같다.
ExpandableTextView.kt
class ExpandableTextView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {
private var originalText: String = ""
private var trimmedText: String = ""
private var trimmedLength: Int = 0
private var isExpanded: Boolean = false
private val expandText = " 더보기"
init {
maxLines = 2
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if (changed) {
trimText()
}
}
private fun trimText() {
originalText = text.toString()
val availableWidth = width - marginLeft - marginRight
val textPaint = TextPaint(paint)
val staticLayout = StaticLayout.Builder.obtain(
originalText,
0,
originalText.length,
textPaint,
availableWidth
)
.setMaxLines(maxLines)
.build()
if (staticLayout.lineCount > maxLines) {
val lastLineStart = staticLayout.getLineStart(maxLines - 1)
val lastLineEnd = staticLayout.getLineEnd(maxLines - 1)
var lastLineText = originalText.substring(lastLineStart, lastLineEnd)
val expandTextWidth = textPaint.measureText(expandText)
var ellipsizedLength =
textPaint.breakText(lastLineText, true, availableWidth - expandTextWidth, null)
trimmedLength = lastLineStart + ellipsizedLength
trimmedText = originalText.substring(0, trimmedLength - 1).trim() + "..." + expandText
text = trimmedText
} else {
trimmedText = originalText
text = originalText
}
}
fun toggleExpand() {
isExpanded = !isExpanded
text = if (isExpanded) {
maxLines = Integer.MAX_VALUE
originalText
} else {
trimmedText
}
}
fun isTextTrimmed(): Boolean = trimmedText != originalText
}
커스텀 뷰에 대한 내용은 나중에 따로 정리할 계획이다.
일단 그 안에 있는 코드를 보자
private var originalText: String = "" // 전체 텍스트
private var trimmedText: String = "" // 더보기가 나오기전 텍스트
private var trimmedLength: Int = 0 // 더보기가 나오기전 텍스트 길이
private var isExpanded: Boolean = false // 전체 텍스트가 나오게 할지 여부
private val expandText = " 더보기" // 자를때 나오는 텍스트(나는 "더보기"로)
init {
maxLines = 2
}
초기 최대 줄 개수를 2줄로 지정해서 그 뒤에는 ellipsize로 "...더보기"로 처리하게끔 할 것 이다.
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if (changed) {
trimText()
}
}
커스텀뷰의 핵심은 onMeasure(), onDraw(), onLayout() 이라고 한다.
간단히 설명하자면
onMeasure() -> 전체 그릴 크기를 설정한다. = 도화지의 크기 설정
onLayout() -> 어느 위치에 그림을 그릴지 설정
onDraw() -> 어떤 그림을 그릴지 설정
여기서 onLayout()을 사용한 이유는
이 TextView 의 사이즈나 포지션이 바뀌었는지 확인하기 위함이다.
이를 통해 변경이 이루어졌다면 trimText()라는 함수를 실행해, 기존 전체 텍스트를 뷰에 띄우려고 한다.
val availableWith = width - marginLeft+ marginRight
TextView의 전체 너비에서 좌우 마진을 뺀 실제 사용 가능한 너비를 계산한다.
val textPaint = TextPaing(paint)
TextPaint는 Paint() 객체를 상속받은 것으로, 텍스트 처리에 있어서 Paint() 보다 기능이 많기 때문에 얘를 사용한다.
val staticLayout = StaticLayout.Builder.
obtain(originalText, 0, originalText.length, textPaint, availableWidth)
.setMaxLines(maxLines)
.build()
StaticLayout을 통해 현재 TextView에 이용 가능한 길이와, 전체 텍스트 길이, maxLines 설정을 통해 얘가 알아서 끊어주고 ellipsize와 더보기를 그릴 공간을 남겨 주도록 한다.
val lastLineStart = staticLayout.getLineStart(maxLines - 1)
val lastLineEnd = staticLayout.getLineEnd(maxLines - 1)
var lastLineText = originalText.substring(lastLineStart, lastLineEnd)
최대 줄 수에 해당하는 마지막 줄의 시작과 끝 인덱스를 출력한다. (maxLines - 1)에 해당하는.
lastLineStart -> 마지막 줄의 시작
lastLineEnd -> 마지막 줄의 끝
lastLineText -> 마지막 줄의 시작부터 마지막 줄의 끝에 해당하는 텍스트를 추출한다.
val expandTextWidth = textPaint.measureText(expandText)
여기서는 '더보기'에 해당하는 텍스트의 너비를 계산한다.
var ellipsizedLength = textPaint.breakText(lastLineText, true, availableWidth - expandTextWidth, null)
breakText() 의 경우 문자열이 지정한 길이보다 넘을 시 잘리는 위치를 반환해준다.
즉, 마지막 줄의 텍스트에서 "..."와 "더보기"를 제외하여 나올 수 있는 Text 길이를 구해준다.
trimmedLength = lastLineStart + ellipsizedLength
trimmedText = originalText.substring(0, trimmedLength - 1).trim() + "..." + expandText
그 뒤, 마지막으로 원본 텍스트에서 지금까지 계산하여 나타나게 될 글자를 추리고, "..."와 "더보기"를 넣으면 된다!
holder.binding.tvContent.setOnClickListener {
if (holder.binding.tvContent.isTextTrimmed()) {
holder.binding.tvContent.toggleExpand()
notifyItemChanged(position)
}
}
그 뒤 넣고 싶은 클릭 이벤트에 다음과 같이 ExpandalbeTextView()의 toggleExpand()를 호출하고, Item을 갱신하면
더보기 안에 있던 원본 텍스트가 나타나게 된다.
'Android > Android 적용' 카테고리의 다른 글
[Android 적용] Health Service API(with.watch OS) (1) | 2024.11.25 |
---|---|
[Android 적용] debounce 코루틴 구현 (0) | 2024.06.29 |
[Android 적용] 네이버 지도 API(3) - 클러스터링 마커 (0) | 2024.06.13 |
[Android 적용] 네이버 지도 API(2) - 위치 (0) | 2024.06.10 |
[Android 적용] 네이버 지도 API(1) - 지도 띄우기(with.mapView) (2) | 2024.06.10 |