ViewBinding과 DataBinding을 사용하는 이유
Null Safety
: 뷰를 직접 참조하기 때문에 없는 아이디로 인한 null exception 발생하지 않음.Type Safety
: 뷰 타입이 일치하기 때문에 클래스 변환 예외가 발생할 위험이 없다. (ex. textView 인데 imageView라고 잘못 적어서 class cast exception이 발생할 위험이 없다.)ViewBinding 사용하기
ViewBinding
을 사용하면 뷰와 상호작용하는 코드를 쉽게 작성할 수 있다. (간단하게 작성할 수 있다는 것이 큰 장점)둘 다 뷰를 직접 참조하는데 사용할 수 있는 binding class를 제공한다.
뷰 바인딩은 보다 단순한 처리의 경우 적합하다.
<장점>
<단점>
buildFeatures{
viewBinding = true
}
// https://developer.android.com/topic/libraries/view-binding?hl=ko
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
binding.testText.text= "이거는 변경된 텍스트"
binding.testText.setOnClickListener{
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
}
}
<?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=".MainActivity">
<TextView
android:id="@+id/testText"
android:textSize="50sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
class SecondActivity : AppCompatActivity() {
val manager =supportFragmentManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val transaction = manager.beginTransaction()
val fragment = TestFragment()
transaction.replace(R.id.frameArea, fragment)
transaction.addToBackStack(null)
transaction.commit()
}
}
<?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=".SecondActivity">
<FrameLayout
android:id="@+id/frameArea"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
class TestFragment : Fragment() {
private var _binding : FragmentTestBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
_binding = FragmentTestBinding.inflate(inflater, container, false)
val view = binding.root
binding.fragmentText.text= "이거는 framgent Text"
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".TestFragment">
<!--TODO: Update blank fragment layout-->
<TextView
android:id="@+id/fragmentText"
android:textSize="50sp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</FrameLayout>
DataBinding 사용하기
DataBinding 라이브러리는 UI 요소와 데이터를 프로그래밍적 방식으로 연결하지 않고 선언적 방식으로 결합할 수 있게 도와주는 라이브러리이다.
기존 프로그래밍적 방식으로는 아래와 같이 작성한다.
findViewById<TextView>(R.id.sample_text).apply {
text = viewModel.userName
}
그러나 DataBinding을 이용하면 레이아웃 파일에서 직접 위젯에 텍스트를 할당할 수 있다. 즉, 코틀린/자바 코드를 직접 호출하지 않아도 된다.
<TextView
android:text="@{viewmodel.userName}" />
buildFeatures{
dataBinding = true
}
// DataBinding
// ViewBinding 이랑 뭐가 다른가?
// 이름처럼 데이터를 연결해주는 역할을 할 수 있습니다. (데이터와 같이 결합해서 사용할 수 있음)
// DataBinding + LiveData
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.dataBindingEx.text= "바뀐 텍스트"
binding.dataBindingEx.setOnClickListener{
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout
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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/dataBindingEx"
android:textSize="50sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
class SecondActivity : AppCompatActivity() {
val manager =supportFragmentManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val transaction = manager.beginTransaction()
val fragment = TestFragment()
transaction.replace(R.id.frameArea, fragment)
transaction.addToBackStack(null)
transaction.commit()
}
}
<?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=".SecondActivity">
<FrameLayout
android:id="@+id/frameArea"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
class TestFragment : Fragment() {
lateinit var binding : FragmentTestBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_test, container, false)
binding.fragmentText.text= "변경된 텍스트"
return binding.root
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".TestFragment">
<!--TODO: Update blank fragment layout-->
<TextView
android:id="@+id/fragmentText"
android:textSize="50sp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</FrameLayout>
</layout>
DataBinding과 data class
data 객체를 view와 bind해서 사용해보자.
data class Person (
val name : String,
val age : Int
)
view에 보여줄 Person이라는 data class를 선언한다.
// DataBinding
// 이름처럼, 뭔가 데이터를 어쩌고 저쩌고 해줄 수 없을까?(연결)
// 데이터 결합
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
var testCount = 20
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
// 기존의 방법
// binding.test.text = "바뀐 텍스트"
// 데이터 결합
val person = Person("개복치", 20)
binding.person= person
}
fun myClick(view : View) {
Log.d("MainActivity", "onClick")
testCount++
val person = Person("개복치", testCount)
binding.person= person
}
}
Person 객체를 초기화하여 xml 파일에서 선언된 data에 set해준다.
<?xml version="1.0" encoding="utf-8"?>
<layout
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">
<data>
<variable
name="person"
type="com.bokchi.jetpack_ex.Person" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<TextView
android:id="@+id/test"
android:textSize="100dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{person.name}"
/>
<TextView
android:textSize="100dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{Integer.toString(person.age)}"
/>
<TextView
android:textSize="100dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{person.age > 30 ? `나이 많음` : `나이 적음`}"
/>
<Button
android:text="btn"
android:onClick="myClick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
<data>
태그는 <layout>
에서 사용할 변수를 정의하는데 사용된다.
나는 Person 이라는 data class 객체를 사용할 것이기 때문에 name이 person인 변수를 한개 선언해주었다.
레이아웃 내의 표현식은 "@{}"
구문을 사용하여 작성한다.
참고