Navigation Component: Manuel Navigation Kıyası ve Kullanımı - Part II

Ahmet SIRIM
7 min readJul 12, 2023

Merhaba, bu yazımda size Navigation Component kullanımından, yapılarından bahsedeceğim. Bu konudaki Navigation Component: Manuel Navigation Kıyası ve Kullanımı - Part I başlıklı ilk yazım, manuel navigation yönetiminin temelleri üstüneydi. Bu yazı ise tamamen Navigation Component kütüphanesi odağında olacak.

Navigation Component kütüphanesi, Set up your environment linkindeki gibi kurulur. Bu kurulum, kütüphane erişimi için yeterlidir fakat şart olmasa da önerilen, daha type-safe bir kullanım sunan, Ensure type-safety by using Safe Args başlığında anlatılan Safe Args plugin’ini de kurmanızı öneririm. Eğer gradle.properties içindeki android.useAndroidX=true kısmınız false ise bu plugin kullanılamıyor, gradle’da Sync Now yapınca androidx.navigation.safeargs can only be used with an androidx project. yazan bir hata çıkıyor. Eğer böyle bir durumla karşılaşırsanız sebebi bu yüzden olabilir.

Temel Bileşenler

Navigation Component, manuel navigation yönetimine kıyasla IllegalStateException hatasıyla karşılaşma riskini azaltan yani daha güvenilir bir development deneyimi sunan, navigation yönetimini kolaylaştıran bir Jetpack kütüphanesidir. Kütüphane, temelinde Navigation graph, NavHost ve NavController isimli üç bileşenden oluşur.

Navigation graph

Navigation graph, src\main\res\navigation içine oluşturulan, “nav_graph” gibi bir isim verilmesi önerilen XML uzantılı bir dosyadır. Bu dosya, navigation yönetimimizle ilgili tüm bilgileri içeren ve içermekle kalmayıp bunları geliştiren kişinin rahatça anlayabileceği harita benzeri bir düzenle sunan, görselleştiren bir resource dosyasıdır.

Navigation Component, aslında Single Activity düzeni ile kullanılması için tasarlanmış bir kütüphanedir fakat birden fazla Activity ile Navigation Component kullanılacağı durumlarda her Activity başına bir Navigation graph oluşturulması öneriliyor.

Navigation graph dosyamıza tıklayıp birkaç destination ekleyince alttaki gibi bir şablon oluşuyor.

Navigation Graph

Destination, navigate işlemiyle ulaşılacak veya geride bırakılacak olan ekranları ifade eder. Bir Activity, Fragment, belirli şartları sağlayan custom bir class ve hatta boş bir placeholder destination olarak eklenebilir.

Yukardaki görsel, keyfime göre yaptığım eklemeler sonucu oluştu fakat bu eklemelerin ilki ile diğerleri arasında önemli bir fark var. İlk eklediğim destination olan FirstFragment’ın id değeri, navigation graph’ın root elementi olan navigation tag’i içindeki startDestination attribute’una da otomatik olarak eklendi. Bu attribute, kullanıcının uygulamayı açtığında ulaşacağı ilk ekranı veya bir başka deyişle kullanıcının back butonuna art arda basmaları sonucu uygulamadan ayrılmadan önce varacağı son ekranı belirtmemizi sağlar zaten bu attribute için verilen destination üstüne IDE tarafından eklenen küçük ev görseli de bize bunu kendince ifade ediyor. Eğer navigation elementinde startDestination belirtilmezse başında “Unable to start activity” yazan, ortalarında da “no start destination defined via app:startDestination” yazan bir exception fırlatılıyor.

Destination’ları belirledikten sonra destination sağındaki dairesel kısımdan aralardaki geçişleri belirtmek, Android Studio üzerindeki en eğlenceli bulduğum yanlardan birisi. Bu işlem, xml üzerinde tanımlanan action elementleri ile yapılıyor. Bu işlem sonrasında yukardaki şablon, alttaki hale dönüyor. İşte bu alttaki şablon, “harita benzeri” diye bahsettiğim şablon.

Navigation Graph

Navigation rotasını belirten action elementi içindeki id, klasik olarak elementin id değerini ifade ederken destination ise varış yerini ifade ediyor. Bu action’lar, NavController ile çalıştırılıyor, navigate işlemi yapıyor.

NavHost

Direkt referans sayfasına gidince oradaki açıklamada da gördüğümüz üzere NavHost, kendisini implement edene NavController ile navigation yönetimi yapabilme imkânı tanıyan bir interface’dir.

Bu referans sayfasını incelerken NavHost interface’inin tanımlamasının hemen altındaki NavHostFragment başlığı, direkt isim benzerliğiyle bir göz kırpıyor. NavHostFragment başlığına tıklayıp referansa ulaşınca da görüyoruz ki NavHostFragment, Fragment class’ını extend eden ve NavHost interface’ini implement eden bir class. Açıklamaya bakınca da NavHostFragment’ın navigation yönetimi sağlayan bir yapı olduğunu görüyoruz.

Kısaca NavHost ve NavHostFragment başlıklarını inceleyince anlıyoruz ki kütüphanenin temel üç bileşeninden ikincisi olan NavHost, en az NavHostFragment class’ının bir subtype’ı olması gereken bir class. Bu class’ın Activity layout içinde tanımlanması, aşağıdaki gibi oluyor:

Activity layout içine NavHostFragment ekleme

Bu bağlama, Add a fragment via XML başlığında da bahsedildiği gibi statik bir şekilde bir FragmentContainerView içine NavHostFragment yerleştirme işlemidir zaten navigation işleyişinin dinamik kısmı, kütüphane tarafından bu statik olan yerleştirme üstünden işletiliyor.

Bu yerleştirme için name attribute içine NavHost implement eden class’ımızın ismini gireriz ki eğer bir abstraction yapmıyorsak genelde direkt görseldeki gibi name kısmını doldurup geçeriz.

Navigation graph ile NavHost arasındaki ilişiyi veya bağlamayı, navGraph attribute ile yaparız. Bu attribute ile kısaca “Bu FragmentContainerView, bu Navigation graph’ı kullanacak” deriz.

Geçilen Fragment’lara geri navigate edip edilmemesini, defaultNavHost attribute’u ile belirleriz. Kütüphanenin sağladığı bu attribute sayesinde manuel navigation yönetiminde kendimizin ayarladığı back stack yönetimi, tek bir attribute ile sağlanıyor. Birden fazla FragmentContainerView varsa sadece bir tanesinde bu attribute true olabilir. Örneğin alttaki, bu Temel Bileşenler başlıklı kısmın kaynak kodunda bu true iken geri bastıkça bir önceki Fragment’a geçiliyor fakat bu attribute false yapılırsa direkt uygulamadan çıkılıyor.

NavController

NavController, Navigation graph içinde belirlediğimiz destination’lar ve action’lar ile NavHost Fragment’ımız üstünde navigation yönetimi yapmamıza sağlayan bir class’tır. Her NavHost’un, kendisine karşılık gelen bir NavController’u vardır.

NavController erişimi için Activity’e, Fragment’a ve View’a extension olarak yazılmış; kütüphane tarafından sunulan alttaki function’lar vardır.

  1. Activity.findNavController(viewId: Int)
  2. Fragment.findNavController()
  3. View.findNavController()

Buraya kadar olan Temel Bileşenler başlıklı kısmın kaynak kodlarına şuradan erişebilirsiniz: Basic Navigation Examples

Nested Navigation Graphs & <include> Etiketi

Şimdi önceki ana başlıkta kütüphanenin sağladığı kolaylıklardan bahsederken “harita benzeri bir düzenle sunan, görselleştiren” gibi bir ifadeden bahsettik. Gayet güzel, makul bir özellik fakat 300 ekranı olan bir uygulama içinde bu özelliği kullanmak, sadece küçük küçük 300 adet dikdörtgen görmemize sebep olabilir ki o da render edilirse, bilgisayarınız bu talebinizi karşılayabilirse. İşte bu durum, Nested navigation graphs konusuyla çözülüyor.

Nested bir navigation graph kurmak için navigation elementi içinde navigation elementi oluştururuz veya bunu direkt kolayca editörden alttaki görseldeki gibi yaparız.

Nested Navigation Graphs

Editör üzerinden yapınca IDE direkt içerdeki navigation elementimiz için bir id ve startDestination tanımladı. Bu durumun da biraz fark ettirmesi ile beraber dokümana bakınca da görüyoruz ki nested navigation erişimi, sadece nested navigation’ın startDestination kısmında belirtilen destination ile yapılıyor ve zaten bu action da bir fragment’tan bir başka fragment’a değil bir fragment’tan bir navigation’a olan bir action oluyor. Olur da bir fragment’tan nested graph‘ın startDestination’ı olmayan bir fragment’a direkt geçmek isterseniz “java.lang.IllegalArgumentException” fırlatılıyor.

Yukardaki görsel, navigation graph’ı nested yaptığım anın görselidir. O şablon, action’ları da tamamladıktan sonra alttaki hale geldi.

Nested Navigation Graphs

Nested graph kullanımı ile benzer bir işlevi gören yani navigation graph parçalayan, Reference other navigation graphs with <include> başlığında da anlatılan bir yol daha var. Bu yol tercih edilirse navigation graph, gerçekten parçalanıyor, başka dosyalara ayrılıyor ve bu başka dosyalardaki parçalar da NavHost’umuz ile ilişkili olan navigation graph içine <include> etiketi ile ekleniyor. Bu yolun seçildiği bir örnek projedeki resource dosyası, navigation resourse file linkindeki ve alttaki görsedeki gibidir.

<include> Kullanılmış Bir Navigation Graph

Şimdi yukardaki ve ondan bir önceki görsel, bize ne zaman nested graph kullanacağımız ve ne zaman <include> kullanacağımız hakkında biraz ipucu veriyor. Eğer gruplandırılan destination’lar, grup dışındakilerle ilişkiliyse yani bir başka deyişle kurduğumuz destination grubu, çıkmaz sokak değilse nested navigation graphs ile navigation graph parçalama yolunu aksi durumda ise yani çıkmaz sokak olan bir akış varsa <include> ile navigation graph parçalama yolunu seçmemiz daha iyi olacaktır.

Buraya kadar olan Nested Navigation Graphs & <include> Etiketi başlıklı kısmın kaynak kodlarına şuradan erişebilirsiniz: Nested Navigation Graphs Examples

Global Actions

Şu ana kadar olan bütün action’larımız, belirli bir yerden belirli bir yereydi. İşte eğer action’ın nereden olduğuyla ilgilenmiyorsak, sadece varış yeriyle ilgileniyorsak veya bir başka deyişle birçok destination’da kullanılabilecek olan yani sadece varış yeri belli olan bir action tanımlamak istiyorsak işte o zaman global action, tam da bu talebimiz için geliyor. Global action, alttaki gibi oluşturulur.

Global action oluşturma

Destination’lar arası veri transferi

İki destination arasında veri transferi, Pass data between destinations başlığında da anlatıldığı gibi nav graph içinden veriyi alacak olan fragment elementinin içine eklenen argument yardımıyla yapılır. Bu ekleme, attribute penceresindeki editor yardımıyla daha type-safe bir şekilde yapılabilir. Açılacak olan pencere, aşağıdaki görseldeki gibidir.

“Add Argument” penceresi

Bu arada argument için default value olarak null girmek istiyorsanız Supported argument types başlığında bahsedildiği gibi @null şeklinde ekleme yapabilirsiniz. Hangi verileri taşıyıp hangilerini taşıyamayacağınızın detaylı bilgisine de yine Supported argument types başlığından veya özet olarak alttaki görselden ulaşabilirsiniz.

Parcelable transferi örneği yapılmış, araştırma yaparken görüp beğendiğim, Passing Data with Navigation Component in Android | by Emine İNAN | Dev Genius başlıklı yazıyı da öneririm. Bu konu için güzel bir örnek.

SecondFragment içinden veriyi aldığımız kod, yukarda 19. satırdadır. O satırdaki “SecondFragmentArgs” tipi, yine Safe Args sayesinde generate edilen bir koddur ve bu da yine Safe Args’ın sağladığı type-safety avantajlarından biridir. Buraya kadar olan Destination’lar arası veri transferi başlıklı kısmın kaynak kodlarına şuradan erişebilirsiniz: Data transfer between destinations example

Kaynaklar:

Tavsiyeler, öneriler veya hatalı bulduğunuz kısımlar için Ahmet Sırım | LinkedIn veya Ahmet.SIRIM@outlook.com adresinden bana ulaşabilirseniz çok sevinirim. Yazıyı okuduğunuz için teşekkür ederim, başarılar :)

--

--