Android 앱 개발을 하다보면 Fragment 를 사용하거나, 하다못해 들어봤을텐데, 이런 형태일 것이다.
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_example, container, false)
}
이 Fragment 의 onCreateView에서 return 하는 inflater.inflate... 대체 뭐길래 View 타입으로 반환되는 것일까?
Fragment 는 추후에 좀 더 자세히 공부하기로 하고, 이 inflate 에 대해 공부한 내용을 정리한다.
Inflate?
inflate는 xml 로 만들어진 layout 파일을 메모리에 올려 View 타입 객체로 만드는 것을 말한다. 아래 이미지를 보자
Activity에서 findViewById를 사용하여 TextView에 text를 넣거나, Button 을 클릭했을때의 행동을 정의한다거나 할 때,
이 findViewById는 레이아웃에 정의한 TextView나 Button의 아이디를 가져와서 해당하는 타입의 객체로 생성한다.
이렇게 Activity에서 xml로 만들어진 layout의 요소에 접근할 수 있고, 사용할 수 있게 하는것이 바로 inflate 이다.
LayoutInflater
이러한 inflate 동작은 LayoutInflater 가 하게 된다. LayoutInflater 는 xml 리소스를 View객체로 반환하는 역할을 한다.
사용법은 다음과 같다.
val inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater // LayoutInflater 객체 생성
inflater.inflate(resource, root, attachToRoot)
LayoutInflater 객체의 inflater 함수를 사용하는데 inflate 함수의 인자로 resource에 객체화할 레이아웃, root에 레이아웃을 띄울 컨테이너, attachToRoot에 Boolean 값이 들어간다.
resource 에는 "R.layout.레이아웃명" 이 들어간다.
root에는 레이아웃을 띄울 컨테이너 인데, 이 컨테이너는 ViewGroup 타입이다.
ViewGroup 이란 View 를 포함하여 화면에 적절히 배치하기 위한 일종의 컨테이너로서, n개의 뷰를 포함할 수 있는 컨테이너 이다. LinearLayout, RelativeLayout, ConstraintLayout 등등이 이에 포함된다.
attachToRoot는 객체화 한 뷰를 컨테이너에 지금 바로 붙일건지(true), 나중에 붙일건지(false)를 지정한다. false로 했을 경우 나중에 addView 를 통해 붙여주어야 화면에 표시된다.
나는 처음에 inflate에 대해 잘 알지 못하고 사용할 때, inflate 로 붙인 레이아웃을 띄울 컨테이너는 무조건 FrameLayout
이라고 생각했었다. 하지만, ViewGroup 타입의 레이아웃은 전부 컨테이너가 될 수 있다.
다음 테스트 코드에서, FrameLayout, LinearLayout, RelativeLayout, ConstraintLayout 에 inflate 를 시켜보았다.
전체 코드
layout_inflate
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/inflate_string"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="inflate 되었음"
style="@style/nanumTextView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_inflate
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activity.InflateActivity">
<!-- 1. FrameLayout에 inflate -->
<FrameLayout
android:id="@+id/fr_layout"
android:layout_width="match_parent"
android:layout_height="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fr_layout" />
<!-- 2. LinearLayout에 inflate -->
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_layout"
android:layout_width="match_parent"
android:layout_height="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fr_layout" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fr_layout" />
<RelativeLayout
android:id="@+id/rl_layout"
android:layout_width="match_parent"
android:layout_height="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ll_layout" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_layout"
android:layout_width="match_parent"
android:layout_height="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/rl_layout" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_frameLayout"
style="@style/nanumButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginBottom="10dp"
android:padding="14dp"
android:text="프레임레이아웃 inflate"
app:layout_constraintBottom_toTopOf="@+id/btn_relativelayout"
app:layout_constraintEnd_toStartOf="@+id/btn_linearlayout"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_linearlayout"
style="@style/nanumButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:padding="14dp"
android:text="리니어레이아웃 inflate"
app:layout_constraintBottom_toTopOf="@+id/btn_constraint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_frameLayout" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_relativelayout"
style="@style/nanumButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:padding="14dp"
android:text="렐러티브레이아웃 inflate"
app:layout_constraintEnd_toStartOf="@+id/btn_constraint"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/v_endline" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_constraint"
style="@style/nanumButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:padding="14dp"
android:text="constraintlayout inflate"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_frameLayout"
app:layout_constraintBottom_toTopOf="@+id/v_endline" />
<View
android:id="@+id/v_endline"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="80dp"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_go_main"
style="@style/nanumButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginVertical="10dp"
android:padding="14dp"
android:text="메인으로"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/v_endline"/>
</androidx.constraintlayout.widget.ConstraintLayout>
InflateActivity
class InflateActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_inflate)
val frContainer = findViewById<FrameLayout>(R.id.fr_layout)
val llContainter = findViewById<LinearLayoutCompat>(R.id.ll_layout)
val rlContainter = findViewById<RelativeLayout>(R.id.rl_layout)
val clContainter = findViewById<ConstraintLayout>(R.id.cl_layout)
val inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
findViewById<AppCompatButton>(R.id.btn_go_main).apply{
setOnClickListener { finish() }
}
findViewById<AppCompatButton>(R.id.btn_frameLayout).apply {
setOnClickListener {
// attachToRooT 를 false로 주어 바로 붙이지 않았을때
val inflateView = inflater.inflate(R.layout.layout_inflate, frContainer, false)
frContainer.addView(inflateView) // 컨테이너에 붙이는 작업
inflateView.findViewById<AppCompatTextView>(R.id.inflate_string).text = "프레임레이아웃 inflate"
}
}
findViewById<AppCompatButton>(R.id.btn_linearlayout).apply{
setOnClickListener {
inflater.inflate(R.layout.layout_inflate, llContainter, true).findViewById<AppCompatTextView>(R.id.inflate_string).text = "리니어레이아웃 inflate"
}
}
findViewById<AppCompatButton>(R.id.btn_relativelayout).apply{
setOnClickListener {
inflater.inflate(R.layout.layout_inflate, rlContainter, true).findViewById<AppCompatTextView>(R.id.inflate_string).text = "렐러티브레이아웃 inflate"
}
}
findViewById<AppCompatButton>(R.id.btn_constraint).apply{
setOnClickListener {
inflater.inflate(R.layout.layout_inflate, clContainter, true).findViewById<AppCompatTextView>(R.id.inflate_string).text = "ConstraintLayout inflate"
}
}
}
}
결과
setContentView(R.layout.activity_main)
새로운 액티비티를 생성했을 때, 어디에서도 inflate 관련된 코드를 찾을 수 없는데, 그럼 어떻게 바로 findViewById를 사용할 수 있었던 것일까?
액티비티를 생성하면, setContentView(R.layout.액티비티레이아웃) 부분을 볼 수 있을 것이다.
이 setContentView 가 xml을 객체화 하는 inflate 동작을 내부적으로 수행한다. 다음은 Activity의 기본적인 코드이다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
Activity가 시작하면 onCreate() 가 불리고, 그 안에서 setContentView(R.layout.activity_main) 이 실행되며 activity_main.xml 이 Activity에 inflate된다.
마무리
한 화면 안에 다른 화면을 보여주어야 할때나, 동적으로 화면을 전환시켜야 할때 inflate를 이용할 수 있다.
inflate 는 xml 레이아웃을 객체화 해주는 것으로서,
LayoutInflater.inflate(resource: Int, container: ViewGroup, attachToRoot: Boolean) 의 형태이다.
출처
Android LayoutInflater 개념 및 사용 방법
LayoutInflater이란? LayoutInflater은 XML에 미리 정의해둔 틀을 실제 메모리에 올려주는 역할을 한다. Inflater 단어의 뜻이 부풀리다라는 의미로써 LayoutInflater라는 단어에서도 유추가 가능하다. 즉, LayoutI
www.crocus.co.kr
'Android' 카테고리의 다른 글
[Android] ViewBinding (1) | 2023.10.30 |
---|---|
[Android] onSaveInstanceState 사용하여 상태 유지하기 (0) | 2023.09.26 |
[Android] Thread 기본 (1) | 2023.09.15 |
[Android] RecyclerView 이해하기 (3) - 중복선택 방지, notifyDataSetChanged() (1) | 2023.09.07 |
[Android] 앱 전체에 적용되는 스타일 및 테마 지정하기 (0) | 2023.08.29 |