본문 바로가기

Diary/삽질노트

[Android] TextView Typeface 관련 궁금증 - 미해결

Typeface 클래스에 대한 궁금증이 생겼다. 문제 해결은 했지만 이유를 아직 모르겠다.

 

달력 데이터를 표시하는 리사이클러뷰 하나가 있고, 오늘 날짜에는 bold 처리, 아닌 날짜에는 노말로 표시한다.

그래서 RecyclerViewAdapter.onBindViewHolder() 에서 아래와 같이 그려지도록 작성했다.

tv_holder_day.setTypeface(tv_holder_day, getTextTypeface(data.date.time.isToday()))


fun getTextTypeface(isToday): Typeface = if (isToday) Typeface.BOLD else Typeface.NORMAL

 

그런데 위와같이 설정하면 오늘 날짜는 제대로 볼드처리돼서 그려지는데,

오늘 날짜(28일) 클릭 -> 다른 날짜(29일) 클릭 -> 또 다른 날짜 (30일) 클릭 

위와 같은 테스트를 하면 28일만 볼드처리되어야할 것이 29일에도 볼드처리가 들어가있다. 마지막으로 업데이트 했던 뷰 스타일 속성이 그대로 따라가는 느낌이랄까? 어댑터 코드는 더보기 참고!

더보기

아이템을 클릭하면 RecyclerViewAdapter 내 아래 메서드가 불린다

private fun updateHighlightItem(oldPosition: Int, newPosition: Int): Int {
	if (oldPosition == newPosition) {
		list[newPosition].isSelected = !list[newPosition].isSelected
		notifyItemChanged(newPosition)

		return UNSELECTED_POSITION
	}

	if (oldPosition in 0 until list.size) {
		list[oldPosition].isSelected = !list[oldPosition].isSelected
		notifyItemChanged(oldPosition)
	}

	if (newPosition in 0 until list.size) {
		list[newPosition].isSelected = !list[newPosition].isSelected
		notifyItemChanged(newPosition)
	}

	return newPosition
}

 

리사이클러뷰의 뷰 재사용 관련 이슈일까도 의심해보았는데, 내 촉으로는 이것도 아닌 것 같았다.

우선 해결방법부터 말해보면 setTypeface() 의 첫번째 파라미터 tf 에 null 을 넘기면 된다. 근데 왜그럴까?

 

TextView.setTypeface() 메서드를 들여다보았다.

public void setTypeface(@Nullable Typeface tf, @Typeface.Style int style) {
    if (style > 0) {
        if (tf == null) {
            tf = Typeface.defaultFromStyle(style);
        } else {
            tf = Typeface.create(tf, style);
        }

        setTypeface(tf);
        // now compute what (if any) algorithmic styling is needed
        int typefaceStyle = tf != null ? tf.getStyle() : 0;
        int need = style & ~typefaceStyle;
        mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
        mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
    } else {
        mTextPaint.setFakeBoldText(false);
        mTextPaint.setTextSkewX(0);
        setTypeface(tf);
    }
}

tf 에 기존 뷰를 집어넣었으니 else 분기문으로 갔을테고, create 메서드로 가져온 스타일에서 비트연산 후 need 가 0이 아닐 경우 가짜 볼드를 처리한다.. 라고 대충 이해할 수 있다.

Sets the typeface and style in which the text should be displayed, and turns on the fake bold and italic bits in the Paint if the Typeface that you provided does not have all the bits in the style that you specified.

텍스트를 표시 할 서체와 스타일을 설정하고, 제공한 서체에 지정한 스타일의 일부 비트가없는 경우 그림판에서 가짜 굵은체 및 기울임 꼴 비트를 켭니다.

해당 메서드에 적힌 주석이다. 가짜 볼드랑 기울임을 켠다..? 도통 뭔소린지 알 수 없지만 need 가 켜지면서 가짜 볼드가 적용된 것 같다는 킹리적 갓심이 든다.

근데 그래도 이해가 안간다. 어떤 Typeface 가 만들어진건지 create 메서드를 보았다.

public static Typeface create(Typeface family, @Style int style) {
        if ((style & ~STYLE_MASK) != 0) {
            style = NORMAL;
        }
        if (family == null) {
            family = sDefaultTypeface;
        }

        // Return early if we're asked for the same face/style
        if (family.mStyle == style) {
            return family;
        }

        final long ni = family.native_instance;

        Typeface typeface;
        synchronized (sStyledCacheLock) {
            SparseArray<Typeface> styles = sStyledTypefaceCache.get(ni);

            if (styles == null) {
                styles = new SparseArray<Typeface>(4);
                sStyledTypefaceCache.put(ni, styles);
            } else {
                typeface = styles.get(style);
                if (typeface != null) {
                    return typeface;
                }
            }

            typeface = new Typeface(nativeCreateFromTypeface(ni, style));
            styles.put(style, typeface);
        }
        return typeface;
    }

family 에는 텍스트뷰(tv_holder_day)에 설정된 typeface(Typeface.Normal)가 들어갔을테고, 내가 넣은 두번째 파라미터 style은 Typeface.Normal 로 동일할건데 그럼 리턴은 family..?

일 줄 알았는데 Normal 이 아닌걸 보니 내부적으로 bold 스타일을 추가해버린거라고 의심하게됐다.

어떤 알수없는 이유에 의해 sStyledTypefaceCache 에서 Bold 스타일을 빼온 것 같은데... (아마도 이 cache 는 static 필드여서 다른 모든 텍뷰들과 공유하는 필드일것이다)

 

시간이 많이 지체돼서 오늘도 그냥 의문만 제기하고 튀튀해야겠다. 근데 진짜 왜그런건지 매우 궁금......ㅠㅠ