안드로이드에서는 화면 구성이 두 가지로 나뉘어져 있다.
- XML 레이아웃 파일 : 화면의 UI구성
- 소스 코드 파일 : 화면에서 기능을 담당
이 때, 어떤 XML 레이아웃 파일이 어떤 소스 코드 파일로 기능하는지 어떻게 알 수 있을까?
안드로이드 스튜디오로 프로젝트를 새로 만들 때, Override된 OnCreate메서드에는 자동으로 추가되는 것이 있다.
setContentView(R.layout.activity_main)
(물론 요즘엔 Data Binding해서 쓰긴 하지만 어쨌든 최초로 만들었을 땐 이렇다.)
이 메서드가 XML 레이아웃 파일과 소스 코드 파일을 연결해 주는 것이다.
(이 메서드는 MainActivity 파일이 상속받는 AppCompatActivity에 존재하는 것이다.)
매개변수에서 대문자 R은 res폴더를 의미한다.
앱이 실행이 될 때 XML 레이아웃의 내용이 메모리에 객체화가 되고, 객체화된 XML 레이아웃을 소스 파일에서 사용하는 구조이다. XML 레이아웃이 메모리에 객체화가 되는 과정을 인플레이션이라고 한다.
XML레이아웃은 앱이 실행이 될 때 객체화가 되므로 그 전까지는 앱은 자신이 XML 레이아웃에 무엇이 있는지 알 수 없다. 즉, setcontentView 메서드 이전에 레이아웃 컴포넌트에 대한 접근은 불가능하다.
XML 레이아웃이 인플레이션 되기 전에 버튼에 대한 참조를 해서 클릭했을 때 토스트 메시지를 띄우게 소스 코드를 만들었다.
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.practiceproject, PID: 20021 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.practiceproject/com.example.practiceproject.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:223) at android.app.ActivityThread.main(ActivityThread.java:7656) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference at com.example.practiceproject.MainActivity.onCreate(MainActivity.java:16) at android.app.Activity.performCreate(Activity.java:8000) at android.app.Activity.performCreate(Activity.java:7984) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:223) at android.app.ActivityThread.main(ActivityThread.java:7656) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) W/System: A resource failed to call close. |
다음과 같은 에러가 나면서 앱이 종료가 됐다.
메모리에 객체화되지 않은 버튼 객체를 참조했기 때문에 다음과 같은 에러가 나는 것이다.
(널 포인터 예외처리가 된 것이다.)
즉, 액티비티에서는 setContentView 메서드가 인플레이션 역할을 하는데, 인플레이션 되기 전에는 앱이 레이아웃에 어떤 객체가 있는지 알 수 없다는 것을 알 수 있다.
이 때, setContentView는 두 가지의 형태로 정의되어있다.
- setContentView(int layoutResID) : 레이아웃 파일을 불러오는 메서드
- setContentView(View view, ViewGroup.LayoutParams params) : 뷰 객체를 불러오는 메서드
XML 레이아웃에서 만든 부분 화면 역시 불러와서 보여줄 수 있다.
그치만, setContentView 메서드는 화면 전체를 설정하는 역할만 수행한다. 따라서 부분 화면을 메모리에 객체화 하는 기능은 없다.
부분 화면을 메모리에 객체화 하기 위해서는 인플레이터를 사용해야 한다.
안드로이드에서는 LayoutInflater라는 클래스를 제공한다. LayoutInflater는 시스템 서비스로, getSystemService 메서드를 이용해서 LayoutInflater 객체를 참조해서 사용한다.
※ 시스템 서비스란, 단말이 시작되면 항상 실행되는 서비스이다.
getSystemService(Context.LAYOUT_INFLATER_SERVICE) |
코드로 살펴보기
우선 새로운 Activity로 진행할 것이므로 새로운 XML 레이아웃 파일을 만들어서 다음과 같이 화면을 구성한다.
나는 activity_menu.xml 파일로 만들었다.
버튼을 누르면 아래 LinearLayout에 내가 만든 부분 레이아웃 파일을 불러올 것이다.
이 화면이 내가 인플레이션할 서브 레이아웃 파일이다. 특별한 건 없다.
메인 액티비티에서는 내가 서브 레이아웃 파일을 로드할 container를 findViewById로 찾아서 불러왔다.
(※ 요즘엔 View Binding을 써서 findViewById는 잘 사용하지 않는다.)
그리고 버튼의 onClickListner를 람다식으로 구현했고 내용은 다음과 같다.
- LayoutInflater 객체 inflater를 getSystemService() 메서드로 불러온다. 매개변수로는 LAYOUT_INFLATER_SERVICE를 넣음으로써 LayoutInflater 서비스를 불러오는 것을 알려준다.
- inflater가 서브 레이아웃을 메인 레이아웃의 container 부분에 인플레이트하도록 한다.
- 체크박스에 체크가 되도록 한다.
LayoutInflater 객체의 inflater 메서드는 다음과 같이 정의되어 있다.
|
LayoutInflater 객체는 두 가지 방법으로 불러올 수 있다.
|
코드 실행화면
마무리
- 인플레이션은 런타임(실행시간)에 일어나고, 레이아웃 XML에 정의된 내용이 메모리에 객체화되는 과정이다. 이것은 중요한 과정이므로 꼭 머리속에 넣고 있어야 한다.
'안드로이드 > 개념' 카테고리의 다른 글
[Android] 인텐트, Intent (0) | 2021.07.31 |
---|---|
[Android] 화면 여러개 만들어서 화면 전환하기 - onActivityResult, startActivityForResult Deprecated. registerForActivityResult 사용해보기 (0) | 2021.07.28 |
[Android] Do It! 안드로이드 도전 04. SMS 입력 화면 만들고 글자의 수 표시하기 + 데이터 바인딩, 뷰 모델, 라이브 데이터로 풀어보기 (0) | 2021.07.27 |
[Android] Do It! 안드로이드 도전 03. 두 개의 이미지뷰에 이미지 번갈아 보여주기 (0) | 2021.07.25 |
[Android] 스크롤 뷰, Scroll View (0) | 2021.07.25 |