Android Navigation Component — Kotlin

Alper Avcı
6 min readSep 18, 2023

--

Merhaba, bu yazımda sizlere Navigation Component’ten bahsedeceğim. Öncelikle Navigation Component’in ne olduğu hakkında bilgi verip projemize nasıl ekleyebileceğimizi göstereceğim. Ardından küçük bir örnek ile basit bir şekilde kullanımını size aktarmaya çalışacağım.

Navigation Component Nedir?

Navigation Component, sayfalar arasında yönlendirmeler yapmamızı sağlayan bir kütüphanedir. Bu kütüphane, özellikle büyük ve karmaşık uygulamalarda farklı sayfalar (fragment’lar veya aktiviteler) arasındaki geçişi daha basit hale getirir.

Navigation Component’in Temel Özellikleri

Navigation Graphs: Uygulamadaki ekranlar ve bu ekranlar arasındaki ilişkiyi grafik üzerinde bizlere sunan bir yapıdır. Tasarım anlamında büyük kolaylık sağlayan bir yapıdır.

Developer Android dökümantasyonundan alınmıştır.

Destinationlar (Hedefler): (1) Uygulamadaki her bir ekran Navigation Graphs içerisinde destination olarak tanımlanır. Bir biri arasında ilişki kurduğumuz her ekran bir destination’dır. Ve her destination benzersiz olmalıdır. Yani her birinin id bilgileri farklı olmalıdır.

Action’lar: (2) Ekranlar arasındaki geçişleri temsil eden eylemlerdir. Bir ekrandan diğer ekrana doğru kurduğumuz ilişki bir action’dur. Action’lar tek yönlüdür. A ekranından B ekranına bir ilişki kurduğumuz zaman herhangi bir tetikleme ile sadece A ekranından B ekranına geçiş sağlanabilir.

Geçiş Animasyonları: Action’lar tetiklendiği anda ekran geçişi sırasında animasyon eklenebilir. Bu animasyonlar daha yumuşak bir geçiş imkanı sağlar.

Deep Linking: Uygulamanızı belirli URL’lerle ilişkilendirmenize ve bu URL’leri kullanarak doğrudan belirli ekranlara yönlendirmenize olanak tanır

Navigation Component Kurulumu

Kütüphaneyi kurmak için üstteki kodu build.gradle(Moudle) içerisine ekleyip ve Sync Now’a basıyoruz. Versiyonlar sizde değişiklik gösterebilir, bunun için developer.android’e göz atabilirsiniz.

Buna ek olarak birde Safe Args kütüphanesini ekleyeceğiz. Bu kütüphane action’lar ile veri aktarmamızı sağlar.

Sıralı şekilde ekleyip her eklemeden sonra Sync Now’a basmanız gerekmekte, aksi halde hata alabilirsiniz. Aynı şekilde burada da versiyonlar değişiklik gösterebilir, bunun için developer.android’e göz atabilirsiniz.

Navigation Component Kullanımı

Navigation’ın nasıl kullanıldığını göstermek iki adet fragment oluşturacağız. İlkinde kullanıcıdan kullanıcı adı, email, doğum yılı ve şifre istediğimiz bir giriş ekranı bulunacak. Kullanıcı adı ve şifre bir nesne ile email ve doğum yılı ise değişken olarak ikinci fragment’ımıza gönderilecek. Bu sayede nesne ve ilkel veri tiplerini taşımayı görmüş olacağız.

Kullanıcıdan bilgi aldığımız FirstFragment tasarımı:

<?xml version="1.0" encoding="utf-8"?>
<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=".FirstFragment">

<EditText
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="164dp"
android:ems="10"
android:hint="Kullanıcı adı"
android:inputType="text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<EditText
android:id="@+id/email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:ems="10"
android:hint="Email"
android:inputType="textEmailAddress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/username" />

<EditText
android:id="@+id/birthYear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:ems="10"
android:hint="Doğum yılı"
android:inputType="number"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/email" />

<EditText
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:ems="10"
android:hint="Şifre"
android:inputType="number"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/birthYear" />

<Button
android:id="@+id/button"
android:layout_width="206dp"
android:layout_height="53dp"
android:layout_marginTop="76dp"
android:text="ONAYLA"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/password" />
</androidx.constraintlayout.widget.ConstraintLayout>

Bilgileri gösterdiğimi SecondFragment tasarımı:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".SecondFragment">

<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="164dp"
android:ems="10"
android:text="Kullanıcı Adı"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:ems="10"
android:text="Email"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/username" />

<TextView
android:id="@+id/birthYear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:ems="10"
android:text="Doğum Yılı"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/email" />

<TextView
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:ems="10"
android:text="Şifre"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/birthYear" />

<Button
android:id="@+id/button"
android:layout_width="234dp"
android:layout_height="48dp"
android:layout_marginBottom="164dp"
android:text="GERİ"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Sıra Navigation dosyasını oluşturmakta. Bu dosya fragment’ler arasındaki bağlantıyı kuracağımız navigation graphs yapısını içeriyor. Aşağıdaki adımları takip ederek dosyamızı oluşturuyoruz.

Navigation dosyasını oluşturduktan sonra aşağıdaki şekilde fragment’larımızı ekliyoruz. Sonrasında fragment’lar arasındaki action’ları oluşturuyoruz.

Action’ların id’leri vardır ve id’ler benzersiz olmalıdır. Id’leri değiştirmek için action üzerine tıklıyoruz ardından sağ tarafta bulunan Attributes bölümünden id değiştiriyoruz. Biz birisini action_first diğerine action_second olarak ayarlıyoruz.

Fragment üzerinde bulunan ev işareti navigation’ın başlangıç ekranını gösterir. Burada başlangıç ekranımız firstFragment olarak ayarlanmıştır. İsterseniz başlangıç ekranı yapmak istediğiniz fragment’ı seçip sol üstte bulunan ev işaretine basarak düzenleyebilirsiniz.

Navigation dosyamızı düzenledik, şimdi bunu ekranda gösterme zamanı. Bunun için NavHostFragment bileşenini kullanıyoruz. Bu bileşen navigation dosyalarını ekranda göstermemizi sağlar. Bu işlemi MainActivity içerisinde yapacağız. Aşağıda nasıl ekleneceğini görebilirsiniz.

Daha önceden söylediğimiz gibi kullanıcıdan verileri alıp diğer fragment’a göndereceğiz. Bunun için bir takım eklemeler yapmamız gerekmekte ama ondan önce göndereceğimiz nesnenin sınıfını oluşturalım.

User adında bir sınıf oluşturuyoruz. Ama bu sınıfta bazı düzenlemeler yapmamız gerekmekte çünkü intentlerde olduğu gibi actionlar da temel veri tipleri (Int, String, vs.), bazı standart Android tipleri (Bundle, Parcelable veya Serializable interface’ini uygulayan türler gibi) veya URI’leri taşıma amacıyla tasarlanmıştır. Bu yüzden sınıfımızı Parcelable olacak şekilde düzenlememiz gerekiyor.

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
class User(val username: String, val password: Int) : Parcelable {

}

Bir ekran başka ekranlardan veri almak isterse bu verileri belirtmemiz gerekir. Bunun için verileri alacak ekranı seçiyoruz ve sağ taraftan Argumets’e verileri ekliyoruz. Eklediğimiz her verinin tipini belirtmemiz gerekmetedir. Aşağıda nasıl ekleneceğini görebilirsiniz.

FirstFragment içeriğini aşağıdaki şekilde düzenliyoruz.

Not: Burada View Binding kullanılmıştır. Konu hakkında bilgi edinmek için View Binding Kullanımı adlı yazıma bakabilirsiniz.

class FirstFragment : Fragment() {

private lateinit var binding: FragmentFirstBinding

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentFirstBinding.inflate(inflater, container, false)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

binding.apply {

button.setOnClickListener {

// Kullanıcı verilerini alma
val username = username.text.toString()
val email = email.text.toString()
val birthYear = birthYear.text.toString().toInt()
val password = password.text.toString().toInt()

val user = User(username, password)

// Tetiklenecek olan action
val directions = FirstFragmentDirections.actionFirst(user, birthYear, email)

// Ekran değişiminin gerçekleşmesini sağlayacak kod
Navigation.findNavController(it).navigate(directions)

}

}

}

}

Öncelikle kullanıcıdan aldığımız verilerin tür dönüşümlerini uygun bir şekilde yaptık. Ardından nesnemizi oluşturduk. Sonrasında hangi action’ı kullanacağımızı seçtik. Sonuç olarak bir Fragmet’ta birden fazla action bulunabilir ve bizim bunlardan uygun olanını seçmemiz gerekir. Ayrıca seçilen action’ın gideceği ekran herhangi bir veri bekliyorsa bunu içerisine parametre olarak alması gerekir. Bizim gideceğimiz ekran 3 farklı veri istemekte ve bizde bu verileri istenilen şekilde action’un içerisine aktarıyoruz.

Veriler diğer ekranımıza gönderildi, şimdi bunları işleyip ekranda gösterme vakti.

SecondFragment içeriğini aşağıdaki şekilde düzenliyoruz.

class SecondFragment : Fragment() {

private lateinit var binding: FragmentSecondBinding

// SecondFragment içerisine gelen verileri yakalama
private val args : SecondFragmentArgs by navArgs()

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

binding.apply {

// Gelen verileri alma
val usernameArgs = args.user.username
val emailArgs = args.email
val birthYearArgs = args.birthYear
val passwordArgs = args.user.password

username.text = "Kullanıcı Adı: $usernameArgs"
email.text = "Kullanıcı Adı: $emailArgs"
birthYear.text = "Kullanıcı Adı: $birthYearArgs"
password.text = "Kullanıcı Adı: $passwordArgs"

button.setOnClickListener {

// Tetiklenecek olan action
val directions = SecondFragmentDirections.actionSecond()

// Ekran değişiminin gerçekleşmesini sağlayacak kod
Navigation.findNavController(it).navigate(directions)

}

}

}

}

Oluşturmuş olduğumuz args değişkeni SecondFragment’ın aldığı verileri içerisinde tutan bir yapıdır. Bu değişken sayesinde bir değişken ile fragment’a gelen bütün verilere ulaşabiliriz.

Verileri aldık ve gerekli yerlere yerleştirdik. Ayrıca FirstFragment’a dönmek için oluşturduğumuz action için butona gerekli kodları yazdık. Bir önceki sayfaya dönmek için özel bir action oluşturmamıza gerek yok, çünkü back stack yapısı mevcuttur. Yani farklı bir ekrana gittiğimizde bundan önceki ekranlar stack’te tutulur ve geri tuşuna bastığımız an stack’e göre ekranlar bize gösterilir.

Umarım sizlere güzel bir şekilde anlatım yapabilmişimdir. İyi çalışmalar dilerim.

Proje kodları : Github

https://developer.android.com/guide/navigation/get-started

--

--