ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • TextView Custom 해보기. (ellipsize="end" & more 붙이기)
    Android(+ Kotlin) 2021. 2. 25. 19:10

    안드로이드는 컴포넌트들이 워낙 잘 되어 있어 별도의 커스텀화 시켜서 사용하는 경우가 없었다.

    하지만 이번에 프로젝트로 인해 필요성이 느껴 개발해보기로 했다. 

     

    TextView를 커스텀해서 사용하기

    인스타, 페북은 메인 피드공간을 줄이기 위해 "... 더 보기"를 이용해 텍스트를 줄이고 있다.

    TextView의 옵션중에는 "lines"옵션을 통해 줄 수 제한을 걸고 "ellipsize" 옵션을 쓰면 그 이상의 길이를 ... 으로 표시한다.

    대부분 ... 으로 끝낼 수도 있지만, 추가적으로 ... more, ... 더 보기 등의 글자를 덧붙이고 색깔과 터치액션을 줄 수 있다.

     

    이번 글에서는 글자만 덧붙이기만 해보려 한다.

    시작해보자

     

    첫번째 TextView 상속받기

    class MyTextView: TextView {
    }

    이렇게 넣으면 얼마나 좋으련만.... 상속이 가능하지 않겠냐만.. TextView자체를 상속 받을 수는 없다. ~~~~ 빨간 밑줄과 함께 안내에는 AppCompatTextView 사용하라고 한다. 안내에 따라 수정하면 다시 붉은줄이 나타난다. 다시 한번 확인하면 아래 이미지와 같이 나타난다.

    두번째에 있는 ~~~~ @JvmOverloads를 선택한다. 

    * 안드로이드의 컴포넌트를 상받아 사용할 때 필수 파라미터로 context, atrributeset, defstyleattr(default style Attributeset)이 존재하여 상속시 필수로 작성해야한다. (여러 작성방법이 있는데 나는 이 방식이 편리하고 마음에 든다)

     

    두번째 viewTreeObserver사용하기

    이것이 무엇인지 먼저 살펴보자 (developer.android.com/reference/android/view/ViewTreeObserver)

    더보기

    A view tree observer is used to register listeners that can be notified of global changes in the view tree. Such global events include, but are not limited to, layout of the whole tree, beginning of the drawing pass, touch mode change.... A ViewTreeObserver should never be instantiated by applications as it is provided by the views hierarchy. 

    공식문서에서는 "뷰트리 전역의 변경사항을 알릴 수 있는 리스너를 등록하는데 사용된다. 글로벌 이벤트에는 layout of the whole tree, beginning of the drawing pass, touch mode change등이 포함되지만 이에 국한하지 않는다. ViewTreeObserver는 뷰 계층 구조에서 제공하므로 애플리케이션에서 인스터스화해서는 안된다." 라고 나와 있다.

    완벽히 이해할 수 없지만.

    간단하게, listener를 등록하여 해당 인터페이스에 맞는 상황에서 이벤트가 발생되는 형태라고 보면 될것 같다.

    내부에는 8개의 interface가 존재 한다.(해당 위 공식문서를 참조해보자.)

    뷰를 그릴때, 포커스가 바뀔때, 뷰가 그려질때, 스크롤 상태가 변경될때, 터치 모드가 변경될때 ...등이 있으며 해당 상태일때 listener가 호출된다.

     

    세번째 OnGlobalLyaoutListener 사용하기

    8개의 interface중 GlobalLayoutListener를 사용는데, 전체 뷰가 그려질때 호출하려고 한다.

     

    viewTreeObserver.addOnGlobalLayoutListener(object :
                ViewTreeObserver.OnGlobalLayoutListener {
                override fun onGlobalLayout() {
                    
                }
    
            })
    viewTreeObserver.addOnGlobalLayoutListener {
    }

    위에 방식대로 해도되고, 아래 방식을 사용해도 된다. 후자는 람다식 표현으로 간략하게 표현을 할 수 있다. (안드로이드 스튜디오에서 노란색 전구가 나와서 후자형태를 추천한다.)

     

    * 해당 listener가 등록될 경우 view의 변화가 있을 때 마다 호출된다.

    리소스 낭비가 심할 수 있고, 추가로 오작동 이슈가 발생 할 수있으니 유의하며, removeOnGlobalLayoutListener 함수를 통해 해당 listener을 제거해주는것도 좋은 방법이 될것 같다.

     

    네번째 TextView 함수&변수 사용하기.

    매우 아쉽게 TextView에는 적용된 text를 가져올 수 있지만, 축약된 글자만큼을 가지고 올 수는 없다. 하지만 아래 함수를 활용하면 비슷하게 갖고 올 수 있다.

     

    - getEllipsisCount(int line)

    해당 라인의 생략될 길이를 반환한다.

    0 : 생략이 없다.

    {n} : 생략될 text의 길이

    * line : 1첫줄 = 0

     

    - maxLines (int)

    xml에서 lines를 설정할 경우 설정되는 값.(설정이 되지 않을경우 표현할 수 있는 최대 값을 표시하는것으로 추측된다.)

     

    - text (CharSequence)

    TextView에서 가지는 text

     

    마지막 라인 수를 체크(getEllipsisCount)하여 생략된 text의 길이를 체크하고 필요한 텍스트를 붙여보자.

    private fun addOnMore() {
        var isEllipsize = layout.getEllipsisCount(maxLines - 1)
    
        if (isEllipsize > 0) {
            var ellips = text.substring(
                0,
                originText.length - isEllipsize - (addText?.let { it.length } ?: 0) - 3)
    
            text = "$ellips ... ${addText}"
        }
    }

     

    실제 동작한 모습

     

    * 주의 

    1. xml에 lines를 설정하지 않을 경우 maxlines의 변수를 사용의 위험성이 있다.

    2. viewTreeObserver 사용시 오작동의 이슈가 발생 할 수 있다.

    필자는 설정된 text에 추가할 글자를 덧붙여 생략이 발생되는지 체크하였다. 그 후 ... more를 붙일지 유무를 다시한번 확인하였다.

    xml text설정, activity 내 text설정을 하게 될 경우 두번의 listener가 호출되며, text에 대한 값이 혼선을 가져오는 상황도 발생된다.

    3. 한글, 영어의 각 글자별 넓이가 다르다. 1(영어) : 1.5(한글) 정도의 수준이다. 그래서 한글 기준으로 text를 자르면 원했던 글자에 비해 더 많이 생략될 수 있다.

    해당 부분을 해결하기위해 추가로 자르거나, 덧붙일 글자의 한/영을 체크하여 길이를 계산하는 방법도 있다. (한글, 영어, 숫자를 확인 방법 - needjarvis.tistory.com/227 )

     

    + 추가

    덧붙인 글자가 어느 위치에 적용되는지 추측할 수 있기 때문에 해당 offset에 spannable을 적용하여 글자색, 아이콘, 선택액션까지 줄 수 있으니 활용해보면 좋을 것 같다.

     

    끝.

    댓글

Designed by Tistory.