서두는 이전 포스팅에서 썼기 때문에 날리고 예제로 바로 들어가겠다.

 

기본적으로 DataBinding과 LiveData를 사용하여 예제를 진행할 것이며, Model 역할에는 Room(DB)을 사용할 것이다.

 

DataBinding을 사용하기 위해 App단위 Gradle에

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

또한 LiveData, Coroutine, ViewModel 를 사용하기 위해 이것들을 추가한다.

implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha01"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha01"

    def lifecycle_version = "2.0.0"

    // ViewModel and LiveData
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"

 

 

아무 class 파일에다가 Room을 적게 되면 Alt+Enter를 사용해 import가 가능하다.

import가 정상적으로 진행되면 이러한 dependencies가 자동으로 추가된다.

    implementation 'androidx.room:room-runtime:2.2.3'
    kapt 'androidx.room:room-compiler:2.2.3'

1. import를 진행한 후 Room에 저장한 Data의 Schema를 만들어보자

@Entity
data class Todo(var title : String)
{

@PrimaryKey(autoGenerate = true) var id : Int = 0

}

2. Query문을 생성하는 Interface를 작성한다.

@Dao
interface TodoDao
{

    @Query("SELECT * FROM Todo")
     fun getAll() : LiveData<List<Todo>>

    @Insert
     fun insert(todo:Todo)

    @Update
     fun update(todo:Todo)

    @Delete
     fun delete(todo:Todo)

}

 

3. MainActivity를 작성.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //setContentView(R.layout.activity_main)
        //setContextView를 대신한다
        val binding = DataBindingUtil
                .setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.lifecycleOwner = this // LiveData를 사용하기 위해서 없으면 Observe할때마다 refresh안딤

        //binding 객체는 layout의 객체로 생각한다?

        //ViewModelProviders를 사용하여 ViewModel을 불러온다.
        val viewModel = ViewModelProviders.of(this)[MainViewModel::class.java]
        binding.viewModel = viewModel//layout의 binding 객체의 name = viewModel 에 viewModel을 초기화

        /*
        //LiveData 형식의 Data를 관찰하여 바뀔때마다 불린다??
        viewModel.getAll().observe(this, object : Observer<List<Todo>?> {
            override fun onChanged(t: List<Todo>?) {

                result_text.text = t.toString()

            }
        })

        layout에 DataBinding을 하였기 떄문에 MainViewModel의 LivewData가 변경될 떄마다
        자동적으로 layout의 binding 객체를 통해 Data 전송?
*/
        /*
        add_button.setOnClickListener(object : View.OnClickListener {
            override fun onClick(v: View?) {
                //비동기 처리
                //Dispatchers.IO -> Worker Thread (백그라운드 )
                lifecycleScope.launch(Dispatchers.IO) {
                    viewModel.insert(Todo(todo_edit.text.toString()))
                }
                //이 코드 자체가 background에서 실행된다( 비동기 )
            }
        })

*/

    }
}

다른 패턴에서의 Activity는 setContentView()를 사용하여 layout과 Activity를 연결하지만

Binding을 사용했기 때문에 setContentView()가 사라진 것을 볼 수 있다.

 

4. ViewModel 생성.

//AndroidViewModel를 쓰는이유 : context가 필요하기 때문
//ViewModel은 context를 사용할 수 없다
class MainViewModel(application: Application) : AndroidViewModel(application) {
    private val db = Room.databaseBuilder(
        application,
        AppDatabase::class.java, "database-name"
    )
        .build()
    var todos : LiveData<List<Todo>>

    var newTodo : String?=null

    init{
        todos = getAll()
    }



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

    fun insert(todo: String) {
        viewModelScope.launch(Dispatchers.IO) {
            db.todoDao().insert(Todo(todo)) }
    }

}

 

5. layout을 생성

<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.practice_nexterz.MainViewModel"/>
        <!-- Int String 같은것도 됨 -->

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

        <Button
            android:id="@+id/add_button"
            android:onClick="@{() -> viewModel.insert(viewModel.newTodo)}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:text="추가"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent">

        </Button>

        <EditText
            android:id="@+id/todo_edit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="할 일"
            android:text="@={viewModel.newTodo}"
            android:inputType="textPersonName"
            app:layout_constraintEnd_toStartOf="@+id/add_button"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

        </EditText>

        <TextView
            android:id="@+id/result_text"
            android:text="@{viewModel.todos.toString()}"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="16dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/todo_edit"></TextView>

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

 


val binding = DataBindingUtil
                .setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        binding.lifecycleOwner = this // LiveData를 사용하기 위해서 없으면 Observe할때마다 refresh안딤
<data>
    <variable
        name="viewModel"
        type="com.example.practice_nexterz.MainViewModel"/>
        <!-- Int String 같은것도 됨 -->

</data>

setContentView()를 사용하지 않고 DataBinding을 사용하여 layout과 Activity를 연결시킨다.(View)

 


 //ViewModelProviders를 사용하여 ViewModel을 불러온다.
        val viewModel = ViewModelProviders.of(this)[MainViewModel::class.java]
        binding.viewModel = viewModel//layout의 binding 객체의 name = viewModel 에 viewModel을 초기화

layout의 변수 viewModel 에 실제 ViewModel을 초기화하여 View 단에서 UI 변경시 ViewModel의 Data에 접근할 수 있다.

 

//LiveData 형식의 Data를 관찰하여 바뀔때마다 불린다??
        viewModel.getAll().observe(this, object : Observer<List<Todo>?> {
            override fun onChanged(t: List<Todo>?) {

                result_text.text = t.toString()

            }
        })

        layout에 DataBinding을 하였기 떄문에 MainViewModel의 LivewData가 변경될 떄마다
        자동적으로 layout의 binding 객체를 통해 Data 전송?

LiveData를 사용하게 되면 이처럼 observe()를 통하여 Data가 변경될 때마다 불리우는 코드를 작성해야하지만

이미 DataBinding을 사용했기때문에 주석처리했다.

DataBinding을 사용하지 않는 LiveData를 사용할 때는 이처럼 observe()에서 UI를 변경해주어야한다.

 

 /*
        add_button.setOnClickListener(object : View.OnClickListener {
            override fun onClick(v: View?) {
                //비동기 처리
                //Dispatchers.IO -> Worker Thread (백그라운드 )
                lifecycleScope.launch(Dispatchers.IO) {
                    viewModel.insert(Todo(todo_edit.text.toString()))
                }
                //이 코드 자체가 background에서 실행된다( 비동기 )
            }
        })

*/

버튼을 클릭시 발생하는 Event 코드인데 Coroutine을 사용하여 BackGround에서 비동기로 처리했다.

이 또한 DataBinding을 사용하였기 때문에 주석처리했다.

이를 사용하는 건 layout 내에서 사용.

 

 

 


Layout

<data>
    <variable
        name="viewModel"
        type="com.example.practice_nexterz.MainViewModel"/>
        <!-- Int String 같은것도 됨 -->

</data>

Layout내에서 DataBinding을 사용하여 쓰고싶은 변수를 선언

 

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

위의 onclickListener()를 주석처리한 이유

클릭 시 -> viewModel의 insert()를 호출하는데 매개변수로는 viewModel.newTodo를 사용

 

 

<EditText
            ...
            android:text="@={viewModel.newTodo}"
           ...

        </EditText>

양방향 DataBinding이다.

단방향으로써는 ViewModel의 Data를 View가 사용하는 것이고

양방향은 View에서 어떠한 Resource가 변경될 때마다 VIewModel의 Data에 갱신한다.

 

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

단방향 DataBinding의 한 예이다.

ViewModel의 Data를 가져와 text를 변경한다.

 

 


아직까지 제대로 이해할 수 없는 디자인 패턴이다. 

간략히 이해한 바로는 View는 layout과 Activity등이 존재하고 setContentView()가 아닌 DataBinding을 통해 ViewModel과 Layout을 연결한다.

LiveData를 사용하여 실시간으로 Data의 변경을 체크할 수 있다.

 

View에서는 UI적인 코드들만 작성

ViewModel에서 Data와 관련된 것들을 작성(LOGIC)

View는 직접 ViewModel에 Data를 요구하기보다는 LiveData와 DataBinding을 통해 변경을 체크하여 Resource를 변경한다.

 

좀더 연구를 해봐야겠지만 이 예제를 통해 막막한 내 머리를 조금은 뚫어준 것 같다.

 

예제를 자세히 따라하고 싶다면

https://www.youtube.com/watch?v=pG6OkJ3rSjg&list=PLxTmPHxRH3VXHOBnaGQcbSGslbAjr8obc&index=1

에서 따라하는 게 좋다. 굉장히 잘 설명하는 강의이다.

 

+ Recent posts