Örnek-4: Navigation Komponenti ile ActionBar ve DrawerLayout Kullanımı

Bu örneğimizde Android'te sıklıkla kullanılan ActionBar ve DrawerLayout'un Navigation komponenti ile nasıl kullanıldığını inceleyeceğiz.

Navigation komponenti içerisinde NavigationUI sınıfını içerir. Bu sınıf, app bar, navigation drawer ve bottom navigation ile navigasyonu yönetmenizi sağlayan statik metotlar içerir. Bu sayede Navigation komponentinden önce yapmak zorunda kaldığımız pek çok şeyi yapmadan, bu bileşenlerin yönetimini navigation komponentine bırakır. Bu örneğimizinde bunu nasıl gerçekleştiğini incelemeye çalışacağız.

Artık dördüncü örneğimize ulaştığımız için örneğimiz de gittikçe büyümeye başladı. Bu yüzden kod üzerindeki değişiklikleri aşağıda belirttiğim Github adresi üzerinden takip edebilirsiniz.

O halde başlayalım..

İlk olarak activity_main.xml dosyamızı güncelleyerek başlıyoruz. DrawerLayout ve AppBar eklemelerimizi aşağıdaki gibi gerçekleştiriyoruz.

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".MainActivity"
    tools:openDrawer="start">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:animateLayoutChanges="true"
            android:theme="@style/AppTheme.AppBarOverlay">
            <androidx.appcompat.widget.Toolbar
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                android:id="@+id/toolbar"/>
        </com.google.android.material.appbar.AppBarLayout>
        <fragment
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/appbar"
            app:navGraph="@navigation/nav_graph" />
    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@android:color/white"

        android:fitsSystemWindows="true"
        android:isScrollContainer="true"
        android:saveEnabled="true"
        android:scrollY="1dp"
        android:scrollbars="none"
        app:elevation="2dp"
        app:headerLayout="@layout/nav_header"
        app:insetForeground="@color/colorPrimaryDark"

        app:itemIconTint="@color/colorPrimaryDark"
        app:menu="@menu/navigation_menu" />

</androidx.drawerlayout.widget.DrawerLayout>

XML üzerinde yaptığımız değişiklikler aslında nav_host_fragment'i saymazsak bu güne kadar Android geliştirme sırasında yaptıklarımızdan farklı değil. Bu yüzden vakit kaybetmeden esas aksiyonu alacağımız MainActivity.kt'ye geçelim.

Biliyorsunuz örneğimiz single activity şekliden fragment'ler ile yönettiğimiz bir yapıda. Dolayısıyla normalde olsa fragmentleri yönetmek için bol bol FragmentTransaction ve StartActivity'ler kullanarak bunları yönetmemiz gerekiyor. Fakat bunları bizim adımıza Navigation komponent nasıl yapıyor dikkat etmeye çalışalım.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    
    drawerLayout = findViewById(R.id.drawer_layout)
    navView = findViewById(R.id.nav_view)
    toolbar = findViewById(R.id.toolbar)
    
    navController = findNavController(R.id.nav_host_fragment)
    appBarConfig = AppBarConfiguration(navController.graph, drawerLayout)
    
    setupActionBar(navController, appBarConfig)
    setupNavigationController(navController)
}

onCreate metodu içerisinde gerekli olan bölümleri findViewById ile aldıktan sonra appBarConfig değişkeni ile tanımladığımız AppBarConfiguration oluşturuyoruz. AppBarConfiguration'u ilke parametresinde bizden navController'ımızın yönettiği graph'ı alıyor. Bu aslında bizim nav_graph.xml olarak bildiğimiz Navigasyon komponentinin Navigation Graph özelliği. Yani şu anda hangi sayfa birbiri ile nasıl bağlı, aralarında hangi action'lar var bu action'lar hangi destination'ları gidiyor hepsini biliyor. İkinci parametre olarak da DrawerLayout'umuzu kendisine veriyoruz. Bunun bir yönüyle anlamı artık DrawerLayout'un yönetiminin konfigürasyonunu ona bırakıyoruz demek. Bu arada bu kullandığımız da dahil olmak üzere AppBarConfiguration'ın 3 farklı constructor'ı bulunuyor. Android Studio üzerinden diğerlerini de inceleyebilirsiniz.

setupActionBar ve setupNavigationController isimli metotlar benim hazırladığım metotları oluşturuyor.

private fun setupNavigationController(navController: NavController) {
    navView.setupWithNavController(navController)
}

setupNavigationController metodunda dikkat ettiyseniz navView isimli bileşen ile bileşene ait setupWithNavController metoduna navController'ımızı atıyoruz. Bunun anlamı şudur; bu işlem NavController ile kullanmak için bir NavigationView ayarlar. Dolayısıyla NavigationView içerisindeki menülerden biri seçildiğinde menü item'ine ait onNavDestinationSelected metodu çağırılacak ve gitmesi gereken destination'a gidecektir. Bunun yönetimini de bizim adımıza Navigation komponent gerçekleştirecektir.

private fun setupActionBar(
        navController: NavController,
        appBarConfig: AppBarConfiguration
    ) {
    setSupportActionBar(toolbar)
    supportActionBar?.let {
        it.setDisplayHomeAsUpEnabled(true)
        it.setHomeAsUpIndicator(R.drawable.ic_menu_24px)
        it.setTitle(R.string.app_name)
    }

    setupActionBarWithNavController(navController, appBarConfig)
}

setActionBar metodunda ise hem bir ActionBar oluşturup çeşitli özelliklerini belirleyip, aynı zaman da setupActionBarWithNavController ile yönetimini yine Navigation komponente devrediyoruz. Örneğin; bu yöntemi çağırarak, destination değiştiğinde ActionBar üzerindeki başlık otomatik olarak güncellenir. Bunu setupActionBarWithNavController yönetir ve bu bilgi NavDestination'a ait getLabel metodundan sağlanır.

Şimdi aşağıdaki gibi nav_graph.xml'deki label'ların ne işe yaradığı şimdi daha net anlaşılıyor değil mi?

<fragment
    android:id="@+id/blankFragment"
    android:name="com.etiya.jpnavigation1.BlankFragment"
    android:label="fragment_blank"
    tools:layout="@layout/fragment_blank">
    <action
        android:id="@+id/action_blankFragment_to_blankFragment2"
        app:destination="@id/navigation" />
    <action
        android:id="@+id/action_blankFragment_to_blankFragment4"
        app:destination="@id/blankFragment4" />
    <action
        android:id="@+id/action_blankFragment_to_optionsMenuFragment"
        app:destination="@id/optionsMenuFragment" />
</fragment>

Bunun yanında ActionBar'daki menü ikonu (hamburger simgesi) ve geri düğmelerinin görüntülenmesini de yine setupActionBarWithNavController yönetir. Hangi durumda menü simgesi, hangi durumda geri butonunun çıkmasını nav_graph.xml içerisindeki action'lara göre yönetilir.

Ancak navigasyon butonunu işlemek için NavController'a ait navigateUp'ı çağırmaktan siz sorumlusunuz. Bu yüzden aşağıdaki gibi onSupportNavigateUp metodunu override edip, yönetimini navController'a bırakmamız gerekiyor.

override fun onSupportNavigateUp(): Boolean {
    return navController.navigateUp(appBarConfig) || super.onSupportNavigateUp()
}

Bu işlemi gerçekleştirmezseniz cihazınızdaki donanımsal geri butonu ile sayfalarınızı yönetebilirsiniz, ancak ActionBar üzerinde bulunan hamburger menü ya da geri butonu tıklansa da herhangi bir işlem gerçekleştirmeyecektir.

Bunun sebebini biraz daha teknik bir şekilde açıklayalım. onSupportNavigateUp, AppCompatActivity'den gelir. NavigationUI'ın geri işlemi, hamburger menü gösterimi ve hatta drawer menüsünü doğru bir şekilde destekleyebilmesi için override edilerek geçersiz kılınması ve yönetiminin NavigationUI'a devredilmesi gerekir. Çünkü AppCompatActivity ve NavigationUI iki bağımsız komponenttir. Ancak toolbar'ınızı her şeyi ile kendiniz yönetmek istiyorsanız override etmeniz gerekmez.

Uygulamanın son halini yukarıdaki Github bağlantısından indirerek kendi denemelerinizi gerçekleştirebilirsiniz.

Last updated