본문 바로가기

TIL

[TIL] Kotlin Navigation 라이브러리를 통해 Fragment 전환

-1- Navigation Library

  • https://developer.android.com/kotlin/ktx?hl=ko
  • https://developer.android.com/jetpack/androidx/releases/navigation?hl=ko
  • build.gradle에 적용하는 Navigation Library에는 자체적인 ktx 버전이 있어서 dependencies에 추가하기전에 버전확인을 하면 좋을 것 같다. 현재 2.7.2 버전이 최근에 릴리즈된 버전이지만, API 34 레벨이상을 요구하므로 버전을 낮춰서 사용할 것이다. 추후에 target API 34이상의 앱을 만들게 될 경우에 추가할 예정이다.

 

 

Navigation Library

  • Navigation Library란??
    • Android 앱의 화면 전환을 관리하기 위한 Android Jetpack 라이브러리이다.
  • Navigation Library의 장점
    • 백 스택 관리
      • Navigation 라이브러리는 백 스택을 자동으로 관리한다.
    • 단일 Activity 아키텍쳐 지원
      • 단일 Activity와 다수의 Fragment 연결이 가능하므로 앱 전반적인 구조가 단순화 되고 Fragment간의 통합이 쉽다.
    • Animation 적용에 용이
      • Navigation Library는 res/anim에서 생성한 animation을 res/navigation 의 navigation graph에서 적용할 수 있다.
    • 디자인 그래프
      • Navigation graph를 사용하여 앱 내에서 Fragment 전환 흐름을 시각화하여 관리할 수 있다
  • supportFragmentManager.replace()를 통한 Fragment 전환과의 차이
    • supportFragmentManager.replace().commit 을 통한 Fragment 전환은 Activity에서 Fragment 전환이 이루어짐으로 Fragment의 갯수가 많아지면 단일 Activity 아키텍쳐에서 Activity의 복잡성이 증가될 수 있다.
    • Navigation Library는 build.gradle dependencies를 추가해야 하므로 앱 크기가 약간 증가한다.
    • 매우 중요 구글이 Navigation Library 이용을 권장한다

 

 

 

 

 

build.gradle

기본적으로 사용될 Fragment는 이미 생성된 것으로 간주한다 ( Fragment build.gradle등은 설명 X )

dependencies {


    implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
    implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
    ...
    
}

 

res/menu 폴더안에 menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/bottom_btn_search"
        android:title="@string/search"
        android:icon="@drawable/ic_search_24"/>
    <item
        android:id="@+id/bottom_btn_favorite"
        android:title="@string/favorite"
        android:icon="@drawable/ic_favorite_24"/>
    <item
        android:id="@+id/bottom_btn_settings"
        android:title="@string/settings"
        android:icon="@drawable/ic_settings_24"/>
</menu>

 

res/navigation 폴더안에 navigation_graph.xml 를 생성해준다

  • Navigation Graph의 Design 탭에서 Fragment를 추가해준다.
  • home 모양의 아이콘으로 초기 화면에 띄워질 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/imagesearch_nav_graph"
    app:startDestination="@id/bottom_btn_search">

    <fragment
        android:id="@+id/bottom_btn_search"
        android:name="com.example.imagesearchapp.ui.view.SearchFragment"
        android:label="@string/search"
        tools:layout="@layout/fragment_search" />
    <fragment
        android:id="@+id/bottom_btn_favorite"
        android:name="com.example.imagesearchapp.ui.view.FavoriteFragment"
        android:label="@string/favorite"
        tools:layout="@layout/fragment_favorite" />
    <fragment
        android:id="@+id/bottom_btn_settings"
        android:name="com.example.imagesearchapp.ui.view.SettingsFragment"
        android:label="@string/settings"
        tools:layout="@layout/fragment_settings" />
</navigation>
  • 이때, Navigation.xml에 id는 res/menu의 menu.xml에서 지정해준 id와 일치시켜주어야 한다.

 

activity_main.xml

<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=".ui.view.MainActivity">

    <FrameLayout
        android:id="@+id/main_frameLayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="80dp"
        app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/imagesearch_nav_host_fragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:name="androidx.navigation.fragment.NavHostFragment"
            app:defaultNavHost="true"
            app:navGraph="@navigation/imagesearch_nav_graph"
            />

    </FrameLayout>

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottomNavigationView"
        android:layout_width="0dp"
        android:layout_height="56dp"
        app:labelVisibilityMode="unlabeled"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/main_frameLayout"
        app:menu="@menu/menu" />


</androidx.constraintlayout.widget.ConstraintLayout>
  • Fragment를 띄울 UI Layout안에 FragmentContainerView를 만들어주고 res/navigation에서 생성해둔 xml을 navGraph로 설정해준다.

 

 

MainActivity.kt

  • supporFragmentManager.replace()로 전환하는 코드가 더 직관적이지만 코드의 길이가 차이가 많다
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        setNavigation()
    }
    private fun setNavigation() {
        val host = supportFragmentManager
            .findFragmentById(R.id.imagesearch_nav_host_fragment) as NavHostFragment
        navController = host.navController
        binding.bottomNavigationView.setupWithNavController(navController)
    }
//    private fun setupBottomNavigationView() {
//        binding.bottomNavigationView.setOnItemSelectedListener {
//            when (it.itemId) {
//                R.id.bottom_btn_search -> {
//                    supportFragmentManager.beginTransaction()
//                        .replace(R.id.main_frameLayout, SearchFragment())
//                        .commit()
//                    true
//                }
//                R.id.bottom_btn_favorite -> {
//                    supportFragmentManager.beginTransaction()
//                        .replace(R.id.main_frameLayout, FavoriteFragment())
//                        .commit()
//                    true
//                }
//                R.id.bottom_btn_settings -> {
//                    supportFragmentManager.beginTransaction()
//                        .replace(R.id.main_frameLayout, SettingsFragment())
//                        .commit()
//                    true
//                }
//                else -> false
//
//            }
//        }
//    }
}

 

 

 

 

 


 

[오늘 복습한 내용]

1. API를 가져올 때 Base Url 과 Header, Parameter를 항상 신경쓰자

  • 어제는 Parameter를 sidoName을 sideName으로 써서 고생했는데 오늘은 url로 고생했다

 


[오류,에러 등등]

1. BaseUrl은 /로 끝나야 하고, @Get Annotation과 중복되면 안된다.

  • baseUrl은 전체 url에서 getMethod에 해당하는 부분을 중복시키면 안된다.

@Get에서 v2/search/image를 해놓고

BASE_URL 에서 v2/search/image를 또 넣어버렸다.


[느낀 점]

1. API를 가져올 때 Base Url 과 Header, Parameter를 항상 신경쓰자

 

2. 조금 더 부지런히 열심히 하자 ++ 선택과 집중

 

3. 코루틴, ViewModel Factory 등 배우는 건 많은데... 어떤 것 부터 이해하고 넘어가야 할 지 모르겠다

 

 


[Reference]

// navigation

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

// api

https://developers.kakao.com/docs/latest/ko/daum-search/dev-guide#search-image