Layout Inflation

Inflation

어플리케이션에서 화면을 정의하기 위해서는 .xml 파일을 정의해야한다. 이후 정의한 .xml파일과 연결되는 액티비티 클래스도 정의해야한다.

private val button : Button by lazy {
	findViewById(R.id.exampleButton)
}

보통 이런식으로 해당 레이아웃과 연결해주는데 이 때 레이아웃의 내용이 메모리에 객체화되는 과정을 인플레이션(Inflation) 이라고 한다.

레이아웃은 앱이 실행되는 시점에 메모리에 객체화가 된다. 즉, .xml 파일에 Button 을 정의해도 앱은 자기가 실행되기 전까지 버튼이 있는지 모른다. 다시 말해서 setContentView(R.layout.activity_main) 이 호출되기 전까지는 버튼이 있는지 모른다는 말이다.

실제 코드로 작성해서 실행시켜보자.

package com.example.event

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.GestureDetector
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.core.view.GestureDetectorCompat

class MainActivity : AppCompatActivity() {
    private val button : Button by lazy {
        findViewById(R.id.exampleButton)
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        bind()
        setContentView(R.layout.activity_main)
    }

    private fun bind() {
        button.setOnClickListener {
            Toast.makeText(applicationContext, "버튼 눌림", Toast.LENGTH_LONG).show()
        }
    }
}

앱을 실행시켜보면 바로 죽는 것을 볼 수 있다.

왜 그럴까? 그 이유는 메모리에 객체화되지 않은 레이아웃을 참조하려고 했기 때문이다.

실제로 오류 목록을 보면 NullPointException 이 발생한 것을 볼 수 있다.

setContentView()가 중요한 건 알겠는데 정확하게 뭘 하는 메소드인가요? 😐

setContentView()는 화면에 표시할 레이아웃이나 뷰 객체를 지정하는 역할을 한다. 위의 코드에서도 activity_main을 매개변수로 전달하여 레이아웃에 들어있는 뷰(Button)이 메모리에 객체화 될 수 있도록 한다. 그렇다면 화면 전체에 보여주는 레이아웃이 아니라 일부분, 부분 레이아웃도 setContentView()로 보여줄 수 있을까? 아쉽게도 할 수 없다. setContentView()는 액티비티의 화면 전체(메인 레이아웃)을 설정하는 역할만을 수행하기 때문이다.

그럼 부분 화면은 어떻게 객체화해요...? 😡

이 때 LayoutInflater를 사용할 수 있다. LayoutInflater를 사용하기 위한 방법은 총 3가지가 있다. (오늘 아침부터 삽질 개많이 함...)

  • getSystemService()
  • getLayoutInflater()
  • LayoutInflater.from()

getSystemService()

가장 기본적인 방법으로 context 에서 LayoutInflater를 가져오는 방법이다.

getLayoutInflater()

LayoutInflater 객체를 가져오는 방법이다. 자신 window의 LayoutInflater 를 사용한다.

LayoutInflater.from()

가장 자주 사용하는 방법으로 LayoutInflaterstatic 으로 정의되어 있는 LayoutInflater.from 을 통해 객체를 만드는 방법이다. 같은 context 에서는 같은 객체를 리턴하기 때문에 굳이 멤버 변수로 선언해놓지 않아도 된다.

View Inflate하기

inflation을 사용하기 위해선 선언한 LayoutInflation의 inflater 를 사용하면 된다.

inflate(resource : Int, root : ViewGroup?, attachToRoot : Boolean)
  • resourse : View를 만들고 싶은 레이아웃 파일의 id -> R.id.~~~ 를 넘겨준다.
  • root : 생성될 View의 parent. 즉, 생성될 공간의 layout 객체를 넘겨준다.
  • attachToRoot : true로 설정할 경우 root의 자식 view로 자동으로 추가된다.

실제 코드로 살펴보자.

activity_mainFrameLayoutButton을 가진다. 버튼이 클릭되면 FrameLayout에 TextView를 넣어줄 계획이다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/frame"
        android:layout_width="300dp"
        android:layout_height="300dp">
    </FrameLayout>

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
         android:layout_height="wrap_content"/>

</LinearLayout>

이건 FrameLayout에 넣어줄 TextView

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent">

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="안녕하세요"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

main_activity 는 위에서 말한 3가지 방법을 모두 사용해봤다.

package com.example.example
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.widget.Button
import android.widget.FrameLayout

class MainActivity : AppCompatActivity() {
    private val frame: FrameLayout by lazy {
        findViewById(R.id.frame)
    }

    private val button: Button by lazy {
        findViewById(R.id.button)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        bind()
    }

    private fun bind() {
        val inflater1: LayoutInflater =
            getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        val inflater2: LayoutInflater = LayoutInflater.from(applicationContext)
        val inflater3: LayoutInflater = layoutInflater
        button.setOnClickListener {
            inflater1.inflate(R.layout.sub1, frame, true)
            inflater2.inflate(R.layout.sub1, frame, true)
            inflater3.inflate(R.layout.sub1, frame, true)
        }
    }
}

inflate 의 첫 번째 인자로 보여줄 레이아웃인 sub1, 두 번째 인자로는 sub1을 넣을 레이아웃의 객체를 넣어줬다. 실행시켜보면 버튼을 클릭하면 텍스트가 추가되는 것을 볼 수 있다.

좋은 웹페이지 즐겨찾기