Idealim
article thumbnail

/* 본 게시물은 ' ' 의 내용을 토대로 작성되었습니다. */

참고 자료

[취준생을 위한 안드로이드 앱만들기 뷰바인딩 - viewBinding] : https://www.youtube.com/watch?v=J_e8N7vwCFI&t=242s 

[Android] ViewBinding vs DataBinding :  https://velog.io/@jaeyunn_15/AndroidViewBinding-vs-DataBinding


#ViewBinding

1. ViewBinding 이란?

뷰 바인딩은 뷰와 상호 작용하는 코드를 보다 쉽게 작성할 수 있는 기능이다. 모듈의 build.gradle에서 뷰 바인딩 속성이 활성화 되면 해당 모듈에 있는 각 XML 레이아웃 파일에 대한 바인딩 클래스가 자동으로 생성된다. 바인딩 클래스 인스턴스에는 해당 레이아웃에 ID 가 있는 모든 뷰에 대해 직접적으로 참조된다. 기존에 사용하던 findViewById 메서드나 kotlin Android Extensions을 대체한다.


2. ViewBinding이 왜 도입 되었을까?

기존에 layout Compnoent 코드에 접근하는 방법은 크게 2가지 였다. 첫 번째로는 findViewById(), 두 번째로는 kotlin Android Extensions 이다. 

1. findViewById() 의 문제점

  • 코드가 쉽게 비대해진다. layout의 컴포넌트가 10개만 되더라도 같은 코드 10줄을 추가해야한다. 
  • null값에 대해 상대적으로 안전하지 못하다. 
  • Id 중복된 값에 취약하다. 예로 a.xml과 b.xml 각각에 id가 btn 인 버튼이 있다고 하자. 특정 액티비티에서 두 버튼을 호출한 후, 둘 중 버튼이 하나를 삭제해도 오류가 뜨지 않는다.

2. kotlin Android Extensions

  • 지원 중단 예정.. 
  • KAE도 JAVA로 디컴파일 시 findViewById() 메서드를 사용함.  

이러한 기존의 문제점들을 개선하고자 ViewBinding이 도입되었다.


3. ViewBinding 의 특징

1. ViewBinding의 특징

  • viewBinding은 빌드 과정에서 생성된다.
  • 끝에 Binding이란 단어가 붙는다
  • 카멜 표기법에 따라 네이밍 된다. (예를 들어 text_example.xml 인 경우 생성되는 바인딩 클래스의 이름은 TestExampleBinding가 된다.)
  • ID가 존재하지 않는 뷰에 대해선 클래스에 참조가 존재하지 않는다.
  • getRoot()메서드가 자동 포함 된다. layout file의 루트 뷰에 관한 직접 참고 제공한다.

2. ViewBinding의 장점

  • null safety - 뷰 직접 참조로 없는 아이디로 널포인트 익셉션 발생을 안한다.
  • type safety - 뷰 타입이 일치함으로 Class Cast Exception 발생 안한다.

4. ViewBinding 사용해보기

바텀 네비게이션 예제를 통해 ViewBinding을 실제로 사용해보자. 

1. ViewBinding 활성화 

ViewBinding은 안드로이드 스튜디오 3.6 부터 사용가능하다. build.gradle에 아래와 같이 viewBinding을 추가한다.

android {
        ...
        viewBinding {
            enabled = true
        }
    }

2. HomeFragment

class HomeFragment : Fragment() {

    // 뷰가 사라질 때 즉 메모리에서 날라갈 때 같이 날리기 위해 따로 빼두기
    private var fragmentHomeBinding :FragmentHomeBinding? = null
    private lateinit var mainActivityContext: MainActivity
    companion object {
        const val TAG : String = "로그"

        fun newInstance() : HomeFragment {
            return HomeFragment()
        }

    }

    // 메모리에 올라갔을때
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(TAG, "HomeFragment - onCreate() called")
    }

    // 프레그먼트를 안고 있는 액티비티에 붙었을 때
    override fun onAttach(context: Context) {
        super.onAttach(context)
        mainActivityContext = context as MainActivity
        Log.d(TAG, "HomeFragment - onAttach() called")
    }

    // 뷰가 생성되었을 때
    // 프레그먼트와 레이아웃을 연결시켜주는 부분이다.
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?

    ): View? {

        Log.d(TAG, "HomeFragment - onCreateView() called")

//        기존에 KAE에서 view를 가져오는 방법
//        val view = inflater.inflate(R.layout.fragment_home, container, false)
//        return view

        //뷰 바인딩 가져오기
        // FragmentHomeBinding
        val binding : FragmentHomeBinding = FragmentHomeBinding.inflate(inflater, container, false)
        fragmentHomeBinding = binding

        binding.homeFragmentBtn.setOnClickListener{ mainActivityContext.main_activity_text_view.text = "프래그먼트 버튼 클릭"}
        return binding.root
    }

    override fun onDestroyView() {
        fragmentHomeBinding = null
        super.onDestroyView()
    }


}
	//기존에 KAE에서 view를 가져오는 방법
        val view = inflater.inflate(R.layout.fragment_home, container, false)
        return view

        //뷰 바인딩 가져오기
        // FragmentHomeBinding
        val binding : FragmentHomeBinding = FragmentHomeBinding.inflate(inflater, container, false)
        return binding.root
기존의 KAE 방식과 유사하다. 직접 layout을 지정해주지 않아도 생성된 바인딩 클래스가 알아서 해줌.
3. MainActivity
class MainActivity : AppCompatActivity() {

    //
    private lateinit var homeFragment: HomeFragment
    private lateinit var rankingFragment: RankingFragment
    private lateinit var profileFragment: ProfileFragment
    private var activityMainBinding: ActivityMainBinding? = null
    companion object {

        const val TAG: String = "로그"

    }

    // 메모리에 올라갔을 때
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = ActivityMainBinding.inflate(layoutInflater)
        activityMainBinding = binding

        // 레이아웃과 연결
        setContentView(binding.root)

        Log.d(TAG, "MainActivity - onCreate() called")

        bottom_nav.setOnNavigationItemSelectedListener(onBottomNavItemSelectedListener)

        homeFragment = HomeFragment.newInstance()
        supportFragmentManager.beginTransaction().add(R.id.fragments_frame, homeFragment).commit()

    }


    // 바텀네비게이션 아이템 클릭 리스너 설정
    private val onBottomNavItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener {

        when(it.itemId){
            R.id.menu_home -> {
                activityMainBinding?.mainActivityTextView?.text = "홈버튼 클릭"
                Log.d(TAG, "MainActivity - 홈버튼 클릭!")
                homeFragment = HomeFragment.newInstance()
                supportFragmentManager.beginTransaction().replace(R.id.fragments_frame, homeFragment).commit()
            }
            R.id.menu_ranking -> {
                activityMainBinding?.mainActivityTextView?.text = "랭킹버튼 클릭"
                Log.d(TAG, "MainActivity - 랭킹버튼 클릭!")
                rankingFragment = RankingFragment.newInstance()
                supportFragmentManager.beginTransaction().replace(R.id.fragments_frame, rankingFragment).commit()
            }
            R.id.menu_profile -> {
                activityMainBinding?.mainActivityTextView?.text = "프로필버튼 클릭"
                Log.d(TAG, "MainActivity - 프로필버튼 클릭!")
                profileFragment = ProfileFragment.newInstance()
                supportFragmentManager.beginTransaction().replace(R.id.fragments_frame, profileFragment).commit()
            }
        }

        true
    }
}

 

실행결과

 

반응형
profile

Idealim

@Idealim

읽어주셔서 감사합니다. 잘못된 내용이 있으면 언제든 댓글로 피드백 부탁드립니다.