[Android/Flutter 교육] 48일차

MSU·2024년 3월 7일

Android-Flutter

목록 보기
50/85
post-thumbnail

안드로이드 back버튼에 콜백넣기

아이폰의 제스처 기능 처럼 안드로이드 기기는 항상 back버튼이 있다.
네비게이션 아이콘에 뒤로가기 처리를 해도 back버튼은 별도로 존재하기 때문에 back버튼에도 동일하게 처리를 해줘야 한다.


a2 = activity as ContentActivity
a2.onBackPressedDispatcher.addCallback{
	a2.removeFragment(ContentFragmentName.ADD_CONTENT_FRAGMENT)
    
    this.remove()
}

모든 화면에 대해서 back버튼을 눌렀을 때에도 제대로 처리가 되는지 확인해야 한다.

게시판 프로젝트

ReadContentFragment

ReadContentFragment 생성

ContentFragmentName 수정

// Tools.kt


// ContentActivity의 Fragments
enum class ContentFragmentName(var str:String){
    MAIN_FRAGMENT("mainFragment"),
    ADD_CONTENT_FRAGMENT("addContentFragment"),
    READ_CONTENT_FRAGMENT("readContentFragment"),
}

프래그먼트 교체 코드의 when절 수정

// ContentActivity.kt


        // 이름으로 분기한다.
        // Fragment의 객체를 생성하여 변수에 담아준다.
        when(name){
            // 게시글 목록 화면
            ContentFragmentName.MAIN_FRAGMENT -> {
                newFragment = MainFragment()
            }
            // 게시글 작성 화면
            ContentFragmentName.ADD_CONTENT_FRAGMENT -> {
                newFragment = AddContentFragment()
            }
            // 게시글 읽기 화면
            ContentFragmentName.READ_CONTENT_FRAGMENT -> {
                newFragment = ReadContentFragment()
            }
        }

AddContentFragment 툴바 메뉴 아이콘 설정

    // 툴바 셋팅
    fun settingToolbarAddContent(){
        fragmentAddContentBinding.apply {
            toolbarAddContent.apply {
                // 타이틀
                title = "글 작성"
                // Back
                setNavigationIcon(R.drawable.arrow_back_24px)
                setNavigationOnClickListener {
                    contentActivity.removeFragment(ContentFragmentName.ADD_CONTENT_FRAGMENT)
                }
                // 메뉴
                inflateMenu(R.menu.menu_add_content)
                setOnMenuItemClickListener {
                    when(it.itemId){
                        // 카메라
                        R.id.menuItemAddContentCamera -> {}
                        // 앨범
                        R.id.menuItemAddContentAlbum -> {}
                        // 초기화
                        R.id.menuItemAddContentReset -> {}
                        // 완료
                        R.id.menuItemAddContentDone -> {
                            // ReadContentFragment로 이동한다.
                            contentActivity.replaceFragment(ContentFragmentName.READ_CONTENT_FRAGMENT, true, true, null)
                        }
                    }

                    true
                }
            }
        }
    }

fragment_read_content.xml 작성

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:transitionGroup="true"
    tools:context=".fragment.ReadContentFragment">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbarReadContent"
        style="@style/Theme.AndroidProject4BoardApp.Toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:theme="?attr/actionBarTheme" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="10dp">

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="제목"
                app:startIconDrawable="@drawable/subject_24px">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/textFieldReadContentSubject"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:clickable="false"
                    android:cursorVisible="false"
                    android:focusable="false"
                    android:inputType="text"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:hint="게시판 종류"
                app:startIconDrawable="@drawable/warning_24px">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/textFieldReadContentType"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:clickable="false"
                    android:cursorVisible="false"
                    android:focusable="false"
                    android:inputType="text"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:hint="작성자 닉네임"
                app:startIconDrawable="@drawable/person_add_24px">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/textFieldReadContentNickName"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:clickable="false"
                    android:cursorVisible="false"
                    android:focusable="false"
                    android:inputType="text"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:hint="작성날짜"
                app:startIconDrawable="@drawable/calendar_month_24px">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/textFieldReadContentDate"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:clickable="false"
                    android:cursorVisible="false"
                    android:focusable="false"
                    android:inputType="text"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:hint="글 내용"
                app:startIconDrawable="@drawable/description_24px">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/textFieldReadContentText"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:clickable="false"
                    android:cursorVisible="false"
                    android:focusable="false"
                    android:inputType="text|textMultiLine"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <ImageView
                android:id="@+id/imageViewReadContent"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:adjustViewBounds="true"
                app:srcCompat="@drawable/panorama_24px" />
        </LinearLayout>
    </ScrollView>

</LinearLayout>

뷰바인딩 및 툴바 셋팅

// ReadContentFragment.kt


class ReadContentFragment : Fragment() {

    lateinit var fragmentReadContentBinding: FragmentReadContentBinding
    lateinit var contentActivity: ContentActivity

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        fragmentReadContentBinding = FragmentReadContentBinding.inflate(inflater)
        contentActivity = activity as ContentActivity

        settingToolbar()

        return fragmentReadContentBinding.root
    }

    fun settingToolbar(){
        fragmentReadContentBinding.toolbarReadContent.apply {
            // 타이틀
            title = "글 읽기"
            // Back
            setNavigationIcon(R.drawable.arrow_back_24px)
        }
    }
    
}

뒤로가기 처리 메서드 작성

// ReadContentFragment.kt


    // 뒤로가기 처리
    fun backProcess(){
        contentActivity.removeFragment(ContentFragmentName.READ_CONTENT_FRAGMENT)
        contentActivity.removeFragment(ContentFragmentName.ADD_CONTENT_FRAGMENT)
    }

툴바 뒤로가기 리스너에 메서드 호출

// ReadContentFragment.kt

                // Back
                setNavigationIcon(R.drawable.arrow_back_24px)
                setNavigationOnClickListener {
                    backProcess()
                }

Back버튼 눌렀을 때도 메서드 호출

// ReadContentFragment.kt

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        fragmentReadContentBinding = FragmentReadContentBinding.inflate(inflater)
        contentActivity = activity as ContentActivity

        settingToolbar()
        settingBackButton()

        return fragmentReadContentBinding.root
    }


    // Back button 눌렀을 때
    fun settingBackButton(){
        contentActivity.onBackPressedDispatcher.addCallback {
            // 뒤로가기 처리 메서드 호출
            backProcess()
            // 뒤로가기 버튼의 콜백을 제거한다.
            // 제거를 안하면 이후에도 back버튼을 눌렀을때 이 콜백 내용만 처리됨
            remove()
        }
    }

댓글/수정/삭제 메뉴

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/menuItemReadContentReply"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:icon="@drawable/description_24px"
        android:title="댓글"
        app:showAsAction="always" />
    <item
        android:id="@+id/menuItemReadContentModify"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:icon="@drawable/edit_24px"
        android:title="수정"
        app:showAsAction="always" />
    <item
        android:id="@+id/menuItemReadContentDelete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:icon="@drawable/delete_24px"
        android:title="삭제"
        app:showAsAction="always" />
</menu>

툴바에 메뉴 적용

// ReadContentFragment.kt


    // 툴바 설정
    fun settingToolbar(){
        fragmentReadContentBinding.apply {
            toolbarReadContent.apply {
                // 타이틀
                title = "글 읽기"
                // Back
                setNavigationIcon(R.drawable.arrow_back_24px)
                setNavigationOnClickListener {
                    backProcess()
                }
                // 메뉴
                inflateMenu(R.menu.menu_read_content)
                setOnMenuItemClickListener {
                    when (it.itemId) {
                        // 댓글
                        R.id.menuItemReadContentReply -> {

                        }
                        // 수정
                        R.id.menuItemReadContentModify -> {
                        
                        }
                        // 삭제
                        R.id.menuItemReadContentDelete -> {

                        }
                    }
                    true
                }
            }
        }
    }

댓글 ReadContentBottomFragment 생성

클래스 상속을 Fragment에서 BottomSheetDialogFragment로 변경한다.

// ReadContentBottomFragment.kt


class ReadContentBottomFragment : BottomSheetDialogFragment() {

    lateinit var fragmentReadContentBottomBinding: FragmentReadContentBottomBinding
    lateinit var contentActivity: ContentActivity

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        fragmentReadContentBottomBinding = FragmentReadContentBottomBinding.inflate(inflater)
        contentActivity = activity as ContentActivity

        return fragmentReadContentBottomBinding.root
    }

}

댓글 BottomSheet 띄워주는 메서드

// ReadContentFragment.kt


                // 메뉴
                inflateMenu(R.menu.menu_read_content)
                setOnMenuItemClickListener {
                    when (it.itemId) {
                        // 댓글
                        R.id.menuItemReadContentReply -> {
                            // 댓글을 보여줄 BottomSheet를 띄워준다.
                            showReplyBottomSheet()
                        }
                        // 수정
                        R.id.menuItemReadContentModify -> {
                        
                        }
                        // 삭제
                        R.id.menuItemReadContentDelete -> {

                        }
                    }
                    true
                }
                



    // 댓글을 보여줄 BottomSheet를 띄워준다.
    fun showReplyBottomSheet(){
        val readContentBottomFragment = ReadContentBottomFragment()
        readContentBottomFragment.show(contentActivity.supportFragmentManager, "ReplyBottomSheet")
    }

fragment_read_content_bottom.xml 작성

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:padding="20dp"
    tools:context=".fragment.ReadContentBottomFragment">

    <com.google.android.material.textfield.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:hint="댓글"
        app:endIconMode="clear_text"
        app:startIconDrawable="@drawable/warning_24px">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/textFieldAddContentReply"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="text"
            android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    </com.google.android.material.textfield.TextInputLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerViewAddContentReply"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

BottomSheet의 리사이클러뷰에 넣을 레이아웃

row_read_content_reply.xml 작성

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="10dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">

        <TextView
            android:id="@+id/textViewRowReplyText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="TextView"
            android:textAppearance="@style/TextAppearance.AppCompat.Large" />

        <TextView
            android:id="@+id/textViewRowReplyNickName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="TextView" />

        <TextView
            android:id="@+id/textViewRowReplyDate"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="TextView" />
    </LinearLayout>

    <Button
        android:id="@+id/buttonRowReplyDelete"
        style="@style/Widget.Material3.Button.IconButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        app:icon="@drawable/delete_24px" />

</LinearLayout>

리사이클러뷰 어댑터 작성

// ReadContentBottomFragment.kt


    // 댓글 목록을 보여줄 RecyclerView의 어댑터
    inner class BottomRecyclerViewAdapter : RecyclerView.Adapter<BottomRecyclerViewAdapter.BottomViewHolder>(){

        inner class BottomViewHolder(rowReadContentReplyBinding: RowReadContentReplyBinding) : RecyclerView.ViewHolder(rowReadContentReplyBinding.root){
            val rowReadContentReplyBinding : RowReadContentReplyBinding

            init {
                this.rowReadContentReplyBinding = rowReadContentReplyBinding

                rowReadContentReplyBinding.root.layoutParams = ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT
                )
            }
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BottomViewHolder {
            val rowReadContentReplyBinding = RowReadContentReplyBinding.inflate(layoutInflater)
            val bottomViewHolder = BottomViewHolder(rowReadContentReplyBinding)
            return bottomViewHolder
        }

        override fun getItemCount(): Int {
            return 100
        }

        override fun onBindViewHolder(holder: BottomViewHolder, position: Int) {
            holder.rowReadContentReplyBinding.textViewRowReplyText.text = "댓글입니다 $position"
            holder.rowReadContentReplyBinding.textViewRowReplyNickName.text = " 작성자 $position"
            holder.rowReadContentReplyBinding.textViewRowReplyDate.text = "2024-03-07"
        }
    }

리사이클러뷰 어댑터 적용

// ReadContentBottomFragment.kt



    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        fragmentReadContentBottomBinding = FragmentReadContentBottomBinding.inflate(inflater)
        contentActivity = activity as ContentActivity

        settingRecyclerViewAddContentReply()

        return fragmentReadContentBottomBinding.root
    }

    // RecyclerView 구성 메서드
    fun settingRecyclerViewAddContentReply(){
        fragmentReadContentBottomBinding.apply {
            recyclerViewAddContentReply.apply {
                // 어댑터
                adapter = BottomRecyclerViewAdapter()
                // 레이아웃 매니저
                layoutManager = LinearLayoutManager(contentActivity)
                // 데코레이션
                val deco = MaterialDividerItemDecoration(contentActivity, MaterialDividerItemDecoration.VERTICAL)
                addItemDecoration(deco)
            }
        }
    }

BottomSheet 높이 고정

댓글 개수가 적을 경우에는 BottomSheet를 열었을때의 높이가 줄어든다.

높이를 일정하게 고정시키려면 코드로 설정이 필요하다.

ReadContentBottomFragment는 BottomSheetDialogFragment를 상속받고 있기 때문에
dialog가 만들어질때 자동으로 호출되는 onCreateDialog를 오버라이딩 해준다.

// ReadContentBottomFragment.kt


    // 다이얼로그가 만들어질 때 자동으로 호출되는 메서드
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        // 다이얼로그를 받는다.
        val dialog = super.onCreateDialog(savedInstanceState)

        return dialog
    }

단말기 액정의 길이를 구하는 메서드를 작성한다.

// ReadContentBottomFragment.kt


    // 사용자 단말기 액정의 길이를 구해 반환하는 메서드
    fun getWindowHeight() : Int {
        // 화면 크기 정보를 담을 배열 객체
        val displayMetrics = DisplayMetrics()
        // 액정의 가로/세로 길이 정보를 담아준다.
        contentActivity.windowManager.defaultDisplay.getMetrics(displayMetrics)
        // 세로 길이를 반환해준다.
        return displayMetrics.heightPixels
    }

단말기 액정의 길이의 85% 값을 구하는 메서드를 작성한다.

// ReadContentBottomFragment.kt


    // BottomSheet의 높이를 구한다(화면 액정의 85% 크기)
    fun getBottomSheetDialogHeight() : Int{
        return (getWindowHeight() * 0.85).toInt()
    }

BottomSheet의 높이를 설정해주는 메서드를 작성한다.

// ReadContentBottomFragment.kt


    // BottomSheet의 높이를 설정해준다.
    fun setBottomSheetHeight(bottomSheetDialog:BottomSheetDialog){
        // BottomSheet의 기본 뷰 객체를 가져온다
        val bottomSheet = bottomSheetDialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)!!
        // BottomSheet 높이를 설정할 수 있는 객체를 생성한다.
        val behavior = BottomSheetBehavior.from(bottomSheet)
        // 높이를 설정한다.
        val layoutParams = bottomSheet.layoutParams
        layoutParams.height = getBottomSheetDialogHeight()
        bottomSheet.layoutParams = layoutParams
        behavior.state = BottomSheetBehavior.STATE_EXPANDED

    }

다이얼로그가 보일 때 동작하는 리스너에 setBottomSheetHeight 메서드를 호출한다.

// ReadContentBottomFragment.kt


    // 다이얼로그가 만들어질 때 자동으로 호출되는 메서드
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        // 다이얼로그를 받는다.
        val dialog = super.onCreateDialog(savedInstanceState)
        // 다이얼로그가 보일 때 동작하는 리스너
        dialog.setOnShowListener {

            val bottomSheetDialog = it as BottomSheetDialog
            // 높이를 설정한다.
            setBottomSheetHeight(bottomSheetDialog)
        }

        return dialog
    }

댓글 수가 적더라도 BottomSheet가 85%의 높이만큼 올라오는 것을 확인할 수 있다.

ModifyContentFragment

ModifyContentFragment 생성

ContentFragmentName 수정

// Tools.kt


// ContentActivity의 Fragments
enum class ContentFragmentName(var str:String){
    MAIN_FRAGMENT("mainFragment"),
    ADD_CONTENT_FRAGMENT("addContentFragment"),
    READ_CONTENT_FRAGMENT("readContentFragment"),
    MODIFY_CONTENT_FRAGMENT("modifyContentFragment"),
}

프래그먼트 교체 코드의 when절 수정

// ContentActivity.kt


        // 이름으로 분기한다.
        // Fragment의 객체를 생성하여 변수에 담아준다.
        when(name){
            // 게시글 목록 화면
            ContentFragmentName.MAIN_FRAGMENT -> {
                newFragment = MainFragment()
            }
            // 게시글 작성 화면
            ContentFragmentName.ADD_CONTENT_FRAGMENT -> {
                newFragment = AddContentFragment()
            }
            // 게시글 읽기 화면
            ContentFragmentName.READ_CONTENT_FRAGMENT -> {
                newFragment = ReadContentFragment()
            }
            // 게시글 수정 화면
            ContentFragmentName.MODIFY_CONTENT_FRAGMENT -> {
                newFragment = ModifyContentFragment()
            }
        }

ReadContentFragment 메뉴의 when절 수정

                // 메뉴
                inflateMenu(R.menu.menu_read_content)
                setOnMenuItemClickListener {
                    when (it.itemId) {
                        // 댓글
                        R.id.menuItemReadContentReply -> {
                            // 댓글을 보여줄 BottomSheet를 띄워준다.
                            showReplyBottomSheet()
                        }
                        // 수정
                        R.id.menuItemReadContentModify -> {
                            // 수정 화면이 보이게 한다.
                            contentActivity.replaceFragment(ContentFragmentName.MODIFY_CONTENT_FRAGMENT, true, true, null)
                        }
                        // 삭제
                        R.id.menuItemReadContentDelete -> {

                        }
                    }
                    true
                }

글 읽기에서 수정 버튼을 누르면 ModifyContentFragment로 이동한다.

fragment_modify_content.xml 작성

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:transitionGroup="true"
    tools:context=".fragment.ModifyContentFragment">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbarModifyContent"
        style="@style/Theme.AndroidProject4BoardApp.Toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:theme="?attr/actionBarTheme" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="10dp">

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="제목"
                app:endIconMode="clear_text"
                app:startIconDrawable="@drawable/subject_24px">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/textFieldModifyContentSubject"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="text"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.button.MaterialButtonToggleGroup
                android:id="@+id/toggleModifyContentType"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                app:selectionRequired="true"
                app:singleSelection="true">

                <Button
                    android:id="@+id/buttonModifyContentType1"
                    style="@style/Widget.Material3.Button.OutlinedButton"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="자유"
                    android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

                <Button
                    android:id="@+id/buttonModifyContentType2"
                    style="@style/Widget.Material3.Button.OutlinedButton"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="유머"
                    android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

                <Button
                    android:id="@+id/buttonModifyContentType3"
                    style="@style/Widget.Material3.Button.OutlinedButton"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="시사"
                    android:textAppearance="@style/TextAppearance.AppCompat.Medium" />

                <Button
                    android:id="@+id/buttonModifyContentType4"
                    style="@style/Widget.Material3.Button.OutlinedButton"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="스포츠"
                    android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
            </com.google.android.material.button.MaterialButtonToggleGroup>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:hint="글 내용"
                app:endIconMode="clear_text"
                app:startIconDrawable="@drawable/description_24px">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/textFieldModifyContentText"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="text|textMultiLine"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <Button
                android:id="@+id/buttonModifyContentImageDelete"
                style="@style/Widget.Material3.Button.OutlinedButton"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:text="이미지 삭제"
                android:textAppearance="@style/TextAppearance.AppCompat.Large" />

            <ImageView
                android:id="@+id/imageViewModifyContent"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:adjustViewBounds="true"
                app:srcCompat="@drawable/panorama_24px" />
        </LinearLayout>
    </ScrollView>
</LinearLayout>

뷰바인딩 및 툴바 셋팅

// ModifyContentFragment.kt

class ModifyContentFragment : Fragment() {

    lateinit var fragmentModifyContentBinding: FragmentModifyContentBinding
    lateinit var contentActivity: ContentActivity

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        fragmentModifyContentBinding = FragmentModifyContentBinding.inflate(inflater)
        contentActivity = activity as ContentActivity
        
        settingToolbarModifyContent()

        return fragmentModifyContentBinding.root
    }
    
    
    // 툴바 설정
    fun settingToolbarModifyContent(){
        fragmentModifyContentBinding.apply {
            toolbarModifyContent.apply {
                // 타이틀
                title = "글 수정"
                // Back
                setNavigationIcon(R.drawable.arrow_back_24px)
                setNavigationOnClickListener {
                    contentActivity.removeFragment(ContentFragmentName.MODIFY_CONTENT_FRAGMENT)
                }
            }
        }
    }
    
}

툴바 메뉴 설정

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/menuItemModifyContentCamera"
        android:icon="@drawable/photo_camera_24px"
        android:title="카메라"
        app:showAsAction="always" />
    <item
        android:id="@+id/menuItemModifyContentAlbum"
        android:icon="@drawable/photo_album_24px"
        android:title="앨범"
        app:showAsAction="always" />
    <item
        android:id="@+id/menuItemModifyContentReset"
        android:icon="@drawable/clear_all_24px"
        android:title="초기화"
        app:showAsAction="always" />
    <item
        android:id="@+id/menuItemModifyContentDone"
        android:icon="@drawable/done_24px"
        android:title="완료"
        app:showAsAction="always" />
</menu>

    // 툴바 설정
    fun settingToolbarModifyContent(){
        fragmentModifyContentBinding.apply {
            toolbarModifyContent.apply {
                // 타이틀
                title = "글 수정"
                // Back
                setNavigationIcon(R.drawable.arrow_back_24px)
                setNavigationOnClickListener {
                    contentActivity.removeFragment(ContentFragmentName.MODIFY_CONTENT_FRAGMENT)
                }

                // 메뉴
                inflateMenu(R.menu.menu_modify_content)
            }
        }
    }

ModifyUserFragment

ModifyUserFragment 생성

ContentFragmentName 수정

// Tools.kt


// ContentActivity의 Fragments
enum class ContentFragmentName(var str:String){
    MAIN_FRAGMENT("mainFragment"),
    ADD_CONTENT_FRAGMENT("addContentFragment"),
    READ_CONTENT_FRAGMENT("readContentFragment"),
    MODIFY_CONTENT_FRAGMENT("modifyContentFragment"),
    MODIFY_USER_FRAGMENT("modifyUserFragment"),
}

프래그먼트 교체 코드의 when절 수정

// ContentActivity.kt


        // 이름으로 분기한다.
        // Fragment의 객체를 생성하여 변수에 담아준다.
        when(name){
            // 게시글 목록 화면
            ContentFragmentName.MAIN_FRAGMENT -> {
                newFragment = MainFragment()
            }
            // 게시글 작성 화면
            ContentFragmentName.ADD_CONTENT_FRAGMENT -> {
                newFragment = AddContentFragment()
            }
            // 게시글 읽기 화면
            ContentFragmentName.READ_CONTENT_FRAGMENT -> {
                newFragment = ReadContentFragment()
            }
            // 게시글 수정 화면
            ContentFragmentName.MODIFY_CONTENT_FRAGMENT -> {
                newFragment = ModifyContentFragment()
            }
            // 사용자 정보 수정 화면
            ContentFragmentName.MODIFY_USER_FRAGMENT -> {
                newFragment = ModifyUserFragment()
            }
        }

네비게이션 뷰의 메뉴 아이템 클릭 리스너 설정

// ContentActivity.kt





    // 네비게이션 뷰 설정
    fun settingNavigationView(){
        activityContentBinding.apply {
            navigationViewContent.apply {
                // 헤더로 보여줄 view를 생성한다.
                val headerContentDrawerBinding = HeaderContentDrawerBinding.inflate(layoutInflater)
                // 헤더로 보여줄 View를 설정한다.
                addHeaderView(headerContentDrawerBinding.root)
                
                // 사용자 닉네임을 설정한다.
                headerContentDrawerBinding.headerContentDrawerNickName.text = "홍길동님"

                // 메뉴를 눌렀을 때 동작하는 리스너
                setNavigationItemSelectedListener {
                    // 딜레이를 조금 준다.
                    SystemClock.sleep(200)
                    // 메뉴의 id로 분기한다.
                    when(it.itemId){
                        // 전체 게시판
                        R.id.menuItemContentNavigationAll -> {
                            // NavigationView를 닫아준다.
                            drawerLayoutContent.close()
                        }
                        // 자유 게시판
                        R.id.menuItemContentNavigation1 -> {
                            // NavigationView를 닫아준다.
                            drawerLayoutContent.close()
                        }
                        // 유머 게시판
                        R.id.menuItemContentNavigation2 -> {
                            // NavigationView를 닫아준다.
                            drawerLayoutContent.close()
                        }
                        // 시사 게시판
                        R.id.menuItemContentNavigation3 -> {
                            // NavigationView를 닫아준다.
                            drawerLayoutContent.close()
                        }
                        // 스포츠 게시판
                        R.id.menuItemContentNavigation4 -> {
                            // NavigationView를 닫아준다.
                            drawerLayoutContent.close()
                        }
                        
                        // 사용자 정보 수정
                        R.id.menuItemContentNavigationModifyUserInfo -> {
                            replaceFragment(ContentFragmentName.MODIFY_USER_FRAGMENT, false, false, null)
                            // NavigationView를 닫아준다.
                            drawerLayoutContent.close()
                        }
                        
                        // 로그아웃
                        R.id.menuItemContentNavigationLogout -> {
                        
                        }
                        // 회원탈퇴
                        R.id.menuItemContentNavigationSignOut -> {

                        }
                        
                    }
                    true
                }
            }
        }
    }

fragment_modify_user.xml 작성

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:transitionGroup="true"
    tools:context=".fragment.ModifyUserFragment">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbarModifyUser"
        style="@style/Theme.AndroidProject4BoardApp.Toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:theme="?attr/actionBarTheme" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="10dp">

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="닉네임"
                app:endIconMode="clear_text"
                app:startIconDrawable="@drawable/person_add_24px">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/textFieldModifyUserInfoNickName"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="text"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:hint="나이"
                app:endIconMode="clear_text"
                app:startIconDrawable="@drawable/face_24px">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/textFieldModifyUserInfoAge"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="number"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:hint="비밀번호"
                app:endIconMode="password_toggle"
                app:startIconDrawable="@drawable/key_24px">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/textFieldModifyUserInfoPw1"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="text|textPassword"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:hint="비밀번호 확인"
                app:endIconMode="password_toggle"
                app:startIconDrawable="@drawable/key_24px">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/textFieldModifyUserInfoPw2"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="text|textPassword"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />
            </com.google.android.material.textfield.TextInputLayout>


            <com.google.android.material.button.MaterialButtonToggleGroup
                android:id="@+id/toggleModifyUserInfoGender"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                app:selectionRequired="true"
                app:singleSelection="true">

                <com.google.android.material.button.MaterialButton
                    android:id="@+id/buttonModifyUserInfoMale"
                    style="@style/Widget.Material3.Button.OutlinedButton"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="남자"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />

                <com.google.android.material.button.MaterialButton
                    android:id="@+id/buttonModifyUserInfoFemale"
                    style="@style/Widget.Material3.Button.OutlinedButton"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:text="여자"
                    android:textAppearance="@style/TextAppearance.AppCompat.Large" />

            </com.google.android.material.button.MaterialButtonToggleGroup>

            <com.google.android.material.checkbox.MaterialCheckBox
                android:id="@+id/checkBoxModifyUserInfoAll"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:text="취미"
                android:textAppearance="@style/TextAppearance.AppCompat.Large" />

            <LinearLayout
                android:id="@+id/checkBoxGroupModifyUserInfo1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:layout_marginTop="10dp"
                android:orientation="horizontal">

                <com.google.android.material.checkbox.MaterialCheckBox
                    android:id="@+id/checkBoxModifyUserInfoHobby1"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="운동" />

                <com.google.android.material.checkbox.MaterialCheckBox
                    android:id="@+id/checkBoxModifyUserInfoHobby2"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="독서" />

                <com.google.android.material.checkbox.MaterialCheckBox
                    android:id="@+id/checkBoxModifyUserInfoHobby3"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="영화감상" />
            </LinearLayout>

            <LinearLayout
                android:id="@+id/checkBoxGroupModifyUserInfo2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:layout_marginTop="10dp"
                android:orientation="horizontal">

                <com.google.android.material.checkbox.MaterialCheckBox
                    android:id="@+id/checkBoxModifyUserInfoHobby4"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="요리" />

                <com.google.android.material.checkbox.MaterialCheckBox
                    android:id="@+id/checkBoxModifyUserInfoHobby5"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="음악" />

                <com.google.android.material.checkbox.MaterialCheckBox
                    android:id="@+id/checkBoxModifyUserInfoHobby6"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:text="기타" />
            </LinearLayout>

        </LinearLayout>
    </ScrollView>
</LinearLayout>

툴바 메뉴 작성

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/menuItemModifyUserDone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:icon="@drawable/done_24px"
        android:title="완료"
        app:showAsAction="always" />
</menu>

뷰바인딩 및 툴바 셋팅

// ModifyUserFragment.kt


class ModifyUserFragment : Fragment() {

    lateinit var fragmentModifyUserBinding: FragmentModifyUserBinding
    lateinit var contentActivity: ContentActivity

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        fragmentModifyUserBinding = FragmentModifyUserBinding.inflate(inflater)
        contentActivity = activity as ContentActivity

        settingToolbarModifyUser()

        return fragmentModifyUserBinding.root
    }

    fun settingToolbarModifyUser(){
        fragmentModifyUserBinding.toolbarModifyUser.apply {
            // 타이틀
            title = "회원 정보 수정"
            // 메뉴
            inflateMenu(R.menu.menu_modify_user)
        }
    }

}

MVVM

Model - 데이터 관리
View
ViewModel - 화면에 필요한 데이터 관리

안드로이드에서 공식적으로 지원을 한다.

차이점

기존 방식의 구조에서는

Layout폴더의 xml파일에서 설정한 뷰의 id가 액티비티(프래그먼트)파일에서 생성된 바인딩 객체의 프로퍼티 이름이 되고 값으로 해당 뷰의 주소값이 들어간다.

액티비티(프래그먼트) 파일과 레이아웃 xml 파일은 종속적인 관계를 갖게된다.

레이아웃 xml에서 만든 뷰의 id가 바뀌면 액티비티(프래그먼트) 에서 사용하는 해당 뷰의 id도 바꿔줘야 한다.

MVVM 구조에서는

액티비티(프래그먼트)에서 레이아웃xml에서 만든 뷰를 사용하지 않는다.
액티비티(프래그먼트)와 레이아웃 xml과의 종속적 관계를 없애고 독립성을 얻기 위함.
완벽하게 독립관계를 구현하기에는 매우 어려울지라도 가능한 종속성을 최소화 시킨다.

구글에서는 화면에 관련된 부분까지만 MVVM 구조로 작업하는 것을 권장하고 있다.
이벤트처리까지 모두 구현하려 하면 매우 힘들어짐.

ViewModel

게시글에 대한 정보 중 게시글번호, 제목, 내용이 있는 경우
화면에 보여질 정보는 제목, 내용만 필요할 때 ViewModel에서는 제목, 내용만을 관리한다.
사용자가 입력한 제목, 내용이 ViewModel에 담기고 액티비티(프래그먼트)는 ViewModel에 담긴 값을 가져와 사용한다.
액티비티(프래그먼트)는 ViewModel에 넣은 값이 어떤 뷰에 적용이 될 지 모른다.
레이아웃xml에서는 ViewModel에서 가져온 값을 어느 뷰의 속성에 넣을지를 정해주기만 하면 된다.

예제

레이아웃

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:padding="10dp"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/edit1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="number" />

    <EditText
        android:id="@+id/edit2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="number" />

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</LinearLayout>

DataBinding 세팅

MVVM구조에서는 뷰바인딩 대신 데이터바인딩을 한다.

    buildFeatures {
        dataBinding = true
    }

ViewModel 클래스 생성

ViewModel 클래스를 상속받는다.

// TestViewModel.kt


class TestViewModel : ViewModel() {
    // View에 설정할 값을 담을 객체
    // 제네릭은 반드시 View의 속성에 대한 값의 타입으로 맞춰줘야 한다. (예를들어 text속성은 String타입)
    val edit1Data = MutableLiveData<String>()
    val edit2Data = MutableLiveData<String>()
    val textViewData = MutableLiveData<String>()
}

레이아웃 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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="10dp"
        tools:context=".MainActivity" >

        <EditText
            android:id="@+id/edit1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="number" />

        <EditText
            android:id="@+id/edit2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="number" />

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="TextView"
            android:textAppearance="@style/TextAppearance.AppCompat.Large" />
    </LinearLayout>
</layout>

data 태그 추가

<?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="testViewModel"
            type="kr.co.lion.android53_mvvm.TestViewModel" />
    </data>

각 뷰의 속성값 넣어주기

        <EditText
            android:id="@+id/edit1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="number"
            android:text="@={testViewModel.edit1Data}" />

        <EditText
            android:id="@+id/edit2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="number"
            android:text="@={testViewModel.edit2Data}" />

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@={testViewModel.textViewData}"
            android:textAppearance="@style/TextAppearance.AppCompat.Large" />

에디터에서 보면 해당 변수를 사용하고 있는 것으로 처리된다.

데이터바인딩 처리

// MainActivity.kt

class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding
    lateinit var testViewModel: TestViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = DataBindingUtil.inflate(layoutInflater, R.layout.activity_main, null, false)
        setContentView(activityMainBinding.root)
    }
}

ViewModel 객체 생성

메인액티비티에서 생성한 viewmodel 객체를 레이아웃xml에서 사용할 수 있게 셋팅해줘야 한다.

// MainActivity.kt


class MainActivity : AppCompatActivity() {

    lateinit var activityMainBinding: ActivityMainBinding
    lateinit var testViewModel: TestViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        activityMainBinding = DataBindingUtil.inflate(layoutInflater, R.layout.activity_main, null, false)

        testViewModel = TestViewModel()
        activityMainBinding.testViewModel = testViewModel
        // 데이터 바인딩 객체의 수명 설정
        // 액티비티가 소멸되면 바인딩 객체도 소멸된다.
        activityMainBinding.lifecycleOwner = this
        
        testViewModel.textViewData.value = "안녕하세요"

        setContentView(activityMainBinding.root)
    }
}

버튼을 누르면 동작하는 메서드

ViewModel 클래스에 메서드를 작성한다.

// TestViewModel.kt


class TestViewModel : ViewModel() {
    // View에 설정할 값을 담을 객체
    // 제네릭은 반드시 View의 속성에 대한 값의 타입으로 맞춰줘야 한다. (예를들어 text속성은 String타입)
    val edit1Data = MutableLiveData<String>()
    val edit2Data = MutableLiveData<String>()
    val textViewData = MutableLiveData<String>()

    // 버튼을 누르면 동작하는 메서드
    fun buttonClick(view: View){
        // 입력한 값을 가져온다.
        val data1 = edit1Data.value?.toInt()!!
        val data2 = edit2Data.value?.toInt()!!

        val r1 = data1 + data2

        // 출력한다
        textViewData.value = r1.toString()
    }
}

레이아웃xml에 적용

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button"
            android:onClickListener="@{ (view) -> testViewModel.buttonClick(view) }" />

뷰의 초기값 셋팅

// TestViewModel.kt


class TestViewModel : ViewModel() {
    // View에 설정할 값을 담을 객체
    // 제네릭은 반드시 View의 속성에 대한 값의 타입으로 맞춰줘야 한다. (예를들어 text속성은 String타입)
    val edit1Data = MutableLiveData<String>()
    val edit2Data = MutableLiveData<String>()
    val textViewData = MutableLiveData<String>("test")
}

profile
안드로이드공부

0개의 댓글