[AAC] View Binding

dwjeongยท2023๋…„ 11์›” 2์ผ
0

์•ˆ๋“œ๋กœ์ด๋“œ

๋ชฉ๋ก ๋ณด๊ธฐ
12/28

๐Ÿ”Ž View Binding์ด๋ž€

๋ทฐ์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ.
๋ชจ๋“ˆ์—์„œ ๋ทฐ ๋ฐ”์ธ๋”ฉ์„ ํ™œ์„ฑํ™”ํ•˜๋ฉด ํ•ด๋‹น ๋ชจ๋“ˆ์— ์žˆ๋Š” ๊ฐ XML ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์— ๋Œ€ํ•ด ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•จ.
๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋Š” ํ•ด๋‹น ๋ ˆ์ด์•„์›ƒ์˜ ID๊ฐ€ ์žˆ๋Š” ๋ชจ๋“  ๋ทฐ์— ๋Œ€ํ•œ ์ง์ ‘์ ์ธ ์ฐธ์กฐ๋ฅผ ํฌํ•จ.
๋ทฐ ๋ฐ”์ธ๋”ฉ์€ findViewById๋ฅผ ๋Œ€์ฒดํ•จ.

  • ์„ค์ •
    ๋ชจ๋“ˆ์—์„œ ๋ทฐ ๋ฐ”์ธ๋”ฉ์„ ํ™œ์„ฑํ™”ํ•˜๋ ค๋ฉด ๋ชจ๋“ˆ ์ˆ˜์ค€์˜ build.gradle ํŒŒ์ผ์—์„œ viewBinding ์˜ต์…˜์„ true๋กœ ์„ค์ •.
android {
    ...
    buildFeatures {
        viewBinding = true
    }
}

๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์˜ ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‹ถ์ง€ ์•Š์€ ๊ฒฝ์šฐ tools:viewBindingIgnore="true" ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•จ.

<LinearLayout
        ...
        tools:viewBindingIgnore="true" >
    ...
</LinearLayout>

๐Ÿ“ ์‚ฌ์šฉ๋ฒ•

๊ฐ ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค์—๋Š” ๋ฃจํŠธ ๋ทฐ์™€ ID๊ฐ€ ์žˆ๋Š” ๋ชจ๋“  ๋ทฐ์— ๋Œ€ํ•œ ์ฐธ์กฐ๊ฐ€ ํฌํ•จ๋จ.
๋ฐ”์ธ๋”ฉํด๋ž˜์Šค์˜ ์ด๋ฆ„์€ XML ํŒŒ์ผ์˜ ์ด๋ฆ„์„ ํŒŒ์Šค์นผ ์ผ€์ด์Šค๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , "Binding"์ด๋ผ๋Š” ๋‹จ์–ด๋ฅผ ๋’ค์— ์ถ”๊ฐ€ํ•˜์—ฌ ์ƒ์„ฑ๋จ.

result_profile.xml ์ด๋ผ๋Š” ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ

<LinearLayout ... >
    <TextView android:id="@+id/name" />
    <ImageView android:cropToPadding="true" />
    <Button android:id="@+id/button"
        android:background="@drawable/rounded_button" />
</LinearLayout>

์ƒ์„ฑ๋œ ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค๋ช…์€ ResultProfileBinding.

์œ„์˜ ์ฝ”๋“œ์—์„œ ImageView๋Š” id๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค์—์„œ ์ด๋ฅผ ์ฐธ์กฐํ•˜์ง€ ์•Š์Œ.

๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค๋Š” ํ•ด๋‹น ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์˜ ๋ฃจํŠธ๋ทฐ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋Š” getRoot() ๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ.
๐Ÿ‘‰ ResultProfileBinding ํด๋ž˜์Šค์—์„œ๋Š” getRoot() ๋ฉ”์„œ๋“œ๊ฐ€ LinearLayout ๋ฃจํŠธ ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋จ.



์•กํ‹ฐ๋น„ํ‹ฐ์—์„œ ๋ทฐ ๋ฐ”์ธ๋”ฉ ์‚ฌ์šฉ

์•กํ‹ฐ๋น„ํ‹ฐ์—์„œ ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด onCreate() ๋ฉ”์„œ๋“œ์—์„œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•จ.

  1. ์ƒ์„ฑ๋œ ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค์— ํฌํ•จ๋œ ์ •์  inflate() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœ.
    ๐Ÿ‘‰ ์•กํ‹ฐ๋น„ํ‹ฐ์—์„œ ์‚ฌ์šฉํ•  ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋จ.

  2. ๋ฃจํŠธ ๋ทฐ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด getRoot()๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ์ฝ”ํ‹€๋ฆฐ ํ”„๋กœํผํ‹ฐ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉ.

  3. ๋ฃจํŠธ ๋ทฐ๋ฅผ setContentView()์— ์ „๋‹ฌํ•˜์—ฌ ํ™”๋ฉด์—์„œ ํ™œ์„ฑ ๋ทฐ๋กœ ๋งŒ๋“ฆ.

private lateinit var binding: ResultProfileBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ResultProfileBinding.inflate(layoutInflater)
    val view = binding.root
    setContentView(view)
}
  1. ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉ
binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }



ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ ๋ทฐ ๋ฐ”์ธ๋”ฉ ์‚ฌ์šฉ

ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด onCreateView() ๋ฉ”์„œ๋“œ์—์„œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰.

  1. ์ƒ์„ฑ๋œ ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค์— ํฌํ•จ๋œ ์ •์  inflate() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœ.
    ๐Ÿ‘‰ ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ ์‚ฌ์šฉํ•  ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋จ.

  2. ๋ฃจํŠธ ๋ทฐ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด getRoot()๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ์ฝ”ํ‹€๋ฆฐ ํ”„๋กœํผํ‹ฐ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉ.

  3. ๋ฃจํŠธ ๋ทฐ๋ฅผ onCreateView()๋ฉ”์„œ๋“œ์— ๋ฐ˜ํ™˜ํ•˜์—ฌ ํ™”๋ฉด์—์„œ ํ™œ์„ฑ ๋ทฐ๋กœ ๋งŒ๋“ฆ.

private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    _binding = ResultProfileBinding.inflate(inflater, container, false)
    val view = binding.root
    return view
}

override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}
  1. ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉ
binding.name.text = viewModel.name
binding.button.setOnClickListener { viewModel.userClicked() }

ํ”„๋ž˜๊ทธ๋จผํŠธ๋Š” ๋ทฐ๋ณด๋‹ค ์˜ค๋ž˜ ์‚ด์•„๋‚จ์Œ. ๋”ฐ๋ผ์„œ ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ onDestroyView()์—์„œ ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค์— ๋Œ€ํ•œ ๋ชจ๋“  ์ฐธ์กฐ๋ฅผ ์ •๋ฆฌํ•ด์•ผ ํ•จ. (๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํšŒ์ˆ˜ํ•˜๋ ค ํ•ด๋„ binding์ด ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด ํšŒ์ˆ˜ํ•˜์ง€ ๋ชปํ•ด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐœ์ƒ)




๐Ÿ“ tools:viewBindingType ์†์„ฑ

# ์„ธ๋กœ๋ชจ๋“œ xml
# in res/layout/example.xml

<TextView android:id="@+id/user_bio" />

# ๊ฐ€๋กœ๋ชจ๋“œ xml
# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" />

TextView๊ฐ€ TextView์™€ EditText์˜ ๊ณตํ†ต ์ƒ์œ„ ํด๋ž˜์Šค์ด๋ฏ€๋กœ TextViewํƒ€์ž…์„ ๊ฐ€์ง„ userBio ํ•„๋“œ๋ฅผ ๋…ธ์ถœ์‹œํ‚ฌ ๊ฒƒ์œผ๋กœ ๊ธฐ๋Œ€ํ•˜์ง€๋งŒ, ๊ธฐ์ˆ ์  ์ œ์•ฝ์œผ๋กœ ์ธํ•ด ๋ทฐ ๋ฐ”์ธ๋”ฉ ์ฝ”๋“œ ์ƒ์„ฑ๊ธฐ๋Š” ๊ฒฐ์ •์„ ๋‚ด๋ฆด ์ˆ˜ ์—†์–ด View ํƒ€์ž… ํ•„๋“œ๋ฅผ ์ƒ์„ฑํ•จ.

๋”ฐ๋ผ์„œ binding.userBio as TextView์™€ ๊ฐ™์ด ์บ์ŠคํŒ… ํ•ด์•ผ ํ•จ.

๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด tools:viewBindingType ์†์„ฑ ์ง€์›. ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปดํŒŒ์ผ๋Ÿฌ์—๊ฒŒ ์ƒ์„ฑ๋œ ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉํ•  ํƒ€์ž…์„ ์•Œ๋ ค์ค„ ์ˆ˜ ์žˆ์Œ.

# in res/layout/example.xml (unchanged)

<TextView android:id="@+id/user_bio" />

# in res/layout-land/example.xml

<EditText android:id="@+id/user_bio" tools:viewBindingType="TextView" />



  • ๋˜ ๋‹ค๋ฅธ ์˜ˆ์‹œ
# in res/layout/navigation_example.xml

<BottomNavigationView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

# in res/layout-w720/navigation_example.xml

<NavigationRailView android:id="@+id/navigation" tools:viewBindingType="NavigationBarView" />

ํ•˜๋‚˜๋Š” BottomNavigationView๋ฅผ ํฌํ•จํ•˜๊ณ  ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” NavigationRailView๋ฅผ ํฌํ•จํ•˜๋Š” ๋‘ ๊ฐœ์˜ ๋ ˆ์ด์•„์›ƒ์ด ์žˆ๋Š” ๊ฒฝ์šฐ, ๋‘ ํด๋ž˜์Šค๋Š” NavigationBarView๋ฅผ ํ™•์žฅํ•จ.

ํ˜„์žฌ ๋ ˆ์ด์•„์›ƒ์— ์–ด๋–ค ํ•˜์œ„ ํด๋ž˜์Šค๊ฐ€ ์žˆ๋Š”์ง€ ์ •ํ™•ํžˆ ์•Œ ํ•„์š”๊ฐ€ ์—†์„ ๊ฒฝ์šฐ tools:viewBindingType์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‘ ๋ ˆ์ด์•„์›ƒ์—์„œ ์ƒ์„ฑ๋œ ์œ ํ˜•์„ NavigationBarView๋กœ ์„ค์ •.



โ— tools:viewBindingType ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์ 

  1. ๊ฐ’์ด android.view.View๋ฅผ ์ƒ์†ํ•œ ํด๋ž˜์Šค์—ฌ์•ผ ํ•จ.
  2. ๊ฐ’์€ ํ•ด๋‹น ํƒœ๊ทธ์˜ ์ƒ์œ„ ํด๋ž˜์Šค์—ฌ์•ผ ํ•จ.

๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ

  <TextView tools:viewBindingType="ImageView" /> <!-- ImageView is not related to TextView. -->
  <TextView tools:viewBindingType="Button" /> <!-- Button is not a superclass of TextView. -->




๐Ÿ“ findViewById์™€์˜ ์ฐจ์ด์ 

  • findViewById ์‚ฌ์šฉ ๋ณด๋‹ค ์ข‹์€ ์ 
  1. Null safety
    ๋ทฐ ๋ฐ”์ธ๋”ฉ์€ ๋ทฐ์— ์ง์ ‘์ ์œผ๋กœ ์ฐธ์กฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž˜๋ชป๋œ ๋ทฐ id๋กœ ์ธํ•œ ๋„ ํฌ์ธํ„ฐ ์˜ˆ์™ธ์˜ ์œ„ํ—˜์ด ์—†์Œ.

  2. Type safety
    ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค์˜ ๊ฐ ํ•„๋“œ๋Š” xml ํŒŒ์ผ์—์„œ ์ฐธ์กฐํ•˜๋Š” ๋ทฐ์™€ ์ผ์น˜ํ•˜๋Š” ํ˜•์‹์„ ๊ฐ€์ง.
    ๐Ÿ‘‰ ํด๋ž˜์Šค ์บ์ŠคํŠธ ์˜ˆ์™ธ์˜ ์œ„ํ—˜์ด ์—†๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธ.



๐Ÿ“ Data binding๊ณผ์˜ ์ฐจ์ด์ 

view binding๊ณผ data binding์€ ๋ชจ๋‘ ๋ทฐ๋ฅผ ์ง์ ‘ ์ฐธ์กฐํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑ.

  1. data binding ๋ณด๋‹ค ์ข‹์€ ์ 
  • ๋น ๋ฅธ ์ปดํŒŒ์ผ: ๋ทฐ ๋ฐ”์ธ๋”ฉ์€ annotation processing์ด ํ•„์š” ์—†์œผ๋ฏ€๋กœ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์ด ๋นจ๋ผ์ง.
  • ์‚ฌ์šฉ ํŽธ์˜์„ฑ: ํŠน๋ณ„ํ•œ ํƒœ๊ทธ๊ฐ€ ์žˆ๋Š” XML ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์ด ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์‚ฌ์šฉ์ด ์šฉ์ดํ•˜๋ฉฐ ๋ชจ๋“ˆ์—์„œ ํ•œ๋ฒˆ ํ™œ์„ฑํ™” ์‹œํ‚ค๋ฉด ํ•ด๋‹น ๋ชจ๋“ˆ์˜ ๋ชจ๋“  ๋ ˆ์ด์•„์›ƒ์— ์ ์šฉ๋จ.

  1. ์ œํ•œ ์‚ฌํ•ญ
  • layout variables or layout expressions๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์•„ XML ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์˜ ๋™์  UI ์ปจํ…์ธ ๋ฅผ ์„ ์–ธํ•˜๋Š”๋ฐ ์ด์šฉ ๋ถˆ๊ฐ€.
  • Two-way binding์„ ์ง€์›ํ•˜์ง€ ์•Š์Œ.

ํ”„๋กœ์ ํŠธ์— ๋”ฐ๋ผ์„œ view binding๊ณผ data binding์„ ๋ชจ๋‘ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹์„ ์ˆ˜ ์žˆ์Œ.

๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์€ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์„ ํ•„์š”๋กœ ํ•˜๋Š” ๋ ˆ์ด์•„์›ƒ์— ์‚ฌ์šฉํ•˜๊ณ , ๋ทฐ ๋ฐ”์ธ๋”ฉ์€ ๊ทธ๋ ‡๊ฒŒ ์•Š์€ ๋ ˆ์ด์•„์›ƒ์— ์‚ฌ์šฉ.

0๊ฐœ์˜ ๋Œ“๊ธ€