[팀 프로젝트] Yu Market 두번째 코드 리뷰 3편 (6)

쓰리원·2022년 3월 12일
0

TeamProject_Yu Market

목록 보기
5/5
post-thumbnail

1. 코드 분석하기

Navigation component를 이용하여 BottonNavigationView에서 사용할 Navigation graph입니다. home.xml, order_list.xml, like.xml, map.xml, my_info.xml을 include 합니다.

(bottom_navigation_menu.xml)

BottomNavigationView의 menu item들 입니다. Navigation graph와 android:id를 일치시켜 주어야합니다.

위 그림과 같이 BottonNavigationView에서 사용하는 Navigation graph의 xml들을 확인할 수 있습니다. (home.xml, order_list.xml, like.xml, map.xml, my_info.xml)

현재 home.xml을 제외한 graph에 단일 Fragment만 존재해서 home.xml의 코드만 보겠습니다.

(home.xml)

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/home"
    app:startDestination="@id/homeMainFragment">

    <fragment
        android:id="@+id/homeMainFragment"
        android:name="com.example.YUmarket.screen.home.homemain.HomeMainFragment"
        android:label="HomeMainFragment" >
        <action
            android:id="@+id/action_homeMainFragment_to_homeFragment"
            app:destination="@id/homeFragment" />
    </fragment>
    <fragment
        android:id="@+id/homeFragment"
        android:name="com.example.YUmarket.screen.home.HomeFragment"
        android:label="HomeFragment" >
        <argument
            android:name="goToTab"
            app:argType="com.example.YUmarket.model.homelist.category.HomeListCategory"
            android:defaultValue="TOWN_MARKET" />
    </fragment>
</navigation>

home.xml 전체 코드입니다. 차근히 나눠서 알아보겠습니다.

<fragment
    android:id="@+id/homeMainFragment"
    android:name="com.example.YUmarket.screen.home.homemain.HomeMainFragment"
    android:label="HomeMainFragment" >
    <action
        android:id="@+id/action_homeMainFragment_to_homeFragment"
        app:destination="@id/homeFragment" />
</fragment>

위와 같이 home.xml에는 homeMainFragment와 homeFragment가 있습니다. homeMainFragment에서 homeFragment로 action을 통해서 이동을 정의 할 수 있습니다.

<fragment
        android:id="@+id/homeFragment"
        android:name="com.example.YUmarket.screen.home.HomeFragment"
        android:label="HomeFragment" >
    <argument
        android:name="goToTab"
        app:argType="com.example.YUmarket.model.homelist.category.HomeListCategory"
        android:defaultValue="TOWN_MARKET" />
</fragment>

HomeFragment에 argument를 정의해 줍니다. HomeMainFragment에서 HomeFragment로 이동시에 표시한 탭의 Data값을 HomeListCategory에서 얻어옵니다. argument로 Data 값에 맞는 상태 탭으로 이동합니다.

(activitiy_main.xml)

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragmentContainer"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@id/bottomNav"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/toolbar"
        app:navGraph="@navigation/navigation_graph" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottomNav"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:labelVisibilityMode="labeled"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/bottom_navigation_menu" />

android:name="androidx.navigation.fragment.NavHostFragment"

android:name은 요소에 어떤 NavHost가 구현될지를 정의해주며 위에서는 Fragment NavHost임을 정의해주었습니다.

그래서 FragmentContainerView에 android:name을 NavHostFragment로 명시를 하여 containerFragment를 설정해주고 app:navGraph에 사용할 Navigation graph를 설정합니다. app:defaultNavHost를 true로 하여 back button의 동작을 intercept하도록 하였습니다. (좀 더 생각 필요)

(MainActivity.kt)

private val navController by lazy {
    val hostContainer = supportFragmentManager
    .findFragmentById(R.id.fragmentContainer) as NavHost

    hostContainer.navController
}
override fun initViews() = with(binding) {
	...

	bottom_nav.setupWithNavController(navController)
    
    ...
}

NavHostFragment에서 NavController를 lazy하게 선언합니다.

Activity의 extension 함수인 Activity.findNavController(viewId: Int)를 사용 못하는 이유는 FragmentContainerView를 이용하여 NavHostFragment를 생성해서 입니다. 대신에 Fragment.findNavController 로 사용이 가능하기도 합니다.

(아래에서 Fragment.findNavController 를 다루겠습니다)

그리고 NavController를 이용하여 BottomNavigationView를 정의하여 알맞은 Fragment가 출력되도록 하였습니다.

When creating the NavHostFragment using FragmentContainerView or if manually adding the NavHostFragment to your activity via a FragmentTransaction, attempting to retrieve the NavController in onCreate() of an Activity via Navigation. findNavController(Activity, @IdRes int) will fail. You should retrieve the NavController directly from the NavHostFragment instead. [1]

Android Developers 공식 페이지에서는 위와 같이 NavHostFragment에서 NavController를 직접(directly) 사용하는 것으로 설명합니다.

private val navControllers by lazy {
    supportFragmentManager
        .findFragmentById(R.id.fragmentContainer)
        ?.findNavController()
}
override fun initViews() = with(binding) {
	...

	bottomNav.setupWithNavController(navControllers!!)
    
    ...
}

하지만 공식 홈페이지의 설명과 다르게 위와 같이 사용할 수도 있습니다.

(HomeMainFragment.kt)

override fun initViews() {

	...
    
	//더보기를 누르면 마켓을 List로 띄워주는 Fragment로 이동
	showMoreTextView.setOnClickListener {
			findNavController().navigate(
        	HomeMainFragmentDirections.actionHomeMainFragmentToHomeFragment())
	}
    
    ...
    
    setCategoryButtonListener()
}

private fun setCategoryButtonListener() = with(binding) {
     val navController = findNavController()
     val buttonList = listOf(
         foodCategoryListButton, martCategoryListButton, serviceCategoryListButton,
         fashionCategoryListButton, accessoryCategoryListButton, etcCategoryListButton
        )

     categories.forEachIndexed { index, homeListCategory ->
            buttonList[index].setOnClickListener {
                navController.navigate(
                    HomeMainFragmentDirections
                        .actionHomeMainFragmentToHomeFragment(homeListCategory)
             )
         }
     }
 }

버튼별로 HomeFragment에서 알맞은 탭을 보여줄 수 있도록 알맞은 argument를 넘겨주고 HomeMainFragment에서 HomeFragment로 이동하도록 OnClickListener를 설정해주었습니다. showMoreTextView의 action에 argument의 기본값이 TOWN_MARKET으로 설정되어 있기 때문에 아무것도 넘겨주지 않도록 했습니다.

private val args by navArgs<HomeFragmentArgs>()

override fun observeData() = with(binding) {
    activityViewModel.locationData.observe(viewLifecycleOwner) {
        when (it) {
            is MainState.Success -> {
                initViewPager()
                //View Pager의 현재 Item을 SafeArgs로 받아온 Tab으로 설정
                viewPager.setCurrentItem(args.goToTab.ordinal, false)
            }
        }
    }
}    

by navArgs()로 argument를 lazy하게 가져오도록 하였습니다. Fragment 이동시에 argument로 받은 HomeListCategory의 값을 이용하여 viewPager.currentItem = args.goToTab.ordinal로 알맞은 카테고리의 화면을 출력합니다.

(추가내용)

  1. viewPager.currentItem = args.goToTab.ordinal

viewPager.currentItem = args.goToTab.ordinal로 ViewPager의 현재 아이템을 설정하였습니다. 이렇게 아이템을 설정하게되면 smoothScroll이 기본값인 true로 설정됩니다.

  1. viewPager.setCurrentItem(args.goToTab.ordinal, false)

ViewPager의 currentItem을 설정할 때 smoothScroll을 false로 설정하여 해결하였습니다.

2. Outro

이로써 우리는 Navigation Component에 대한 기본적인 내용과 더불어 이 개념을 활용하는 여러가지 방법을 실제로 사용해 보았습니다. 다음 내용으로 돌아오겠습니다. 감사합니다.


3. reference

https://developer.android.com/guide/navigation/navigation-getting-started#navigate.

profile
가장 아름다운 정답은 서로의 협업안에 있다.

0개의 댓글