본문 바로가기

안드로이드/JetPack

[안드로이드] 6. DataBinding

DataBinding

DataBinding을 액티비티에서 데이터를 XML로 전달할 수 있다. 그리고 그 반대 또한 가능하다. 추가로 Java에서는 findViewByID를 더 이상 쓸 필요가 없어진다. (XML을 바인딩 하였기에 리턴값으로 바인딩 객체가 반환되는데 이를 이용.) 하지만, 가장 중요한 점은 MainActvity의 UI 갱신문이 사라지게 된다.

 

기본 구성

데이터 바인딩 사용을 위해 build:gradle(:app)에 다음을 추가

android{
    ...
	dataBinding{
    	    enabled = true
    	}
    ...
}

 

그 다음은 xml의 최상위 레이아웃을 layout 태그로 감싼다.

<?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
           ...

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

MainActivity.kt

class MainActivity : AppCompatActivity() {
    val ViewModel: MainViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //setContentView(R.layout.actvitiy_main)
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
	binding.lifecycleOwner = this //LiveData를 활용하기 위함
        
        ViewModel.getAll().observe(this, Observer { todos ->
            result_textView.text = todos.toString()
        }) //getAll을 할 경우 todos로 값이 들어온다.

        add_button.setOnClickListener {
            lifecycleScope.launch(Dispatchers.IO) {
                ViewModel.insert(Todo(todo_edit.text.toString()))
            }
        }
    }
}

setContentView(R.layout.actvitiy_main)을 DataBindingUtil... 구문으로 대체한다. 그러면 ActvityMainBinding이라는 클래스가 반환되어 binding 변수에 저장된다. 이때, 클래스 이름은 XML을 따른다. 예를들어, activity_main이라면 ActvityMainBinding, activity_login이라면 ActvityLoginBinding이다.

 

추가로 LiveData를 바인딩에서도 활용하려면 바인딩 객체의 lifecycleOwner를 this로 설정해주어야 한다.

 

 

actvitiy_main.xml

<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="viewModel"
            type="com.example.sampleapp.MainViewModel" />
    </data>
    ...
    

그 다음은 위 처럼 data 태그로 감싸 변수를 생성할 수 있다. 그리고 액티비티에서 해당 변수에 데이터를 전달하여 데이터를 바인딩 할 수 있다.

 

 

우리의 최종 목표는  MainActvity.kt에 존재하는 아래의 코드를 없애는 것이다.

ViewModel.getAll().observe(this, Observer { todos ->
    result_textView.text = todos.toString()
}) //getAll을 할 경우 todos로 값이 들어온다.

먼저 MainActivity에서 위 코드를 제거한다.

class MainActivity : AppCompatActivity() {
    val ViewModel: MainViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.lifecycleOwner = this
        binding.viewModel = ViewModel

        add_button.setOnClickListener {
            lifecycleScope.launch(Dispatchers.IO) {
                ViewModel.insert(Todo(todo_edit.text.toString()))
            }
        }
    }
}

 

그 다음 MainViewModel.kt를 다음과 같이 수정한다.

class MainViewModel(application: Application): AndroidViewModel(application) {
    val db = Room.databaseBuilder(
        application,
        AppDatabase::class.java, "database-name"
    ).build()

	//XML에서 접근하기 위해 새로 추가된 부분
    var todos: LiveData<List<Todo>>
    init {
        todos = getAll()
    }

    fun getAll() : LiveData<List<Todo>>{
        return db.todoDao().getAll()
    }

    suspend fun insert(todo: Todo){
        db.todoDao().insert(todo)
    }
}

 

마지막으로 XML을 다음과 같이 수정한다.

 ...
<TextView
   ...
   android:text="@{viewModel.todos.toString()}"
   ...

결과로 액티비티 내의 UI 갱신 코드가 없이 XML 코드만으로 갱신이 됨을 확인할 수 있다.


 

D추가로 사실 위의 버튼 또한 없앨 수 있다. 버튼을 없앤다면 EditText의 값을 MainViewModel의 값으로 가져오고, 버튼 클릭 시 갱신하는 문장을 XML에 추가하면 된다.

 

XML만 간단하게 살펴보면

<?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="viewModel"
            type="com.example.sampleapp.MainViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/result_textView"
            ...
            android:text="@{viewModel.todos.toString()}"
            ... />

        <Button
            android:id="@+id/add_button"
            ...
            android:onClick="@{()->viewModel.insert(viewModel.newTodo)}"
            ... />

        <EditText
            android:id="@+id/todo_edit"
            ...
            android:text="@={viewModel.newTodo}"
            ... />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

버튼의 onClick 구문과 EditText의 text 구문을 확인해보면 대충 어떻게 동작할 지 짐작이 갈 것이다. EditText의 text값은 viewModel의 newTodo로 갱신되는 중이고, 버튼을 클릭할 시 viewModel의 insert 메서드를 수행하게 된다.