TextView 갖고 놀기

CmplxN·2020년 11월 29일
0

글 줄임 TextView

개발의 기본 중 기본은 게시판 만들기다.
유저들은 게시판의 제목 등을 보고 게시판의 상세 내용 페이지로 들어간다.
이때 게시판의 제목 대신 게시판의 상세 내용 일부를 통해 상세 내용 페이지로 들어가는 경우도 있다.
아래 스샷을 참고하자.

그러면 이와 비슷한 커스텀 뷰를 만들어보는 과정을 간단하게 알아보자.

본문 가공하기

본문을 가공할 때는 원래 나타내려는 텍스트가 그대로 보여지면 몇줄이 되는지 알아야한다.
이를 위해 TextView.getLineCount()를 사용한다.

TextView.getLineCount()를 사용하면 현재 TextView에서의 Line수를 세준다.
하지만 주의해야할 점은 내부 레이아웃이 다 그려지지 않으면 보여지지 않는다.
그러므로 가공되기 전의 text를 super.setText()해야 lineCount를 쓸 수 있다.

lineCount를 사용할 수 있게 된 다음에는 마지막 라인의 문자열 index를 알아야 한다.
마지막 라인의 문자열 index를 기준으로 문자를 하나씩 지울 것이므로 index를 eraser로 칭하겠다.
이는 Layout.getLineEnd() 함수를 이용해 쉽게 구할 수 있다.

그런 다음부터 readMoreText(위 스샷에서 ... 더보기)를 붙일 위치를 찾는다.
아까 구한 eraserPaint.measureText()함수를 사용한다.
Paint.measureText()를 이용해 현재 TextView의 text에 쓰는 Paint로 텍스트의 길이를 알 수 있다.
그것을 TextView의 너비인 getWidth()와 비교하고 조건이 맞을때까지 eraser로 줄임의 시작 index를 찾는다.

말이 길어져 무슨말인지 알아듣기 어려우므로 코드로 직접 확인하자

var spannable = SpannableStringBuilder(lastLineText).append(readMoreText)
while (paint.measureText(spannable, 0, spannable.length) > width) {
    spannable = spannable.delete(eraser, eraser + 1)
    --eraser
}

마지막 줄을 완성한 다음 [그전 줄 텍스트 + '\n' + spannable]을 super.setText()하면 된다.

주의할 점

논리적으로는 위에서 풀어 설명한 것을 코딩하면 원하는 결과가 나와야 한다.
여기서 주의할 점 두가지를 보고 가자.

  1. 말줄임 텍스트 가공은 layout 구성이 먼저 끝나야 한다.
    그러므로 원본의 super.setText() call 이후 post를 통해 호출한다.
    아래와 같은 형식으로 말이다.
super.setText(text)
post { getEditedText() }
  1. 빈 text를 걸러주는 로직이 필요하다.
    데이터 바인딩을 이용할 때 이런 경우가 발생하는 것 같은데
    super.setText()에 빈 문자열이 들어가는 현상이 나타난다.
    이런 오작동을 방지하기 위해 빈 문자열이 getEditedText() 로직을 통과해 super.setText()되지 않도록 하자.

고정 사이즈 만들기

덤으로 이 커스텀 뷰의 높이를 일정하게 만드는 방법에 대해 알아보자.
최대 줄수를 5로 한다고 하자. 이때 모든 TextView가 같은 높이이길 바랄 수 있다.
이때 기존 TextView의 attribute 중 하나인 minLines를 설정하면 될 수도 있다.

"될 수도 있다"라는 것은 단순 minLines만으로는 살짝 어색하게 구현되기 때문이다.

문제의 원인은 LineSpacing이다.
minLines로는 마지막 줄의 LineSpacing이 들어가지 않는다. 왜죠??
그러므로 가로방향 RecyclerView나 ViewPager2에서 아이템들의 높이가 미세하게 달라진다.

그래서 생각해낸 해결책은 줄수가 부족하면 가짜 텍스트나 투명한 텍스트를 넣는 것이다.
그러면 유저한테 보이지는 않지만 어쨋든 텍스트가 있기에 LineSpacing이 생긴다.

fixedSize라는 별도의 attr을 만들어 xml에서 설정하게 했다.
그리고 getTrimmedText()에 분기를 추가하여 line수가 모자를 때 개행문자로 채우게 했다.
그렇게 해서 텍스트 라인수, LineSpacing과 무관하게 같은 높이로 만들 수 있게 되었다.

Bold 처리하기

안드로이드 TextView에서 Bold를 처리하는 방법은 간단하다.

android:textStyle="bold"
tv.setTypeface(tv.typeface, Typeface.BOLD)

와 같은 방식으로 설정하면 그만이다.

하지만 일부분만 Bold처리하거나, 상황에 따라 Bold였다가 Normal이었다가 바뀌는 경우를 보자.

일부만 Bold 처리하기

사실 Html을 생각하면 Bold 처리는 어려운 문제가 아니다.
사실 검색해 보면 쉽게 나오지만, 안드로이드 TextView에서도 간단하게 html 문법을 적용시킬 수 있다.
setText에 인자로 Html.fromHtml함수를 사용해 Spanned를 넘겨주는 것이다.

tv.text = Html.fromHtml("new <b>world</b>",Html.FROM_HTML_MODE_LEGACY)

와 같이 말이다.

여기서 주의할 점이 하나 있는데, string resource에서 가져올 때는 <가 인식되지 않는다.
그러므로 <대신 &lt;를 사용하도록 하자.

상황에 따라 Bold와 Normal 바꾸기

상황에 따라 아래와 같은 코드로 Bold였다가 Normal이었다가 바꿔보자.

tv.setTypeface(tv.typeface, Typeface.BOLD)
tv.setTypeface(tv.typeface, Typeface.NORMAL)

이런 방식으로 바꾸면 처음 한번은 Normal에서 Bold로 잘 바뀔 것이다.
그러나 한번 Bold로 바뀐 다음에는 Normal로 돌아오지 못할 것이다.

이럴 때는

tv.setTypeface(Typeface.create(tv.getTypeface(), Typeface.NORMAL))

와 같은 방식으로 기존의 Typeface를 날려줘야 Bold가 사라진다.

profile
Android Developer

0개의 댓글