와챠의 우당탕탕 코딩 일기장

[Android/Kotlin] FragmentContainerView 연습 Code with Joyce 본문

코딩 일기장/Android(Kotlin)

[Android/Kotlin] FragmentContainerView 연습 Code with Joyce

minWachya 2022. 3. 5. 19:39
반응형

유튜브에서 Kotlin 강좌(Code with Joyce)를 보고 있는데 마지막 강의에서 Navigation에 대한 내용을 다루시길래 재밌어 보여서 나도 같이 만들어보고자 한다. 

 

Navigation 궁금했음!! 더 잘 다르고 싶었음!

 

https://developer.android.com/guide/navigation/navigation-getting-started?hl=ko 

 

탐색 구성요소 시작하기  |  Android 개발자  |  Android Developers

탐색 구성요소 시작하기 이 주제는 탐색 구성요소를 설정하고 사용하는 방법을 설명합니다. 탐색 구성요소의 대략적인 개요는 탐색 개요를 참고하세요. 환경 설정참고: 탐색 구성요소는 Android

developer.android.com


 

1. dependencies에 아래 내용 추가

dependencies {
	...
    // Kotlin
    def nav_version = "2.4.1"
    implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
    implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
    ...
}

 

2. res>navigation 폴더 추가

type도 navigation이다.

 

 

3. navigation 폴더 우클릭>new>Navigation Resourse File>nav_graph.xml 생성

4. activity_main.xml(프레그먼트들 담을 액티비티의 xml)에 아래 코드 추가

위의 공식 문서에서 FragmentContainerView 부분을 복붙해주면 된다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"

        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

5. Fragment 생성 및 xml 꾸미기

+) 만들고자 하는 앱은 심리 테스트 앱이다. 생성된 프레그먼트를 소개하자면...

MainFragment: 심리 테스트 시작 화면

QuestionFragment: 질문 화면

SelectionFragment: 내 답변을 선택하는 화면

ResultFragment: 결과 화면

모든 화면 및 화면에 사용된 이미지 출처는 https://www.youtube.com/watch?v=M1e2tLnzVPo&t=1s 

 

6. nav_graph.xml에 fragment들 추가 및 순서 지정

저 화면 추가 버튼을 눌러서 해당 액티비티 내에서 담을 프레그먼트들을 클릭한다.

시작 부분은 홈 표시가 되도록 하고, 화면 이동 순서대로 화살표를 죽죽 긋는다.

그냥 드래그 하면 된다..., 덜

 

+) 위의 화면애서 화살표 눌러 Navigation 애니메이션 설정 가능!!

<action
    android:id="@+id/action_mainFragment_to_questionFragment"
    app:destination="@id/questionFragment"
    app:enterAnim="@anim/nav_default_pop_enter_anim"
    app:exitAnim="@anim/nav_default_exit_anim"
    app:popEnterAnim="@anim/nav_default_pop_enter_anim"
    app:popExitAnim="@anim/nav_default_pop_exit_anim" />

 

7. Fragment 이동 코드 작성

MainFragment: 심리 테스트 시작 화면

private var _binding: FragmentMainBinding?= null
private val binding get() = _binding!!

class MainFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        _binding = FragmentMainBinding.inflate(inflater, container, false)
        val view = binding.root

        // 다음 프레그먼트로 이동
        binding.btnNext.setOnClickListener {
            findNavController().navigate(R.id.action_mainFragment_to_questionFragment)
        }

        return view
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

QuestionFragment: 질문 화면

코드는 위와 비슷해서 생략... 버튼 눌러서 다름 화면으로 이동하는 거

 

SelectionFragment: 내 답변을 선택하는 화면: 선택한 옵션 정보를 다음 프레그먼트로 전달 + 뒤로가기

private const val TAG = "mmmMainFragment"
private var _binding: FragmentSelectionBinding?= null
private val binding get() = _binding!!

class SelectionFragment : Fragment(), View.OnClickListener {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        _binding = FragmentSelectionBinding.inflate(inflater, container, false)
        val view = binding.root

		// 옵션 클릭 리스너
        binding.option1.setOnClickListener(this)
        binding.option2.setOnClickListener(this)
        binding.option3.setOnClickListener(this)
        binding.option4.setOnClickListener(this)
        // 뒤로가기 리스너
        binding.btnBack.setOnClickListener(this)

        return view
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    // 클릭 리스너
    override fun onClick(view: View?) {
        when(view?.id) {
            // 옵션 클릭: 해당 인덱스 가지고 다음 프레그먼트로 이동
            R.id.option1 -> navigationWithIndex(1)
            R.id.option2 -> navigationWithIndex(2)
            R.id.option3 -> navigationWithIndex(3)
            R.id.option4 -> navigationWithIndex(4)
            // 뒤로가기: pop
            R.id.btn_back -> findNavController().popBackStack()

        }
    }

    // 옵션 인덱스 가지고 다음 프레그먼트로 이동
    private fun navigationWithIndex(index: Int) {
        val bundle = bundleOf("index" to index)
        findNavController().navigate(R.id.action_selectionFragment_to_resultFragment, bundle)
    }

}

 

ResultFragment: 결과 화면

private var _binding: FragmentResultBinding?= null
private val binding get() = _binding!!

class ResultFragment : Fragment() {
    var option = -1

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        _binding = FragmentResultBinding.inflate(inflater, container, false)
        val view = binding.root

        // 선택한 옵션 받아오기
        option = arguments?.getInt("index") ?: -1
        // 결과 보이기
        setResult(option)
        // 홈 프레그먼트로 이동
        binding.btnHome.setOnClickListener { findNavController().navigate(R.id.action_resultFragment_to_mainFragment) }

        return view
    }

    // 선택한 옵션에 맞는 결과 보여주기
    private fun setResult(option: Int) {
        when(option) {
            1 -> {
                binding.resultTitle.text = "1번"
                binding.resultSubTitle.text = "1번을 선택하셨군요."
            }
            2 -> {
                binding.resultTitle.text = "2번"
                binding.resultSubTitle.text = "2번을 선택하셨군요."
            }
            3 -> {
                binding.resultTitle.text = "3번"
                binding.resultSubTitle.text = "3번을 선택하셨군요."
            }
            4 -> {
                binding.resultTitle.text = "4번"
                binding.resultSubTitle.text = "4번을 선택하셨군요."
            }
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

}

끝.. 입니다^^


털석... 이렇게나 쉽고 간단하고 아름답기까지 한 네비게이션 사용 방법이 있었다니...

그동안 난 대체 뭘 했던 것인가..

이거 완성하고 나서 이 기능이 넘 좋고 간편해서..... 약간 허무했음ㅋ

그래서 잠깐 산책도 다녀옴

 

아무튼 너무 쉽고 간단한 방법을 배워서 좋았다~

 

또 배운 것이 있는데

  1. Guideline 사용법(xml의 Design화면 우클릭>Add Helpers>Guideline)
  2. onCreate 안에서 onClickListener를 람다식으로 정의하는 게 아니라 클래스에 View.OnClickListener를 상속받는 방식으로 onClickListener를 처리한 것...
  3. ctrl+d는 해당 라인 복붙...

 

특히 2번 3번 배운 게 좋았다.

2번은... 맨날 고민했음

버튼 클릭 리스너같은 걸 onCreate에 쓰는 방식은 onCreate 내부가 지저분한 것 같다고 생각했음

새로운 방법을 배워서 기쁘다

 

3번도 맨날 마우스로 복붙 디립다 해왓는데 이렇게 편한 방법이..?

요새 단축키의 효율성을 깨닫는 중이다.

단축키 더 공부해야함

 

반응형
Comments