Android — TextView.java
TextView 생성자 코드를 살펴보자
Android를 배우면 처음 배우는 것은 Hello World를 출력하는 것이고, xml를 이용한 실습을 한다면 TextView를 이용 하게 됩니다. 이후 텍스트를 나타내기 위해서는 TextView 태그를 사용해서 구현하고 TextView api를 이용해서 제어 합니다.
우리는 TextView로 추상화 된 api를 사용하지만 과연 TextView는 어떻게 구현이 되어 있을까요?
이를 살펴보기 위해서 TextView.java 를 살펴 보았습니다.
생성자 / Constructor
TextView는 오버로딩을 통해서 하나의 생성자에서 작업을 수행하고 있습니다. 이 생성자를 하나씩 살펴보겠습니다.
Super 호출
TextView 클래스는 View의 서브 클래스 입니다. 따라서 View가 지닌 attr또한 적용되어야 합니다. 따라서 super 를 통해서 View 생성자를 먼저 호출해주고 있습니다.
자동 완성 프레임 워크 및 Content Capture
View는 importantForAutofill attr를 가지고 있습니다. 이에 대한 설명은 아래와 같습니다.
TextView 생성자에서는 importantForAutofill이 auto인 경우 yes로 설정해주고 있습니다.
이와 유사하게 android은 자동 완성 프레임 워크 뿐만 아니라 content capture 기능도 제공하고 있습니다. 마찬가지로 content capture를 auto로 설정 된 경우 yes로 변경 해줍니다.
setTextInternal
Spannable은 문자나 단락 수준으로 텍스트 스타일을 지정할 수 있게 해주는 인터페이스 입니다.
PrecomputedText는 TextView에서 수행하는 measure, layout등을 미리 수행할 수 있도록 제공하는 클래스 입니다.
setTextInternal에서는 결국 mText, mSpannable, mPrecomputed 를 초기화 합니다.
Paint 초기화
TextView를 그리기 위해서 사용되는 textPaint와 hilightPaint를 초기화 합니다.
movement 초기화
TextView에서는 구현하고 있지 않지만 subclass에서 getDefaultMovementMethod를 오버라이딩 해서 구현하면 mMovement를 생성자 시점에 초기화 합니다.
MovementMethod에 대한 설명은 아래와 같으며
TransformationMethod
mTransformation 을 null으로 설정하며 이 멤버 변수는 setter를 통해서 지정할 수 있습니다. TransformationMethod 클래스는 비밀번호의 경우 *으로 나오는 것 과 같이 text의 값과 보여지는 값을 다르게 하고 싶을 때 제공 됩니다.
TextView Default 값 설정
TextApperanceAttributes
TextApperanceAttributes는 생성하는 시점에 textColor와 textSize를 지정해주고 있습니다. 이는 TextView의 기본 텍스트 색상과 크기를 의미합니다.
BreakStrategy, HyphnationFrequency, JustificationMode
줄바꿈, 하이픈 빈도, 맞추기와 관련된 flag를 default 값으로 설정 합니다.
Theme로부터 TextAppearance 정보 가져오기
theme로 부터 TextViewApperance styleable를 가져오고 있습니다.
saveAttributeDataForStyleable 메소드의 의 주석을 보면 debuging을 위한 함수하는 것을 알 수 있습니다. layout inspection tools 에서 보기 위해서는 아래 함수 호출이 필요 합니다.
R.styleable.TextViewAppearance
TextViewApperance styleable내 정의된 textApperance를 가져옵니다.
TextView에서 textApperance가 정의 된 경우 TextApperance Id를, 존재하지 않는 경우는 -1를 가져옵니다.
TextView에 textAppearance 를 정의한 경우는 textAppearanceId를 이용하여 다시 Theme 로 부터 TextAppearance 정보를 가져 옵니다.
styleable.TextApperance 정의는 아래와 같습니다.
TextApperance 조회
위에서 가져온 TextAppearance가 null이 아닌 경우 readTextAppearance를 호출해서 attributes로 부터 TextAppearance 속성을 추출 해줍니다. 이후 attributes에 mFontFamilyExplicit를 false로 바꿔주고 있습니다.
mFontFamilyExplicit
mFontFamilyExplicit이 사용되는 곳은 아래와 같습니다.
mFontFamilyExplicit는 이름 그대로 명시적으로 fontFamily를 사용했는지를 나타내는 변수입니다.
readTextAppearance
readTextAppearance는 TypedArray로 부터 TextAppearance attr를 조회해서 attributes의 상태를 업데이트 해주고 있습니다.
DefaultEditable
DefaultEditable는 xml에서 editable를 정의하지 않는 경우 getDefaultEditable의 값을 따르며 상속을 통해서 서브 클래스에서 수정할 수 있습니다.
TextAppearance
theme로 부터 TextView styleable를 가져 옵니다.
TextView styleable을 보면 textAppearance가 존재하며 TextView와 TextAppearance는 동일한 attr를 가지고 있는 것을 알 수 있습니다.
<declare-styleable name="TextView">
// ...
<attr name="textColor" />
<attr name="textColorHighlight" />
<attr name="textColorHint" />
<attr name="textAppearance" />
</declare-styleable><declare-styleable name="TextAppearance">
<attr name="textColor" />
<attr name="textSize" />
<attr name="textStyle" />
<attr name="textFontWeight" />
// ...
<declare-styleable/>
TextViewAppearance:textAppearance와 textView:textAppearance의 차이점은 무엇일 까?
TextViewAppearance styleable과 textView styleable 각각으로 부터 attributes를 자겨 오고 있습니다. 이 둘은 readTextAppearance api를 호출 할 때 styleArray 값을 다르게 호출 하고 있습니다.
@param styleArray
Whether the given TypedArray is a style or a TextAppearance. This defines what attribute indexes will be used to read the properties.styleArray인자는 다른 인자로 주어신 TypedArray 변수가 style인지 TextAppearance인지를 나타내고 있습니다.
Get Quokkaman’s stories in your inbox
Join Medium for free to get updates from this writer.
styleArray가 사용되는 코드는 아래와 같습니다.
만약 styleArray인 경우 attr를 sAppearanceValues로 사상해주고 있습니다.
즉 TextView styleable의 attributes를 TextAppearance styleable의 attributes로 사상해주고 있습니다. 정확히는 TextAppearance에 대한 속성들이지만 TextView의 속성으로도 지정할 수 있게 해주어서 Text에 대한 속성을 손쉽게 수정 할 수 있습니다. 만약 이 사상을 지원하지 않는 다면 TextView는 매번 TextAppearance를 생성하여 text 속성을 지정 해야 합니다.
즉 우리가 TextView의 속성이라고 생각했던 textSize, textColor등은 정확히 말하면 TextAppearance의 속성입니다. 편의상 TextView styleable에서도 제공하고 있을 뿐 식제 동작은 textAppearance에 있습니다.
TextView Attributes 가져오기
이제 TextView의 attributes를 가져옵니다.
위 코드를 보면 textIsSetFromXml 플래그가 있습니다. xml의 text로 text가 설정 되는 경우 true가 되며 생성자 마지막에 mTextSetFromXmlOrResourceId가 true로 설정 됩니다.
mTextSetFromXmlOrResourceId는 변수명 그대로 xml 혹은 resourceId로 부터 텍스트가 설정 된 경우 true가 되는 플래그 변수이며 자동완성과 관련된 플래그 입니다.
// Autofill-related attributes//// Indicates whether the text was set statically or dynamically, so it can be used to// sanitize autofill requests.private boolean mTextSetFromXmlOrResourceId = false;
InputType 확인
xml에 정의된 inputType을 기준으로 패스워드 여부를 확인 합니다.
Editor가 존재하는 경우 editor의 inputType를 조정하기 위해서 위임 해주고
입력 타입이 패스워드인 경우 monospace를 강제로 적용 해주는 로직이 존재합니다.
InternationalizedInput 지원 여부 확인
targetSDK를 확인해서 OREO 이상인 경우 internationalizeInput를 지원하는 플래그를 true로 변경 합니다.
Fallbacklinespacing 설정
fallbacklinespacing은 글꼴 및 언어에 따라서 줄간격을 자동으로 조정해주는 기능입니다.
UseTextPaddingForUiTranslation
mUseTextPaddingForUiTranslation 변수의 주석을 보면 text가 번역 될 때 호환성을 위해서 텍스트를 채울 수 있을 때 true로 설정된다고 되어 있으며 private이며 final입니다. 오직 R 버전 이하에서만 TextpaddingForUiTranslation이 사용되도록 되어 있습니다.
Editor 업데이트
xml로 부터 받은 attr를 이용해서 Editor 객체를 업데이트 해주고 있습니다.
Editor 는 TextView를 editable하게 만들기 위해서 사용되는 헬퍼 클래스 입니다.
Drawables
Drawable에 대한 설정을 수행 합니다.
아래 코드를 보면 mDrwables는 설정하지 않았지만 drawableTint 만 설정한 경우는 Drawables를 새로 생성해주는 로직이 존재 합니다.
이후 drawable를 CompundDrawables로 지정합니다.
SingleLine 설정
single line 관련된 설정을 수행 합니다.
ellipsize 설정
single line인데 ellipsize를 설정하지 않는 경우 ELLIPSIZE_END를 기본값으로 설정하고 있습니다. 이후 setEllipsize함수를 통해 ellipsize를 설정 합니다.
marquess의 경우 아래 config 값에 따라서 isFadingMarqueeEnabled여부가 달라 지고 있습니다.
mFadingMarqueeEnabled = res.getBoolean(com.android.internal.R.bool.config_ui_enableFadingMarquee);
fontWeightAdjustment 설정
Configuration 로 부터 fontWeightAdjustment 값을 가져옵니다.
fontWeightAdjustment가 존재하는 경우 typeface로 부터 weight를 가져온 이후 adjustment값 까지 더해주고 있습니다.
tf.getWeight() + mFontWeightAdjustmentFilter
성능상 이유로 Editable이면서 single line인 경우 최대 길이를 제한 하고 있습니다.
text, hint
text와 hint를 설정해주고 있습니다. mText와 mTransformed는 null인 경우 empty string으로 바꿔주고 있습니다.
Clickable
TextView는 input 혹은 movement method를 가지는 경우 디폴트로 클릭이 가능합니다. 이를 처리하기 위해서 TextView 생성자 내 아래와 같은 로직이 존재합니다.
ImportantForAccessibility
ImportantForAccessibility가 auto로 설정 된 경우 yes를 따르도록 수정 하고 있습니다.
AutoSizeText
만약 TextView의 autoSizeTextType이 UNIFORM인 경우 아래 로직을 수행합니다. 아래 코드에서 text size를 조절하고 업데이트 까지 수행 합니다.
BaseLine
baseline를 설정 한 경우 setter함수를 호출하여 설정해주고 있습니다.
위 setFirstBaselineToTopHeight와 setLastBaselineToBottomHeight 함수는 padding을 다시 조정하고 있습니다.
lineHeight
lineHeight를 설정하는 경우 연산을 통해 lineSpacing를 결정합니다.
TextView를 분석하기 위해서 시작했지만 생각보다 TextView가 가진 정책이 많아 생성자 밖에 분석하지 못했습니다. 각 정책에 대해서 어떻게 구현 했는지는 기회가 되면 다음 포스팅에서 다뤄 보도록 하겠습니다. 이번 포스팅은 TextView는 내부 측면에서 어떤 속성들을 다루고 있는지 생성자를 통해서 다뤄 보았습니다.
TextView는 TextAppearance와 동일한 attr를 가지고 있는데 이는 TextView내부에서 TextAppearance에 해당하는 값으로 바꿔주는 로직이 존재하는 것을 확인할 수 있었고 TextView 뿐만 아니라 Editable한 경우에 대해서 고려하고 있는 것을 알 수 있었습니다.









