Android

Jetpack Navigation Component 활용하기

그란. 2020. 11. 22. 18:35

 

회원가입시 아래와 같은 화면을 구현해보겠습니다.

 

조건

1) 입력후 하단 버튼을 눌러 다음 단계로 이동

2) 이전에 입력한 필드를 선택하여 해당 단계로 이동 ( 값 초기화 ) 

 

 

분석

1) 위쪽 인디케이터가 있지만 슬라이드 기능은 없음 -> ViewPager로 하지 않아도 됨

2) 입력하게 되면 아래쪽에 쌓이는 구조 -> RecyclerView로 한 경우 뷰타입을 각각 분리해야하고 텍스트도 각각 1:1로 지정해줘야함 ( RadioButton 타입도 섞여있음)

3) 각 항목을 선택하면 해당 단계로 돌아가야 함 -> 각 항목의 클릭 리스너도 각각 1:1로 지정해줘야함

 

 

-> 결국 모든 화면을 Fragment로 나누는것으로 결정 

 

(1) 화면 및 클래스 생성 

 

  • fragment_nickname.xml, fragment_password.xml, fragment_password_confirm.xml, fragment_target.xml
  • NickNameFragment, PasswordFragment, PasswordConfirmFragment, TargetFragment

(2) 데이터 공유는 부모 Activity의 ViewModel을 이용 (sharedViewModel)

 

class NicknameFragment {

	override val viewModel: RegisterViewModel by activityViewModels()

}

 

nav_register

<navigation 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:id="@+id/nav_register"
    app:startDestination="@id/nickname">

    <fragment
        android:id="@+id/nickanme">
        <action
            android:id="@+id/goPassword"
            app:destination="@id/password"
            app:launchSingleTop="true" />
    </fragment>

    <fragment
        android:id="@+id/password">
        <action
            android:id="@+id/goPasswordConfirm"
            app:destination="@id/passwordConfirm"
            app:launchSingleTop="true" />
    </fragment>

    <fragment
        android:id="@+id/passwordConfirm">
        <action
            android:id="@+id/goTarget"
            app:destination="@id/target"
            app:launchSingleTop="true" />
    </fragment>

    <fragment
        android:id="@+id/target">
        <action
            android:id="@+id/target"
            app:launchSingleTop="true" />
    </fragment>

</navigation>

startDestination : 시작 프래그먼트 : nickname

 

action [ goPassword ] : nickname 에서 password 이동 

action [ goPasswordConfirm ] : password 에서 passwordConfirm 이동

action [ goTarget ] : passwordConfirm 에서 target 이동

 

 

2-way Binding 연결 

class RegisterViewModel(){
  private val nickname: MutableLiveData<String> = MutableLiveData()
  private val password: MutableLiveData<String> = MutableLiveData()
  private val passwordConfirm: MutableLiveData<String> = MutableLiveData()
  private val target: MutableLiveData<String> = MutableLiveData()
  
  private val goPassword : MutableLiveData<Unit> = SingleLiveData()
  private val goPasswordConfirm : MutableLiveData<Unit> = SingleLiveData()
  private val goTarget : MutableLiveData<Unit> = SingleLiveData()
  private val goMain : MutableLiveData<Unit> = SingleLiveData()
  
  fun goPassword(){
  	goPassword.value = Unit
  }
  
  fun goPasswordConfirm(){
  	goPasswordConfirm.value = Unit
  }
  
  fun goTarget(){
    goTarget.value = Unit
  }
  
  fun goMain(){
  	goMain.value = Unit
  }
}
<EditText
    android:id="@+id/et_nickname"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="닉네임 입력"
    android:inputType="textEmailAddress|textAutoComplete"
    android:text="@={viewModel.nickname}"/>

 

하단 "다음" 버튼을 누른 경우 이벤트 연결

 <Button
   android:id="@+id/bt_password"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:onClick="@{()->viewModel.goPassword()}"
   android:text="다음" />

 

action ID를 지정하여 이동 

class RegiserActivity {
    goPassword.observe(this, Observer{
        viewDataBinding.flContainer
        .findNavController()
        .navigate(goPassword)
    }
}

 

 

 

뒤로 돌아가기 ( 기본적으로 Navigation Component는 BackStack에 쌓입니다 ) 

 

전역 action 추가 : 어떤 화면에서도 닉네임화면, 비밀번호화면 등으로 이동할 수 있으므로 fragment밖에 지정합니다 

<navigation 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:id="@+id/nav_register"
    app:startDestination="@id/nickname">

    <fragment
        android:id="@+id/nickanme">
        <action
            android:id="@+id/goPassword"
            app:destination="@id/password"
            app:launchSingleTop="true" />
    </fragment>

    <fragment
        android:id="@+id/password">
        <action
            android:id="@+id/goPasswordConfirm"
            app:destination="@id/passwordConfirm"
            app:launchSingleTop="true" />
    </fragment>

    <fragment
        android:id="@+id/passwordConfirm">
        <action
            android:id="@+id/goTarget"
            app:destination="@id/target"
            app:launchSingleTop="true" />
    </fragment>

    <fragment
        android:id="@+id/target">
        <action
            android:id="@+id/target"
            app:launchSingleTop="true" />
    </fragment>
    
    <action
        android:id="@+id/backNickname"
        app:destination="@id/nickname"
        app:popUpTo="@id/nickname"
        app:popUpToInclusive="true" />

    <action
        android:id="@+id/backPassword"
        app:destination="@id/password"
        app:popUpTo="@id/password"
        app:popUpToInclusive="true" />

</navigation>   
   

popUpToInclusive="true" 로 지정하면 되돌아가면서 중간 화면들도 같이 pop 됩니다 (backStack에서 제거) 

 

 

뒤로 돌아가기 위해 해당 TextView화면(패스워드) onClick 추가 

<TextView
    android:id="@+id/tv_password"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@{output.password}"
    android:onClick="@{()->viewModel.backToNicknameDialog()"
/>

 

타겟화면에서 비밀번호로 이동한 경우 팝업창 추가

class RegisterViewModel(){
    private val backNicknameDialog : MutableLiveData<Unit> = SingleLiveData()
    private val backPasswordDialog : MutableLiveData<Unit> = SingleLiveData()


    override fun backToNicknameDialog() {
       backNicknameDialog.value = Unit
    }

    override fun backToPasswordDialog() {  
       backTargetDialog.value = Unit
    }


    override fun backToNickname() {
       backToPassword()
       nickname.value = ""
    }

    override fun backToPassword() {
      password.value = ""
      target.value = ""
    }
}

 

확인을 선택한 경우 닉네임, 비밀번호, 대상 초기화

class RegisterActivity{

  backToNicknameDialog.observe(this,Observer{
      //팝업창 (닉네임 입력창으로 이동하시겠습니까?) 
      (확인) ->	viewModel.backToNickname()
  }
}

 

Activity 화면에서 Navigation 연결 -> 자동으로 처음 NicknameFragment 부터 시작됩니다

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/fl_container"
    android:name="androidx.navigation.fragment.NavHostFragment"
    app:navGraph="@navigation/nav_register"
    app:defaultNavHost="true" />

 

'Android' 카테고리의 다른 글

Map vs FlatMap  (0) 2021.04.01
Data class + DiffUtil 이슈  (0) 2021.03.25
Hilt 사용법 및 Module (Binds vs Provides) 정리 및 후기  (4) 2020.11.15
NaverMap API 적용하기  (3) 2020.11.08
JIRA With Intellij  (0) 2020.10.19