레이아웃 인플레이션 이해하기

2018. 11. 22. 15:25Android Programming

지금까지 하나의 화면을 만들기 위해 XML 레이아웃을 정의했습니다.

XML레이아웃은 단순히 XML로 정의된 파일이며 화면을 어떻게 배치하는지 정의할 뿐입니다. 따라서 XML 레이아웃만 만들었다고 해서 화면을 띄우고 동작시킬수 있는 것은 아닙니다.

안드로이드는 화면 배치를 알려주는 XML 레이아웃 파일 화면의 기능을 담당하는 소스 코드 파일로 분리되어 있습니다. 이렇게 화면 레이아웃과 화면 기능이 분리되어 있기 때문에 항상 하나의 화면을 만들 때는 XML 레이아웃 파일 하나와 자바소스 파일 하나를 쌍으로 만들어야 한다고 생각하자.

그런데 두개의 XML 레이아웃 파일을 만들고 자바 소스 파일은 하나만 만들었다면 그중 어떤 XML 레이아웃 파일이 자바 소스파일과 매칭되는 것인지 알 수 있을까 ?  즉, 새로만든 XML 레이아웃 파일을 화면 기능을 담당하는 자바 소스에 어떻게 설정하는지 궁금해 진다. 새로운 프로젝트를 만들 때 자동으로 만들어지는 자바 소스코드(MainActivity.java 파일)을 보면 onCreate() 메소드 안에 있는 코드는 단순히 두줄 뿐이다. 

super.onCreate() 메소드가 단순히 부모 클래스의 동일한 메소드를 호출한다는 점을 고려하면 setContentView() 메소드가 있는 한 줄이 자바코드의 전부라고 생각할 수도 있습니다. 결국, 어떤 XML 레이아웃 파일과 매칭할 것인지 자바 소스코드에서 설정하는 부분이 setContentView() 메소드라는 것을 추측해 볼 수 있다. 

setContentView(R.layout.activity_main);   => R.layout.레이아웃 파일 이름

화면의 기능을 담당하는 자바 소스파일을 하나 만들면 그 안에는 AppCompatActivity를 상속하는 하나의 클래스가 자동으로 만들어 진다. 이 클래스가 상속하는 AppCompatActiviry에는 화면에 필요한 기능들이 들어있다. 그중에 setContentView() 메소드를 호출하면서 XML 레이아웃 파일 이름을 파라미터로 전달하면 XML 레이아웃과 자바 소스코드가 서로 연결된다. 따라서 앱을 실행했을때 보이는 화면은 이 둘의 합작품이라고 생각하면 된다.

여기서 대문자 R 은 res 파일을 가리킨다.

따라서  XML 레이아웃 파일의 내용을 자바 소스코드에서 사용하려면 앱 이 실행될 때 XML 레이아웃 파일의 내용이 메모리로 로딩되어 객체화 되어야 한다. 이렇게 XML레이아웃에 정의된 내용이 메모리에 로딩된 후 객체화 되는 과정을 인플레이션(Inflation) 이라고 합니다.

이 과정을 이해하는 것은 실제 앱을 만드는 과정에서 매우 중요하다. 왜냐하면 XML 레이아웃 파일은 앱이 실행되는 시점에 로드되어 메모리에 객체화 되기 때문이다. 즉 XML 레이아웃 파일 안에 <Button> 태그를 정의해 두었더라도 앱은 그 정보를 미리 알고있는 것이 아니라 실행하면서 확인하게 됩니다. 이 내용을 확인해 보기 위해 여러분이 만든 가장 단순한 프로젝트에서 setContentView() 메소드가 호출되기 전에 XML 레이아웃에 정의된 버튼을 찾아 참조하면 문제가 발생한다. 

Caused by : java.lang.NullPointerException: Attempt to invoke virtual method

setContentView() 메소드가 매우 중요하다는 것을 알수 있다. 크게 두가지 역할로 나뉘는데

첫 번째는 화면에 나타낼 뷰를 지정하는 역할

두 번째는 XML 레이아웃의 내용을 메모리에 객체화 하는 역할.

[Reference]

public void setContentView (int layoutResID)

public void setContentView (View view [, ViewGroup.LayoutParams params])


그렇다면 화면 전체에 보여줄 XML 레이아웃이 아니라 전체 화면 중에서도 일부분만 차지하는 레이아웃을 별도의 XML 레이아웃 파일로 만들어두고 그 XML 레이아웃의 내용을 로딩하여 보여줄 수는 없을까? 화면의 일부분을 차지하는 것을 부분 화면이라고 부를 수 있는데 전체 화면이 아닌 부분 화면도 별도의 XML 레이아웃 파일에 정의한후 불러와 보여줄 수 있다. 다만 setContentView() 메소드는 액티비티의 화면 전체를 설정하는 역할을 하므로 화면 전체가 아닌 부분화면을 위한 XML 레이아웃을 메모리에 객체화 하려면 별도의 인플레이션 객체를 사용해야 한다. 안드로이드에서는 이를 위해 LayoutInflater라는 클래스를 제공하며, 이 클래스는 시스템 서비스로 제공되므로 다음과 같은 코드를 이용하여 LayoutInflater 객체를 참조한 후 사용할 수 있다.

getSystemService(Context.LAYOUT_INFLATER_SERVICE)

시스템 서비스는 단말이 시작되면서 항상 실행되는 서비스이다. 서비스에 대해서는 나중에 다시 자세하게 살펴볼 것이다. 일단 여기서는 눈에 보이지 않지만 단말이 시작되면 단말 안에서 실행되는 어떤 기능이라고 생각하면 된다. 시스템 서비스로 제공되는 기능들은 getSystemService() 메소드를 사용하여 객체를 참조한 후 사용할 수 있다.


화면 일부분을 XML 레이아웃 파일로 작성.

LayoutInflater 객체를 사용해 뷰그룹 객체로 객체화(인플레이션) 한 후 메인 레이아웃에 추가.

LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);

inflater.inflate(R.layou.sub1, container, true);


와 같은 형식 onCreate()메소드 내부에서 사용하여 XML레이아웃 파일을 메모리에 객체화 시킬수 있다.(인플레이션)


[Reference]

View inflate (int resource, ViewGroup root)

이 메소드의 첫 번째 파라미터로는 XML 레이아웃 리소스를 지정하며, 두 번째 파라미터로는 뷰 들을 객체화 하여 추가할 대상이 되는 부모 컨테이너를 지정합니다. 


LayoutInflater 객체의 경우, 시스템 서비스로 제공되므로 getSystemService() 메소드를 사용해 객체를 참조하지만 LayoutInflater 클래스에 정의된 from() 메소드를 사용할 수도 있습니다.

[Reference]

static LayoutInflater LayoutInflater.from (Context context)


이외에도 LayoutInflater를 내부적으로 지원하는 View의 클래스 메소드를 이용하는 방법도 있습니다. 다음과 같이 정의된 Inflate() 메소드를 이용하면 한 줄로도 객체화 과정을 수행할 수 있습니다.

[Reference]

static View inflate (Context context, int resource, ViewGroup root)


레이아웃을 개체화 하는 과정은 매우 중요합니다. 그 이유는 앞에서 설명한 바와 같이 앱이 실행되는 런타임 시에 XML 에 정의된 내용들이 메모리에 객체화 되기 때문입니다. 무엇보다도 XML이 분리되어 있기 때문에 생기는 이런 과정을 명확히 이해하고 기억 하는것이 앞으로 반복적으로 나오는 인플레이션 과정에 빨리 익숙해 지는 방법입니다.

'Android Programming' 카테고리의 다른 글

gradle 설정  (0) 2018.11.21
소스파일이 프로젝트로 묶여 빌드되기까지  (0) 2018.11.21