
기존의 viewBinding을 dataBinding으로 수정
buildFeatures {
// viewBinding = true
dataBinding = true
}
Sync now를 눌러 적용을하면 아래와 같이 코드에 에러 표시가 나고

기존의 레이아웃 파일들 수정이 필요하다

xml 레이아웃 파일을 열어서 code탭에서 아래와 같이 기존의 태그 내용들을 <layout>으로 감싸준다.
또한 기존 최상단 태그에 있던 xmlns: 속성 부분 3줄을 새로 추가한 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.drawerlayout.widget.DrawerLayout
android:id="@+id/drawerLayoutContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ContentActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/containerContent"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigationViewContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="@menu/menu_content_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
</layout>
모든 layout xml파일들을 수정한 후 다시 실행하면 정상적으로 실행이 된다.
viewmodel 패키지 생성


viewmodel 패키지 안에 JoinViewModel 클래스 파일을 생성하여 작성한다.
프로퍼티명은 동일하게 맞출 필요는 없으나 편의를 위해 동일하게 맞춰놓는다.
// JoinViewModel.kt
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class JoinViewModel : ViewModel() {
// 아이디
val textFieldJoinUserId = MutableLiveData<String>()
// 비밀번호
val textFieldJoinUserPw = MutableLiveData<String>()
// 비밀번호2
val textFieldJoinUserPw2 = MutableLiveData<String>()
}
fragment_join.xml 수정
뷰모델의 데이터를 정의하고 각각의 ui요소와 뷰모델의 프로퍼티값(MutableLiveData)을 연결해준다.
<?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="joinViewModel"
type="kr.co.lion.androidproject4boardapp.viewmodel.JoinViewModel" />
</data>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/textFieldJoinUserId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:text="@={joinViewModel.textFieldJoinUserId}" />
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/textFieldJoinUserPw"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text|textPassword"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:text="@={joinViewModel.textFieldJoinUserPw}" />
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/textFieldJoinUserPw2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text|textPassword"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:text="@={joinViewModel.textFieldJoinUserPw2}" />
뷰모델과 연결할때 값을 셋팅하는 부분에서 @만 붙이면 단방향연결(값만 가져오기)이고 @=를 붙이면 양방향연결(뷰모델과 동기화)이 된다.
양방향연결인 경우 MutableLiveData가 변경되면 xml에서도 변경이 되고, xml에서 변경되면 MutableLiveData도 변경된다.
모든 ui요소가 양방향연결이 되는게 아니고 지원이 안되는 것들도 있다.
단방향연결이 처리속도가 빠르지만 오류가 나지 않는 선에서 양방향으로 설정해도 문제없다.
처리속도는 인간이 체감하기에 큰 차이가 없음
JoinFragment.kt 수정
// JoinFragment.kt
class JoinFragment : Fragment() {
lateinit var fragmentJoinBinding: FragmentJoinBinding
lateinit var mainActivity: MainActivity
lateinit var joinViewModel: JoinViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
// fragmentJoinBinding = FragmentJoinBinding.inflate(inflater)
// 바인딩 객체를 생성한다. ViewBinding의 기능을 포함한다.
// 첫 번째 : LayoutInflater
// 두 번째 : 화면을 만들 때 사용할 layout폴더의 xml 파일
// 세 번째 : xml을 통해서 만들어진 화면을 누가 관리하게 할 것인가를 지정한다. 여기서는 Fragment를 의미한다.
// 네 번째 : Fragment 상태에 영향을 받을 것인지
fragmentJoinBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_join, container, false)
// ViewModel 객체를 생성한다.
joinViewModel = JoinViewModel()
// 생성한 ViewModel 객체를 layout 파일에 설정해준다.
fragmentJoinBinding.joinViewModel = joinViewModel
// ViewModel의 생명 주기를 Fragment와 일치시킨다. Fragment가 살아있을 때 ViewModel 객체도 살아있게끔 해준다.
fragmentJoinBinding.lifecycleOwner = this
mainActivity = activity as MainActivity
settingToolbar()
settingButtonJoinNext()
settingView()
return fragmentJoinBinding.root
}
여기서 생성한 joinViewModel 객체는 레이아웃 xml에서 설정한 joinViewModel과 동일한 객체이다.
직접 값을 넣어주면 앱 실행 시 넣어준 값을 확인할 수 있다.
joinViewModel.textFieldJoinUserId.value = "아이디"
joinViewModel.textFieldJoinUserPw.value = "비밀번호1"
joinViewModel.textFieldJoinUserPw2.value = "비밀번호2"

툴바도 앞의 과정과 동일하게 바인딩이 필요하다.
// JoinViewModel.kt
class JoinViewModel : ViewModel() {
// 아이디
val textFieldJoinUserId = MutableLiveData<String>()
// 비밀번호
val textFieldJoinUserPw = MutableLiveData<String>()
// 비밀번호2
val textFieldJoinUserPw2 = MutableLiveData<String>()
// 툴바의 타이틀
val toolbarJoinTitle = MutableLiveData<String>()
val toolbarJoinNavigationIcon = MutableLiveData<Int>()
}
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbarJoin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:title="@{joinViewModel.toolbarJoinTitle}"
app:navigationIcon="@{joinViewModel.toolbarJoinNavigationIcon}" />
// JoinFragment.kt
// 툴바 설정
fun settingToolbar(){
// 타이틀에 설정해준다.
joinViewModel.toolbarJoinTitle.value = "회원가입"
joinViewModel.toolbarJoinNavigationIcon.value = R.drawable.arrow_back_24px
fragmentJoinBinding.apply {
toolbarJoin.apply {
// 타이틀
// title = "회원가입"
// Back
// setNavigationIcon(R.drawable.arrow_back_24px)
setNavigationOnClickListener {
// 이전 화면으로 간다.
mainActivity.removeFragment(MainFragmentName.JOIN_FRAGMENT)
}
}
}
}

모든 값을 뷰모델과 연동할 필요는 없고 필요한 부분만 셋팅해주면 된다.(변경되는 부분만)
// Tools.kt
class Tools {
companion object {
// 뷰에 포커스를 주고 키보드를 올린다.
fun showSoftInput(context: Context, view: View){
// 뷰에 포커스를 준다.
view.requestFocus()
thread {
//딜레이를 준다.
SystemClock.sleep(200)
// 키보드 관리 객체를 가져온다.
val inputMethodManager = context.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
// 키보드를 올린다.
inputMethodManager.showSoftInput(view, 0)
}
}
// 키보드를 내려주고 포커스를 제거한다.
fun hideSoftInput(activity:Activity){
// 포커스를 갖고 있는 view가 있다면
if(activity.window.currentFocus != null){
// 키보드 관리 객체를 가져온다.
val inputMethodManager = activity.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
// 키보드를 내려준다.
inputMethodManager.hideSoftInputFromWindow(activity.window.currentFocus?.windowToken,0)
// 포커스를 제거해준다.
activity.window.currentFocus?.clearFocus()
}
}
// 입력 요소가 비어있을 때 보여줄 다이얼로그를 구성하는 메서드
fun showErrorDialog(context:Context, view: View, title:String, message:String){
val materialAlertDialogBuilder = MaterialAlertDialogBuilder(context)
materialAlertDialogBuilder.setTitle(title)
materialAlertDialogBuilder.setMessage(message)
materialAlertDialogBuilder.setPositiveButton("확인"){ dialogInterface: DialogInterface, i: Int ->
showSoftInput(context, view)
}
materialAlertDialogBuilder.show()
}
}
}
// 입력요소 초기설정
fun settingTextField(){
// 입력 요소들을 초기화 한다.
joinViewModel.textFieldJoinUserId.value = ""
joinViewModel.textFieldJoinUserPw.value = ""
joinViewModel.textFieldJoinUserPw2.value = ""
// 첫 번째 입력 요소에 포커스를 준다.
Tools.showSoftInput(mainActivity, fragmentJoinBinding.textFieldJoinUserId)
}

// 다음 버튼
fun settingButtonJoinNext(){
fragmentJoinBinding.apply {
buttonJoinNext.apply {
// 버튼을 눌렀을 때
setOnClickListener {
// 입력을 검사한다.
val chk = checkTextInput()
// 입력이 모두 잘 되어 있다면...
if(chk == true){
// 키보드를 내려준다.
Tools.hideSoftInput(mainActivity)
// AddUserInfoFragment를 보여준다.
mainActivity.replaceFragment(MainFragmentName.ADD_USER_INFO_FRAGMENT, true, true, null)
}
}
}
}
}
// 입력요소 유효성 검사 메서드
fun checkTextInput():Boolean{
// 사용자가 입력한 내용을 가져온다
val userId = joinViewModel.textFieldJoinUserId.value!!
val userPw = joinViewModel.textFieldJoinUserPw.value!!
val userPw2 = joinViewModel.textFieldJoinUserPw2.value!!
// 아이디를 입력하지 않았다면
if(userId.isEmpty()){
Tools.showErrorDialog(mainActivity, fragmentJoinBinding.textFieldJoinUserId, "아이디 입력 오류", "아이디를 입력해주세요")
return false
}
// 비밀번호를 입력하지 않았다면
if(userPw.isEmpty()){
Tools.showErrorDialog(mainActivity, fragmentJoinBinding.textFieldJoinUserPw, "비밀번호 입력 오류", "비밀번호를 입력해주세요")
return false
}
// 비밀번호 확인을 입력하지 않았다면
if(userPw2.isEmpty()){
Tools.showErrorDialog(mainActivity, fragmentJoinBinding.textFieldJoinUserPw2, "비밀번호 입력 오류", "비밀번호를 입력해주세요")
return false
}
// 입력한 비밀번호가 서로 다르다면
if(userPw != userPw2){
joinViewModel.textFieldJoinUserPw.value = ""
joinViewModel.textFieldJoinUserPw2.value = ""
Tools.showErrorDialog(mainActivity, fragmentJoinBinding.textFieldJoinUserPw2, "비밀번호 입력 오류", "비밀번호가 다릅니다")
return false
}
return true
}



아이디 중복 검사 여부를 체크할 변수 생성
// JoinFragment.kt
lateinit var fragmentJoinBinding: FragmentJoinBinding
lateinit var mainActivity: MainActivity
lateinit var joinViewModel: JoinViewModel
// 아이디 중복 확인 검사를 했는지...
// true면 아이디 중복 확인 검사를 완료한 것으로 취급한다.
var checkUserIdExist = false
입력요소 초기설정 메서드에 변수값 초기화 코드 추가
// JoinFragment.kt
// 입력요소 초기설정
fun settingTextField(){
// 입력 요소들을 초기화 한다.
joinViewModel.textFieldJoinUserId.value = ""
joinViewModel.textFieldJoinUserPw.value = ""
joinViewModel.textFieldJoinUserPw2.value = ""
// 첫 번째 입력 요소에 포커스를 준다.
Tools.showSoftInput(mainActivity, fragmentJoinBinding.textFieldJoinUserId)
// 아이디 입력요소의 값을 변경하면 중복확인 여부 변수값을 false로 설정한다.
fragmentJoinBinding.textFieldJoinUserId.addTextChangedListener {
checkUserIdExist = false
}
}
입력요소 유효성 검사 메서드에 아이디 중복확인 여부 코드 추가
// JoinFragment.kt
// 입력요소 유효성 검사 메서드
fun checkTextInput():Boolean{
// 아이디 중복확인을 하지 않았다면..
if(checkUserIdExist == false){
Tools.showErrorDialog(mainActivity, fragmentJoinBinding.textFieldJoinUserId, "아이디 중복 확인 오류", "아이디 중복확인을 해주세요")
return false
}
return true
}
중복확인 버튼 메서드 추가
// JoinFragment.kt
settingToolbar()
settingButtonJoinNext()
settingTextField()
settingButtonJoinCheckId()
// 중복확인 버튼
fun settingButtonJoinCheckId(){
fragmentJoinBinding.apply {
buttonJoinCheckId.apply {
setOnClickListener {
checkUserIdExist = true
}
}
}
}


// Tools.kt
// 남자 또는 여자를 나타내는 값을 정의한다.
enum class Gender(var str:String){
MALE("male"),
FEMALE("female")
}
성별은 버튼의 id값을 받기 때문에 Int형으로 설정한다.
성별을 셋팅하는 메서드를 작성하여 직접 value를 셋팅하지 않고 메서드를 사용하도록 한다.
// AddUserInfoViewModel.kt
class AddUserInfoViewModel : ViewModel() {
// 닉네임
val textFieldAddUserInfoNickName = MutableLiveData<String>()
// 나이
val textFieldAddUserInfoAge = MutableLiveData<String>()
// 성별
val toggleAddUserInfoGender = MutableLiveData<Int>()
// 성별을 셋팅하는 메서드
fun settingGender(gender: Gender){
// 성별로 분기한다.
when(gender){
Gender.MALE -> {
toggleAddUserInfoGender.value = R.id.buttonAddUserInfoMale
}
Gender.FEMALE -> {
toggleAddUserInfoGender.value = R.id.buttonAddUserInfoFemale
}
}
}
}
<data>
<variable
name="addUserInfoViewModel"
type="kr.co.lion.androidproject4boardapp.viewmodel.AddUserInfoViewModel" />
</data>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/textFieldAddUserInfoNickName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:text="@={addUserInfoViewModel.textFieldAddUserInfoNickName}" />
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/textFieldAddUserInfoAge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number|numberDecimal"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:text="@={addUserInfoViewModel.textFieldAddUserInfoAge}" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/toggleAddUserInfoGender"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:selectionRequired="true"
app:singleSelection="true"
app:checkedButton="@={addUserInfoViewModel.toggleAddUserInfoGender}">
toggle버튼의 체크여부는 checkedButton 속성으로 설정한다.
// AddUserInfoFragment.kt
lateinit var addUserInfoViewModel: AddUserInfoViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
// fragmentAddUserInfoBinding = FragmentAddUserInfoBinding.inflate(inflater)
fragmentAddUserInfoBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_add_user_info, container, false)
addUserInfoViewModel = AddUserInfoViewModel()
fragmentAddUserInfoBinding.addUserInfoViewModel = addUserInfoViewModel
fragmentAddUserInfoBinding.lifecycleOwner = this
mainActivity = activity as MainActivity
settingToolbar()
settingButtonAddUserInfoSubmit()
return fragmentAddUserInfoBinding.root
}
// AddUserInfoFragment.kt
// 입력 요소 관련 설정
fun settingInputUI(){
addUserInfoViewModel.textFieldAddUserInfoNickName.value = ""
addUserInfoViewModel.textFieldAddUserInfoAge.value = ""
addUserInfoViewModel.settingGender(Gender.MALE)
// 닉네임에 포커스를 준다.
Tools.showSoftInput(mainActivity, fragmentAddUserInfoBinding.textFieldAddUserInfoNickName)
}
가끔 이런식으로 실행할 때 에러가 나는 경우가 있는데

xml파일에서 잘못 셋팅한 경우가 있다.

"msg":"Cannot find a getter
"msg":"Cannot find a setter
이 경우 해당 UI 요소에 설정한 속성에 getter혹은 setter가 없어서 발생하는 에러이므로 xml에서 = 기호를 지워준다.


이와같이 일부 UI요소는 MutableLiveData를 셋팅한 속성이 단방향만 지원하고 있는 경우이다.
그럼에도 에러가나는 경우는 해당 UI요소의 속성이 MutableLiveData를 셋팅할 수 없는 속성인 경우이다. 그런 경우에는 MutableLiveData 셋팅이 가능한 다른 속성을 찾아주거나 직접 속성을 만들어줘야 한다.
@BindingAdapter 어노테이션 사용을 위해 build.gradle.kts에 kotlin-kapt플러그인 추가
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
}
// AddUserInfoViewModel.kt
class AddUserInfoViewModel : ViewModel() {
companion object {
// ViewModel에 값을 설정하여 화면에 반영하는 작업을 할 때 호출된다.
// 괄호() 안에는 속성의 이름을 넣어준다. 속성의 이름은 자유롭게 해주면 되지만 기존의 속성 이름과 중복되지 않아야 한다.
// 매개변수 : 값이 설정된 View 객체, ViewModel을 통해 설정되는 값
@BindingAdapter("android:checkedButtonId")
@JvmStatic
fun setCheckedButtonId(group:MaterialButtonToggleGroup, buttonId:Int){
group.check(buttonId)
}
}
}
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/toggleAddUserInfoGender"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:selectionRequired="true"
app:singleSelection="true"
android:checkedButtonId="@{addUserInfoViewModel.toggleAddUserInfoGender}">

초기값으로 남자에 체크되어있는 것을 확인할 수 있다.
값을 속성에 넣어주는 것을 순방향이라고 부른다.
반대로 속성의 값이 변경되어 MutableLive데이터로 전달하는 것을 역방향이라고 한다.
순방향만 구현해주면 단방향이 되고, 순방향과 역방향을 모두 구현해주면 양방향
화면 요소가 가진 속성에 새로운 값이 설정되면 ViewModel의 변수에 값이 설정될 때 호출된다.
리스너 역할을 할 속성을 만들어준다.
class AddUserInfoViewModel : ViewModel() {
companion object {
@BindingAdapter("checkedButtonChangeListener")
@JvmStatic
fun checkedButtonChangeListener(group: MaterialButtonToggleGroup, inverseBindingListener: InverseBindingListener){
group.addOnButtonCheckedListener { group, checkedId, isChecked ->
inverseBindingListener.onChange()
}
}
// 역방향 바인딩이 벌어질 때 호출된다.
@InverseBindingAdapter(attribute = "android:checkedButtonId", event = "checkedButtonChangeListener")
@JvmStatic
fun getCheckedButtonId(group: MaterialButtonToggleGroup):Int{
return group.checkedButtonId
}
}
}
xml에서 @를 @=로 수정해서 양방향으로 바꿔준다.
양방향으로 바뀌게 되면 InverseBindingAdapter를 찾게되고 InverseBindingAdapter에 설정된 event로 checkedButtonChangeListener가 설정된다.
사용자 조작에 의해 속성값이 변경되면
checkedButtonChangeListener 리스너가 동작되고 리스너 안에 정의한 메서드 inverseBindingListener.onChange()가 호출된다.
onChange()메서드가 호출되면 해당 속성이 등록되어 있는 InverseBindingAdapter에 등록되어 있는 메서드 getCheckedButtonId를 호출된다.
getCheckedButtonId로 반환받은 id값이 해당 UI요소와 연결된 MutableLiveData에 저장된다.
TextInputEditText의 경우 값을 셋팅하는데 필요한 속성이 text밖에 없는데 String타입의 값만을 받을 수 있다.
따라서 Int형 값을 받을수가 없기에 이런 경우 숫자타입의 값을 받을 수 있는 속성을 직접 만들어주면 된다.
앞서 MaterialToggleGroup과 같은 경우도 MVVM구조에서 사용할수 있는 속성이 없다면 직접 속성을 만들어 사용한다.
개발자가 MutableLiveData에 값을 넣어주면 이 값이 UI요소의 속성에 설정된다(순방향)
속성="@{}"
사용자 조작등에 인해 속성 값이 변경되면 연결되어 있는 MutableLiveData에 값이 저장된다.(역방향)
속성="@={}"
// AddUserInfoViewModel.kt
class AddUserInfoViewModel : ViewModel() {
// 닉네임
val textFieldAddUserInfoNickName = MutableLiveData<String>()
// 나이
val textFieldAddUserInfoAge = MutableLiveData<String>()
// 성별
val toggleAddUserInfoGender = MutableLiveData<Int>()
// 취미들
val checkBoxAddUserInfoHobby1 = MutableLiveData<Boolean>()
val checkBoxAddUserInfoHobby2 = MutableLiveData<Boolean>()
val checkBoxAddUserInfoHobby3 = MutableLiveData<Boolean>()
val checkBoxAddUserInfoHobby4 = MutableLiveData<Boolean>()
val checkBoxAddUserInfoHobby5 = MutableLiveData<Boolean>()
val checkBoxAddUserInfoHobby6 = MutableLiveData<Boolean>()
// 취미 전체
val checkBoxAddUserInfoAllState = MutableLiveData<Int>()
val checkBoxAddUserInfoAll = MutableLiveData<Boolean>()
checkedState 속성은 순방향만 지원되고 역방향이 지원이 안된다(단방향)
checked 속성은 순방향,역방향 모두 지원된다(양방향)
전체 체크박스를 누르면 onCheckBoxAllChanged()메서드가 호출되게 한다.
개별 체크박스를 누르면 onCheckBoxChanged()메서드가 호출되게 한다.
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/checkBoxAddUserInfoAll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="취미"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:checkedState="@{addUserInfoViewModel.checkBoxAddUserInfoAllState}"
android:checked="@={addUserInfoViewModel.checkBoxAddUserInfoAll}"
android:onClickListener="@{ (view) -> addUserInfoViewModel.onCheckBoxAllChanged()}" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/checkBoxAddUserInfoHobby1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="운동"
android:checked="@={addUserInfoViewModel.checkBoxAddUserInfoHobby1}"
android:onClickListener="@{ (view) -> addUserInfoViewModel.onCheckBoxChanged()}" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/checkBoxAddUserInfoHobby2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="독서"
android:checked="@={addUserInfoViewModel.checkBoxAddUserInfoHobby2}"
android:onClickListener="@{ (view) -> addUserInfoViewModel.onCheckBoxChanged()}" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/checkBoxAddUserInfoHobby3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="영화감상"
android:checked="@={addUserInfoViewModel.checkBoxAddUserInfoHobby3}"
android:onClickListener="@{ (view) -> addUserInfoViewModel.onCheckBoxChanged()}" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/checkBoxAddUserInfoHobby4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="요리"
android:checked="@={addUserInfoViewModel.checkBoxAddUserInfoHobby4}"
android:onClickListener="@{ (view) -> addUserInfoViewModel.onCheckBoxChanged()}" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/checkBoxAddUserInfoHobby5"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="음악"
android:checked="@={addUserInfoViewModel.checkBoxAddUserInfoHobby5}"
android:onClickListener="@{ (view) -> addUserInfoViewModel.onCheckBoxChanged()}" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/checkBoxAddUserInfoHobby6"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="기타"
android:checked="@={addUserInfoViewModel.checkBoxAddUserInfoHobby6}"
android:onClickListener="@{ (view) -> addUserInfoViewModel.onCheckBoxChanged()}" />
// AddUserInfoFragment.kt
// 체크박스 관련 설정
fun settingCheckBox(){
// 모든 체크박스를 초기화한다.
addUserInfoViewModel.checkBoxAddUserInfoHobby1.value = false
addUserInfoViewModel.checkBoxAddUserInfoHobby2.value = false
addUserInfoViewModel.checkBoxAddUserInfoHobby3.value = false
addUserInfoViewModel.checkBoxAddUserInfoHobby4.value = false
addUserInfoViewModel.checkBoxAddUserInfoHobby5.value = false
addUserInfoViewModel.checkBoxAddUserInfoHobby6.value = false
}
// AddUserInfoViewModel.kt
// 체크박스 전체 상태를 설정하는 메서드
fun setCheckAll(checked:Boolean){
checkBoxAddUserInfoHobby1.value = checked
checkBoxAddUserInfoHobby2.value = checked
checkBoxAddUserInfoHobby3.value = checked
checkBoxAddUserInfoHobby4.value = checked
checkBoxAddUserInfoHobby5.value = checked
checkBoxAddUserInfoHobby6.value = checked
}
// 전체 취미 체크박스를 누르면
fun onCheckBoxAllChanged(){
// 전체 취미 체크박스의 체크 여부를 모든 체크박스에 설정해준다.
setCheckAll(checkBoxAddUserInfoAll.value!!)
}
// 각 체크박스를 누르면
fun onCheckBoxChanged(){
// 체크되어 있는 체크박스 개수를 담을 변수
var checkedCnt = 0
// 체크되어 있다면 체크되어있는 체크박스의 개수를 1 증가시킨다.
if(checkBoxAddUserInfoHobby1.value == true){
checkedCnt++
}
if(checkBoxAddUserInfoHobby2.value == true){
checkedCnt++
}
if(checkBoxAddUserInfoHobby3.value == true){
checkedCnt++
}
if(checkBoxAddUserInfoHobby4.value == true){
checkedCnt++
}
if(checkBoxAddUserInfoHobby5.value == true){
checkedCnt++
}
if(checkBoxAddUserInfoHobby6.value == true){
checkedCnt++
}
// 체크되어 있는 것이 없다면
if(checkedCnt == 0){
checkBoxAddUserInfoAll.value = false
checkBoxAddUserInfoAllState.value = MaterialCheckBox.STATE_UNCHECKED
}
// 모두 체크되어 있다면
else if(checkedCnt == 6){
checkBoxAddUserInfoAll.value = true
checkBoxAddUserInfoAllState.value = MaterialCheckBox.STATE_CHECKED
}
else{
checkBoxAddUserInfoAllState.value = MaterialCheckBox.STATE_INDETERMINATE
}
}



// AddUserInfoFragment.kt
// 가입 버튼
fun settingButtonAddUserInfoSubmit(){
fragmentAddUserInfoBinding.buttonAddUserInfoSubmit.apply {
// 눌렀을 때
setOnClickListener {
// 유효성 검사를 수행한다.
val chk = checkInputForm()
// 모든 유효성 검사에 통과를 했다면
if(chk == true){
val materialAlertDialogBuilder = MaterialAlertDialogBuilder(mainActivity)
materialAlertDialogBuilder.apply {
setTitle("가입완료")
setMessage("가입이 완료되었습니다\n로그인해주세요")
setPositiveButton("확인"){ dialogInterface: DialogInterface, i: Int ->
mainActivity.removeFragment(MainFragmentName.ADD_USER_INFO_FRAGMENT)
mainActivity.removeFragment(MainFragmentName.JOIN_FRAGMENT)
}
}
materialAlertDialogBuilder.show()
}
}
}
}
// 입력 요소에 대한 유효성 검사
fun checkInputForm():Boolean {
// 입력한 값을 가져온다.
val userNickname = addUserInfoViewModel.textFieldAddUserInfoNickName.value!!
val userAge = addUserInfoViewModel.textFieldAddUserInfoAge.value!!
// 입력하지 ㅇ낳은 것이 있을 경우 경고문을 띄운다.
if(userNickname.isEmpty()){
Tools.showErrorDialog(mainActivity, fragmentAddUserInfoBinding.textFieldAddUserInfoNickName, "닉네임 입력 오류", "닉네임을 입력해주세요")
return false
}
if(userAge.isEmpty()){
Tools.showErrorDialog(mainActivity, fragmentAddUserInfoBinding.textFieldAddUserInfoAge, "나이 입력 오류", "나이를 입력해주세요")
return false
}
return true
}


