Android Fundamentals-5 | Navigation

Suveybesena
9 min readMay 5, 2022

--

Merhabalar,

Bu seride doküman okuma alışkanlığımı geliştirmek ve Android temellerine dair daha kapsamlı bilgi edinmek adına developer.android.com üzerinden seçtiğim konular hakkında notlarımı paylaşıyor olacağım. Keyifli okumalar.

Navigasyon ilkeleri

Farklı ekranlar ve uygulamalar arasında gezinme, kullanıcı deneyiminin temel bir parçasıdır. Aşağıdaki ilkeler, uygulamalar arasında tutarlı ve sezgisel bir kullanıcı deneyimi için bir temel oluşturur. Navigation component, default olarak bu ilkeleri uygulamak üzere tasarlanmıştır ve kullanıcıların uygulamalar arasında gezinebilmeleri için standardize edilmiş sezgisel bir kalıp sunar.

Start Destination

Oluşturduğumuz her uygulamanın sabit bir başlangıç hedefi vardır. Bu, kullanıcının launcherdan uygulamamızı başlattığında gördüğü ilk ekrandır. Bu hedef aynı zamanda kullanıcının Back düğmesine bastıktan sonra başlatıcıya döndüğünde gördüğü son ekrandır. Örnek olarak ;

Bir ürün listesi ve detay sayfalarını içerek uygulamayı launcher’dan başlatırken, kullanıcının gördüğü ilk ekran, Liste Ekranıdır. Bu aynı zamanda uygulamadan çıkmadan önce gördükleri son ekrandır. Liste ekranından Back düğmesine basarlarsa, launcher’a geri dönerler.

Not: Bir uygulamanın tek seferlik oturum açma ya da onboarding gibi ekranı olabilir. Kullanıcılar bu ekranları yalnızca belirli durumlarda gördüğünden, bu koşullu ekranlar start destination olarak kabul edilmemelidir.

Uygulama gezintisinde stack mantığı vardır. Yeni bir hedef belirlediğimizde, yani kullanıcı başka bir ekrana navigate ettiğinde, bu ekran stack’in en üst kısmına eklenir, bir önceki ekran stack’de mevcut ekranın bir altında kalır. Yani yığının üst kısmı geçerli ekrandır ve yığındaki önceki hedefler, bulunduğumuz yerin geçmişini temsil eder.

Back stack’i değiştiren işlemler, ya stack’in en üstüne yeni bir hedef iterek ya da en üstteki hedefi stack’ten çıkararak her zaman stack’in üstünde çalışır. Bir hedefe gitmek, o hedefi stack’in en üstüne iter.

Navigation component, tüm back stack’i bizim için yönetir, ancak bunu custom hale getirebiliriz.

Up and Back Button

Back düğmesi, ekranın alt kısmındaki sistem gezinme çubuğunda görünür ve kullanıcının son zamanlarda çalıştığı ekranların geçmişinde ters kronolojik sırayla gezinmek için kullanılır. Back düğmesine bastığımızda, geçerli hedef back stack’in en üstünden açılır ve ardından önceki hedefe gideriz.

Up düğmesi, ekranın üst kısmındaki uygulama çubuğunda görünür. Uygulamamızın görevi dahilinde Up ve Back düğmeleri aynı şekilde davranır. Ama;

Up düğmesi uygulamadan asla çıkmaz

Bir kullanıcı uygulamanın başlangıç hedefindeyse, Up düğmesi uygulamadan hiçbir zaman çıkmadığı için Up düğmesi görünmez. Ancak Back düğmesi gösterilir ve uygulamadan çıkar.

Uygulamamız başka bir uygulamadan deep link kullanılarak başlatıldığında, Up, bizi deep link’i tetikleyen uygulamaya değil, uygulamamızın ana ekranına döndürür çünkü Up düğmesi ile uygulamadan çıkamayız, ancak Back düğmesi bizi diğer uygulamaya geri götürür.

Deep link, manuel gezinmeyi simüle eder

İster deep link ister belirli bir hedefe manuel olarak navigasyon yapalım, o navige ettiğimiz uygulamanın başlangıç hedefine geri dönmek için Up düğmesini kullanabiliriz

Uygulamamızın görevi içindeki bir hedefe deep link oluştururken, uygulamamızın görevi için mevcut tüm back stackler kaldırılır ve deep link’in back stack’i ile değiştirilir.

Örneğin bir uygulamanın start destinationunda olduğumuzu düşülelim, burada bir liste var, ve bu listeden bir item’a tıkladığımızda bizi bu A item’ın detaylarının olduğu ekrana götürüyor. Tam bu noktada, home tuşuna basarak uygulamayı background’a aldığımızı varsayalım. Uygulamamız, bir deep link ile doğrudan item’ın detay sayfasına gitmemize izin veriyor olsun. Bu link ile B detayının bulunduğu bu sayfaya gider isek, önceki liste ve detay sayfasını içeren back stack kaldırılır, güncel detay sayfası ve uygulamanın launch sayfasını da içeren yeni bir deep link back stack’i oluşturulur.

A back stack’inin , B ayrıntı ekranı için yapılan sentetik bir back stack ile değiştirildiğine unutmayalım. Start destination’da back stack’e eklendi. Tüm bunlar önemlidir çünkü sentetik back stack gerçekçi olmalıdır. Uygulamada organik olarak gezinerek elde edilebilecek bir back stack ile eşleşmelidir.

Bu ihtiyacı karşılamak için oluşturulan sentetik back stack NavGraph’a dayalı basitleştirilmiş bir stack’dir. Nested graph içermeyen basit bir NavGraph için bu, start destination ve deep link hedefinden oluşacaktır. Daha karmaşık, nested gezinme grafikleri için, sentetik back stack, deep link hedefinin ataları olan tüm nested grafiklerin başlangıç ​​hedeflerini de içerecektir.

Navigasyon bileşeni, deep link’i destekler ve navigasyon grafiğimizdeki herhangi bir hedefe bağlanırken bizim için gerçekçi bir back stack oluşturur.

Navigation

Navigation, kullanıcıların uygulamamızdaki farklı içerik parçaları arasında gezinmesine, bunlara girmesine ve bu içeriklerden geri çıkmasına olanak tanıyan etkileşimleri ifade eder. Android Jetpack’in Navigation componenti, basit düğme tıklamalarından app bar ve navigation drawer gibi daha karmaşık kalıplara kadar gezinmeyi uygulamamıza yardımcı olur. Navigasyon componenti aynı zamanda yerleşik bir dizi ilkeye bağlı kalarak tutarlı ve öngörülebilir bir kullanıcı deneyimi sağlar.

Navigation componenti aşağıda açıklanan üç temel bölümden oluşur:

Navigation graph: Navigation ile ilgili tüm bilgileri tek bir merkezi konumda içeren bir XML kaynağı. Bu, uygulamamızdaki destination olarak adlandırılan tüm içerik alanlarının yanı sıra bir kullanıcının uygulamamızda izleyebileceği olası yolları içerir.

NavHost: Navigation graph’ımızdaki hedefleri görüntüleyen boş bir kapsayıcı. Navigation componenti,, fragment hedeflerini görüntüleyen default bir NavHost uygulaması olan NavHostFragment’i içerir.

NavController: Bir NavHost içinde uygulama navigasyonunu yöneten bir nesne. NavController, kullanıcılar uygulamamızda hareket ettikçe NavHost’taki hedef içeriğin değiştirilmesini düzenler.

Uygulamamızda gezinirken, NavController’a navigasyon grafiğimizdeki belirli bir yol boyunca veya doğrudan belirli bir hedefe gitmek istediğimizi söyleriz. NavController daha sonra NavHost’ta uygun hedefi gösterir.

Navigation componenti ,aşağıdakiler de dahil olmak üzere bir dizi başka avantaj sağlar:

Fragment geçişlerini handle etme.

Up ve Back eylemlerini default olarak doğru şekilde işleme.

Animasyonlar ve geçişler için standartlaştırılmış kaynaklar sağlamak.

Deep linki uygulama ve işleme.

Navigation UI patternları dahil olmak üzeren avigation drawers ve bottom navigation gibi navigation kalıplarını minimum kodla handle etme.

Safe Args — Gradle plugin: Destinationlar arasında gezinirken ve veri aktarımı yaparken güvenlik sağlayan bir eklenti.

ViewModel desteği — UI ile ilgili verileri grafiğin hedefleri arasında paylaşmak için bir ViewModel’i bir navigasyon grafiğine dönüştürebiliriz.

Ayrıca, navigation graphlerimizi görüntülemek ve düzenlemek için Android Studio’nun Navigation Editor ‘ünü kullanabiliriz.

Navigation, uygulamamızın hedefleri arasında, yani uygulamamızın içinde, kullanıcıların gezinebileceği herhangi bir yerde gerçekleşir. Bu hedefler action’lar aracılığıyla bağlanır.

Navigation graph, tüm destination and actionlarımızı içeren bir kaynak dosyasıdır. Graph, uygulamamızın tüm gezinme yollarını temsil eder.

1. Destinations , uygulamamızdaki farklı içerik alanlarıdır.

2. Actions , kullanıcıların izleyebileceği yolları temsil eden hedeflerimiz arasındaki mantıksal bağlantılardır.

Navigation Editor

Bir grafik ekledikten sonra Android Studio, Navigation Editor’de açar. Navigation Editor ‘de gezinme grafiklerini görsel olarak düzenleyebilir veya temel alınan XML’i doğrudan düzenleyebiliriz.

Top-level navigation graph

Uygulamamızın en üst düzey gezinme grafiği, kullanıcının uygulamayı başlatırken gördüğü ilk hedefle başlamalı ve uygulamamızda gezinirken gördükleri hedefleri içermelidir.

Uygulamamızdaki oturum açma, onboarding gibi akışlar nested graphs ile en iyi şekilde olarak temsil edilir. Bağımsız sub-navigation flowlarını bu şekilde iç içe yerleştirerek, uygulamamızın kullanıcı arayüzünün ana akışını anlamak ve yönetmek daha kolaydır. Ek olarak, nested grafikler yeniden kullanılabilir. Ayrıca bir düzeyde kapsülleme sağlarlar; nested grafiğin dışındaki bu grafiğin içindeki hedeflerin hiçbirine doğrudan erişimi yoktur.

Grafik yapımızı modülerleştirmenin başka bir yolu, ana navigation graph’te bir <include> öğesi aracılığıyla bir grafiği diğerinin içine dahil etmektir. Bu, dahil edilen grafiğin tamamen ayrı bir modülde veya projede tanımlanmasına izin vererek yeniden kullanılabilirliği en üst düzeye çıkarır.

Global actions

Birden çok hedefin kullanabileceği ortak bir eylem oluşturmak için genel bir eylem kullanabiliriz. Örneğin, farklı hedeflerdeki düğmelerin aynı ana uygulama ekranına gitmesini isteyebiliriz.

Hedefler arasında gezinmenin önerilen yolu, Safe Args Gradle eklentisini kullanmaktır. Bu eklenti, hedefler arasında güvenli türde gezinmeyi sağlayan basit object and builder class ‘ları oluşturur. Safe Args, hem gezinme hem de hedefler arasında veri aktarımı için önerilir.

Safe Args’ı etkinleştirdikten sonra, oluşturulan kodumuz, tanımladığımız her eylem için sınıflar ve yöntemlerin yanı sıra her bir gönderme ve alma hedefine karşılık gelen sınıfları içerir.

Safe Args, bir eylemin başladığı her hedef için bir sınıf oluşturur. Oluşturulan sınıf adı, kaynak hedef sınıf adına “Directions” ekler. Örneğin, kaynak hedef SpecifyAmountFragment olarak adlandırılırsa, oluşturulan sınıf SpecifyAmountFragmentDirections olarak adlandırılır.

Oluşturulan sınıf, kaynak hedefte tanımlanan her action için statik bir yöntem içerir. Bu yöntem, herhangi bir tanımlanmış action parametresini argüman olarak alır ve doğrudan navigate() ‘e iletebileceğimiz bir NavDirections nesnesi döndürür.

DeepLinkRequest’i kullanarak gezinme

Aşağıdaki örnekte gösterildiği gibi, doğrudan deep link hedefine gitmek için navigasyonu(NavDeepLinkRequest) kullanabiliriz;

NavDeepLinkRequest, Uri’ye ek olarak, eylemler ve MIME türleri içeren deep linkleri de destekler. İsteğe bir eylem eklemek için fromAction() veya setAction() kullanılır. Bir isteğe MIME türü eklemek için fromMimeType() veya setMimeType() kullanabiliriz.

Action veya hedef ID’leri kullanan navigasyonun aksine, hedefin görünür olup olmadığına bakılmaksızın grafiğimizdeki herhangi bir deep linke gidebiliriz. Geçerli grafikte bir hedefe veya tamamen farklı bir grafikte bir hedefe gidebiliriz.

NavDeepLinkRequest kullanarak gezinirken, back stack sıfırlanmaz. Bu davranış, gezinirken back tack’in değiştirildiği diğer deep link gezintisinden farklıdır. popUpTo ve popUpToInclusive, sanki bir kimlik kullanarak gezinmişsiniz gibi back stack’den hedefleri kaldırmaya devam eder.

Navigation ve Back Stack

Android, ziyaret ettiğimiz hedefleri içeren bir back stack tutar. Uygulamamızın ilk hedefi, kullanıcı uygulamayı açtığında yığına yerleştirilir. navigate() yöntemine yapılan her çağrı, yığının üstüne başka bir hedef koyar. Up veya Back’e dokunmak, yığının en üstündeki hedefi kaldırmak (veya açmak) için sırasıyla NavController.navigateUp() ve NavController.popBackStack() yöntemlerini çağırır.

NavController.popBackStack(), başka bir hedefe başarıyla pop edilip edilmediğini gösteren bir boolean döndürür. Bunun false döndürdüğü en yaygın durum, grafiğimizin başlangıç hedefini manuel olarak açmamızdır.

Yöntem false değerini döndürdüğünde, NavController.getCurrentDestination() null değerini döndürür. Aşağıdaki örnekte gösterildiği gibi, ya yeni bir hedefe gitmekten ya da Activity’mizde finish()’i çağırarak pop’u yönetmekten biz sorumluyuz;

popUpTo ve popUpToInclusive

Bir actionu kullanarak gezinirken, isteğe bağlı olarak back stack’den ek hedefler çıkarabiliriz. Örneğin, uygulamamızın bir oturum açma akışı varsa, bir kullanıcı oturum açtıktan sonra, Back düğmesinin kullanıcıları oturum açma akışına geri götürmemesi için oturum açmayla ilgili tüm hedefleri back stack’den çıkarmamız gerekir.

Bir hedeften diğerine giderken hedefleri açmak için, ilişkili <action> öğesine bir app:popUpTo niteliği ekleyebiliriz. app:popUpTo, Navigasyon kitaplığına, navigate() çağrısının bir parçası olarak back stack’den bazı hedefleri açmasını söyler. Attribute value, yığında kalması gereken en son hedefin kimliğidir.

app:popUpToInclusive=”true” ise, belirtilen hedefin de ayrıca stack’den kaldırılıp kaldırılmayacağını belirtmek için kullanılur.

C hedefine ulaştıktan sonra, back stack her hedefin (A, B, C) bir örneğini içerir. A hedefine geri dönerken, A’yı da açarız, bu da navigasyon sırasında B ve C’yi yığından kaldırdığımız anlamına gelir. app:popUpToInclusive=”true” ile, ilk A’yı da yığından çıkararak etkin bir şekilde temizliyoruz. Burada, app:popUpToInclusive kullanmazsak, back stack aynı A hedefinden iki adet içerir.

popUpToSaveState ve restoreSaveState

Bir hedefe gitmek için app:popUpTo’yu kullandığınızda, Navigasyon 2.4.0-alpha01 ve üstü, isteğe bağlı olarak back stack’den atılan tüm hedeflerin durumlarını kaydetmemize izin verir. Bu seçeneği etkinleştirmek için ilişkili <action> öğesine app:popUpToSaveState=”true” öğesini ekleriz;

popUpToInclusive ‘de olduğu gibi, bir hedefe gittiğimizde, app:destination içindeki hedefle ilişkili durumu otomatik olarak geri yüklemek için app:restoreSaveState=”true” öğesini kullanırız.

Pass data between destinations

Navigasyon, bir hedef için bağımsız değişkenler tanımlayarak bir navigasyon işlemine veri eklememizi sağlar. Örneğin, bir kullanıcı profili hedefi, hangi kullanıcının görüntüleneceğini belirlemek için bir kullanıcı kimliğini değişken olarak alabilir.

Genel olarak, hedefler arasında yalnızca minimum miktarda veri iletmeyi kesinlikle tercih etmeliyiz. Örneğin, Android’de kaydedilen tüm durumlar için toplam alan sınırlı olduğundan, nesnenin kendisini geçmek yerine bir nesneyi almak için bir anahtar iletmeliyiz.

Desteklenen bağımsız değişken türleri

Navigation kütüphanesi aşağıdaki değişken türlerini destekler:

Bir argüman türü null değerleri destekliyorsa, android:defaultValue=”@null” kullanarak default bir null değeri bildirebiliriz.

Özel türlerden birini seçtiğimizde, Select Class dialog kutusu görünür ve bizden o tür için ilgili sınıfı seçmemizi ister. Proje sekmesi, mevcut projemizden bir sınıf seçmenize olanak tanır.

Navigation kütüphanesinin türü sağlanan değere göre belirlemesini sağlamak için < inferred type> öğesini seçebiliriz.

Array’i, bağımsız değişkenin seçilen Type değerinin bir dizisi olması gerektiğini belirtmek için işaretleyebilirsiniz. Aşağıdakilere dikkat et:

· Enum arrayleri ve and resource references arrayleri desteklenmez.

· Arrayler, temel alınan türün null yapılabilir değerleri desteğinden bağımsız olarak null yapılabilir değerleri destekler. Örneğin, app:argType=”integer[]” kullanmak, boş bir array geçirmenin kabul edilebilir olduğunu belirtmek için app:nullable=”true” kullanmamıza olanak tanır.

· Arrayler tek bir varsayılan değeri, “@null” destekler, başka bir varsayılan değeri desteklemez.

·

Dikkat: Argümanların üzerinden karmaşık veri yapılarının geçirilmesi bir anti-pattern olarak kabul edilir. Her hedef, item IDleri gibi gerekli minimum bilgilere dayalı olarak UI verilerini yüklemekten sorumlu olmalıdır. Bu, işlemin yeniden oluşturulmasını basitleştirir ve olası veri tutarsızlıklarını önler.

Verileri tür güvenliğiyle iletmek için Güvenli Args’ı kullanın

Navigation component, güvenli türde gezinme ve ilişkili değişkenlere erişim için basit nesne ve oluşturucu sınıfları oluşturan Safe Args adlı bir Gradle eklentisine sahiptir. Safe Args, tür güvenliğini sağladığından, gezinme ve veri geçişi için şiddetle tavsiye edilir.

Bazı durumlarda, örneğin Gradle kullanmıyorsanız Safe Args eklentisini kullanamazsınız. Bu durumlarda, verileri doğrudan iletmek için Bundles kullanabilirsiniz.

AndroidX’e Geçiş uyarınca gradle.properties dosyamızda android.useAndroidX=true olması gerekir.

Safe Args’ı etkinleştirdikten sonra, oluşturulan kodumuz, her bir gönderme ve alma hedefinin yanı sıra her action için aşağıdaki güvenli tür sınıfları ve yöntemleri içerir.

Aşağıdaki örnek, bir argüman ayarlamak ve onu navigation() yöntemine iletmek için bu yöntemleri nasıl kullanacağımızı gösterir:

Alma hedefimizin kodunda, bundle’ı almak ve içeriğini kullanmak için getArguments() yöntemini kullanırız. -ktx bağımlılıklarını kullanırken, Kotlin kullanıcıları argümanlara erişmek için by navArgs() özelliği temsilcisini de kullanabilir.

Global Action’da Safe Args kullanımı

Global action’la Safe Args kullanırken, aşağıdaki örnekte gösterildiği gibi root <navigation> öğemiz için bir android:id değeri sağlamamız gerekir:

Okuduğunuz için teşekkür ederim, bir sonraki yazıda görüşmek üzere :)

Referanslar ;

https://developer.android.com/guide/navigation/navigation-principles

--

--