안드로이드 LayoutInflater 사용하기

LayoutInflater는 안드로이드에서 View를 만드는 가장 기본적인 방법입니다.

FragmentView를 만들거나 RecyclerView에서 ViewHolder를 만들때, CustomView에서 xml로 정의된 Viewmerge할 때 등 여러곳에서 사용됩니다.

이 글에서는 LayoutInflater를 적절히 사용하는 방법과, 비동기적으로 LayoutInflater를 사용 하는 방법을 알아보도록 하겠습니다.


LayoutInflater 생성하기

Context#getSystemService()

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

val inflater: LayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)

Activity#getLayoutInflater()

Activity에서는 LayoutInflater를 쉽게 얻어올수 있도록 getLayoutInflater() 를 제공합니다. Activity는 자신 windowLayoutInflater를 사용합니다.

val inflater: LayoutInflater = getLayoutInflater()

LayoutInflater.from()

이 방법은 가장 자주 사용하는 방법으로, LayoutInflaterstatic으로 정의되어있는 LayoutInflater.from을 통해 LayoutInflater를 만드는 방법입니다. 내부적으로 context#getSystemService를 호출 하고 있으며, 같은 context에서는 같은 객체를 리턴하기 때문에 굳이 멤버 변수로 선언해 놓지 않고 필요할 때마다 호출해서 사용해도 괜찮습니다.

val inflater: LayoutInflater = LayoutInflater.from(context) 
RecyclerView에서 ViewHolder를 만들때 LayoutInflater를 반복적으로 사용하기 때문에, kotlin extension을 사용해 편하게 사용 중 입니다.
fun ViewGroup.inflate(resId: Int, attach: Boolean = false): View
= LayoutInflater.from(context).inflate(resource, this, attachToRoot)
// ViewHolder(view = parent.inflate(R.layout.item_view))

AsyncLayoutInflater

LayoutInflate는 동기적으로(synchronous) 뷰를 생성 하기 때문에, support package에서는 비동기적으로 뷰를 만들 수 있는 AsyncLayoutInflater를 제공합니다. XML내의 뷰 hierarchy가 복잡하거나 UI스레드에서 inflate하는데 시간이 너무 오래걸린다면 사용 할 수 있습니다.

val inflater = AsyncLayoutInflater(context)
inflater.inflate(R.layout.my_layout, parent) { view, resId, parent ->
// parent.addView(view)
}

간단하게 비동기로 뷰를 생성 할수 있지만. 몇가지 주의할 점이 있습니다
1. parent의 generateLayoutParam()이 thread safe해야 합니다
2. 생성자에서 Handler를 만들거나 Looper.myLooper()를 사용하면 안됩니다.
3. LayoutInflater.Factory를 사용 할 수 없습니다.
4. Fragmentinflate 할 수 없습니다.


View inflate하기

inflater에서 View객체를 만들기 위해서는 inflater를 사용 하면 됩니다.

inflate(resource: Int, root: ViewGroup?, attachToRoot: Boolean)
  • resource: View를 만들고 싶은 레이아웃 파일의 id입니다. R.layout.my_view
  • root: 생성될 View의 parent를 명시해줍니다. null일 경우에는 LayoutParams값을 설정 할 수 없기 때문에 XML내의 최상위 android:layout_xxxxx값들이 무시되고 merge tag를 사용 할 수 없습니다.
  • attachToRoot: true 로 설정해 줄 경우 root의 자식 View로 자동으로 추가됩니다. 이때 rootnull 일 수 없습니다.
  • return: attachToRoot에 따라서 리턴값이 달라집니다. true일 경우 root가, false일 경우 XML내 최상위 뷰가 리턴됩니다.

View.inflate()

View에서는 LayoutInflaterinflate까지 한번에 실행하는 View.inflate()를 제공 하고 있습니다. (내부에서는 LayoutInflater.inflate를 수행함) 이 때 주의할점은 parentnull이 아니면 자동으로 attach됩니다.

View.inflate(context, R.layout.my_layout, parent)

요약

val inflater = LayoutInflater.from(context)
...
val view = inflater.inflate(R.layout.my_view, parent, false)
parent.addView(view)
...
inflater.inflate(R.layout.my_view, parent, true)
...
View.inflate(context, R.layout.my_view, parent)
  • XML레이아웃 파일에서 뷰를 생성할때는 LayoutInflater를 이용해야 한다.
  • 비동기로 뷰를 생성할때는 AsyncLayoutInflater를 이용 할 수 있다.
  • LayoutInflater는 LayoutInflater.from(context)를 이용하여 얻는다.
  • LayoutInflater객체의 inflate메서드를 이용하여 새로운 뷰를 생성 할 수 있다.
  • root를 지정하지 않을 경우 xml상의 최상위 뷰의 android:layout_xxxxx들은 무시된다. (참고자료)
  • attachToRoot를 true로 설정할경우 뷰를 생성할때 자동으로 root의 자식으로 추가된다.