안드로이드 뷰의 getWidth 메소드와 getMeasuredWidth 메소드의 차이를 정리하고자 합니다.
본 메소드의 차이점을 명확하게 이해하기 위해 EditText를 활용해보겠습니다.
커스텀 레이아웃에 EditText를 넣고, 레이아웃의 width 값은 wrap_content로 EditText의 가로폭에 맞춥니다.
그러면 EditText 내부의 값이 바뀔 때마다 커스텀 레이아웃의 width 값이 달라집니다.
이때 값이 바뀌고나서 바뀐 width값을 가져오는 과정을 보면 두 메소드의 차이를 명확하게 이해할 수 있습니다.
사용한 코드와 레이아웃은 현재 진행중인 개인프로젝트에 사용된 소스코드의 일부입니다.
커스텀 레이아웃의 xml 파일은 다음과 같습니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/container_chord_item_bar"
android:background="@drawable/se_chord_item_normal_bar"
android:orientation="vertical"
android:clipChildren="false"
android:layout_marginBottom="4dp"
android:visibility="visible">
<TextView
android:id="@+id/txv_chord"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="4dp"
android:text="C"
android:background="@drawable/se_chord_item_half_horizontal_line"
android:textColor="#FF000000"
android:textSize="20sp" />
<EditText
android:id="@+id/txv_lyric"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="가사"
android:textColor="@color/black"
android:textSize="20sp"/>
</LinearLayout>
EditText의 값이 바뀔 때마다 변경된 커스텀 레이아웃의 width 값을 가져올 것이므로
내부 텍스트가 변경될 때의 이벤트를 잡아야 합니다.
다음과 같이 EditText에 리스너를 달아주었습니다.
edt_lyric.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
itemChord.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
int changed = itemChord.getMeasuredWidth() - ItemWidth;
int changed2 = itemChord.getWidth() - ItemWidth;
Log.d("Test", "changed " + changed + ", " + changed2);
}
@Override
public void afterTextChanged(Editable s) {
}
});
이때 중요한 것이 measure 메소드입니다.
이 메소드를 호출하지 않으면 getMeasuredWidth() 메소드를 호출하는 것은 의미가 없습니다.
changed 값은 getMeasuredWidth() 메소드를 사용하여 새로 측정한 값 - 기존 측정했던 값
changed2 값은 getWidth() 메소드를 사용하여 새로 측정한 값 - 기존 측정했던 값
앱을 실행해서 찍히는 로그값을 봅시다.
연노랑색으로 칠해진 칸 하나가 위에서 작성한 xml 코드의 모습입니다.
저 "가사"라고 쓰여진 텍스트를 수정해보겠습니다.
다음 사진처럼 a를 추가했습니다.
찍히는 로그를 볼까요?
두번째 줄 로그는 코드상에 표시하지 않았지만 메소드의 인자로 넘어온 charset s 값과
EditText의 현재 텍스트값을 getText() 메소드로 가져온 값을 비교한 것입니다.
이 둘은 똑같습니다.
첫째줄을 보면 30, 0 으로 되어있습니다.
getMeasuredWidth() 메소드로 가져왔을 땐 레이아웃의 width 값이 30증가했지만
getWidth() 메소드로 가져왔을 땐 레이아웃의 width 값에 변화가 없습니다.
이번엔 기존에 적혀있던 a를 지워보겠습니다.
그럼 최초로 적혀있던 텍스트와 동일해지니 변화값은 0으로 나오는게 정상입니다.
이번엔 반대의 결과가 나왔습니다.
getMeasuredWidth() 메소드로 가져온 변화값은 0입니다.
하지만 getWidth() 메소드로 가져온 변화값이 30입니다.
이 변화가 나타나는 이유를 이해하려면 안드로이드에서 View의 생명주기를 이해해야 합니다.
구글에 검색하면 많은 분들이 이해하기 쉽게 설명을 해주셨기 때문에 여기서는 간단하게만 적겠습니다.
뷰의 생명주기 단계 중
뷰가 생성된 이후
뷰의 사이즈를 측정하는 단계가 지나고나서 ( = Measure() )
그 이후 몇가지 단계를 거치고 마지막으로 뷰의 디자인을 시작적으로 그려내는 단계에 이릅니다.
( = draw() )
메소드의 이름을 보면 이제 감이 좀 잡히실 것 같습니다.
getMeasuredWidth() 메소드는 measure 의 결과로 측정된 뷰의 사이즈를 가져옵니다.
하지만 getWidth() 메소드는 draw의 결과로 그려진 뷰의 사이즈를 가져옵니다.
위에서 했던 예시를 통해 설명을 덧붙이겠습니다.
EditText의 텍스트값을 변경하면 ( "가사" -> "가a사" )
변경된 값이 현재 시각적으로 변경된 듯 보이더라도
리스너에서 이벤트를 받은 시점에는 아직 measure() 메소드가 호출되지 않은 상태이기 때문에
getMeasuredWidth 메소드를 사용하든, getWidth() 메소드를 사용하든 둘다 변경되기 이전의 값을 보여줍니다.
따라서 changed 의 값과 changed2은 똑같이 0입니다.
그래서 위 코드에서는 강제로 measure 메소드를 호출하여 뷰의 사이즈를 측정하도록 했습니다.
따라서 getMeasuredWidth() 메소드를 사용하면
measure() 메소드가 호출되어 뷰의 사이즈가 새로이 측정되었기 때문에 갱신된 사이즈 값을 가져오게 됩니다.
(View.MeasureSpec.UNSPECIFIED 는 코드로 텍스트값을 세팅했을 경우
세팅된 텍스트 값을 적용하여 측정하도록 지시하는 코드입니다.
자세한 내용은 역시 구글링을 통해 알아보시면 더 좋습니다.)
하지만 draw() 메소드는 호출하지 않았기 때문에
getWidth() 메소드를 사용하면 변경전의 사이즈 값을 보여줍니다.
다시 EditText의 값을 변경하면 ( "가a사" -> "가사" )
이전에 "가a사" 로 적었던 부분은 draw() 함수까지 모두 호출이 완료되어 갱신이 끝났지만,
역시 변경점을 갱신하기 이전에 리스너에서 이벤트를 잡습니다.
따라서 강제로 measure() 메소드로 측정하고 getMeasuredWidth() 메소드를 사용했을 때는
갱신된 사이즈 값을 가져와 changed 값이 0이지만
getWidth() 메소드를 사용했을 때는
이전의 측정값을 가져와 changed2 값을 30으로 한박자 늦게 가져옵니다.
그래서 보통 구글에 뷰의 사이즈를 측정하는 방법을 검색하면
view.measure(0, 0)
view.getMeasuredWidth();
view.getMeasuredHeight();
다음과 같은 코드를 소개합니다.
'Android > Java' 카테고리의 다른 글
[안드로이드] Constraint Layout 동적 생성 후 동적으로 뷰 추가하기 (0) | 2021.03.20 |
---|---|
[안드로이드] 프래그먼트(Fragment) (0) | 2021.01.03 |
[안드로이드] 인텐트 활용 : 데이터 교환(2) (0) | 2021.01.02 |
[안드로이드] 인텐트 활용 : 데이터 교환(1) (0) | 2021.01.01 |
[안드로이드] 인텐트의 개념 (0) | 2021.01.01 |