Gezinme bileşenine taşıma

Gezinme bileşeni; karmaşık gezinmeyi, geçiş animasyonunu, derin bağlantıları ve uygulamanızdaki ekranlar arasında geçiş yapan, derleme zamanı kontrol edilmiş bağımsız değişkeni yönetebilen bir kitaplıktır.

Bu doküman, mevcut bir uygulamayı Gezinme bileşenini kullanacak şekilde taşımak için genel amaçlı bir kılavuz işlevi görür.

Genel olarak taşıma işlemi aşağıdaki adımları içerir:

  1. Ekrana özgü kullanıcı arayüzü mantığını etkinliklerin dışına taşıyın - Uygulamanızın kullanıcı arayüzü mantığını etkinliklerin dışına taşıyın. Her etkinliğin yalnızca Toolbar gibi genel gezinme arayüzü bileşenlerinin mantığına sahip olmasını sağlayın ve her bir ekranın uygulanması için yetkiyi bir parçaya veya özel hedefe verin.

  2. Gezinme bileşenini entegre etme - Her etkinlik için, söz konusu etkinlik tarafından yönetilen bir veya daha fazla parçayı içeren bir gezinme grafiği oluşturun. Parça işlemlerini Gezinme bileşeni işlemleriyle değiştirin.

  3. Etkinlik hedefleri ekleme - Etkinlik hedeflerini kullanarak startActivity() çağrılarını işlemlerle değiştirin.

  4. Aktiviteleri birleştirme: Birden fazla etkinliğin ortak bir düzeni paylaştığı durumlarda gezinme grafiklerini birleştirin.

Ön koşullar

Bu kılavuzda, AndroidX kitaplıklarını kullanmak için uygulamanızı önceden taşıdığınız varsayılmaktadır. Henüz yapmadıysanız devam etmeden önce AndroidX'i kullanmak için projenizi taşıyın.

Ekrana özgü kullanıcı arayüzü mantığını etkinliklerin dışına taşı

Etkinlikler, uygulamanız ile Android arasında grafiksel etkileşimi kolaylaştıran sistem düzeyindeki bileşenlerdir. Etkinlikler, uygulamanızın manifest dosyasına kaydedilir. Böylece Android, hangi etkinliklerin başlatılabileceğini bilir. Etkinlik sınıfı, uygulamanızın kullanıcı arayüzünün ön plana girmesi veya ön plandan ayrılması, döndürme gibi Android değişikliklerine de tepki vermesini sağlar. Etkinlik, ekranlar arasında durum paylaşımı için de bir yer işlevi görebilir.

Uygulamanız bağlamında etkinlikler, gezinme için bir ana makine görevi görmeli ve ekranlar arasında geçiş yapma, verileri iletme vb. mantığı ve bilgiyi içermelidir. Ancak, kullanıcı arayüzünüzün ayrıntılarını yönetmek için kullanıcı arayüzünüzün daha küçük ve yeniden kullanılabilir bir bölümü olması daha iyidir. Bu kalıp için önerilen uygulama parçalar şeklindedir. Parça kullanmanın avantajları hakkında daha fazla bilgi edinmek için Tek Etkinlik: Neden, Ne Zaman ve Nasıl bölümüne bakın. Gezinme, navigation-parça bağımlılığı aracılığıyla parçaları destekler. Navigasyon, özel hedef türlerini de destekler.

Uygulamanız parçalar kullanmıyorsa yapmanız gereken ilk şey, uygulamanızdaki her ekranı bir parça kullanacak şekilde taşımaktır. Bu noktada etkinliği kaldırmazsınız. Bunun yerine, ekranı temsil eden ve kullanıcı arayüzü mantığınızı sorumluluğa göre ayıran bir parça oluşturursunuz.

Parçalarla tanışın

Parçaları kullanıma sunma sürecini göstermek için, iki ekrandan oluşan bir uygulama örneğinle başlayalım: ürün listesi ekranı ve ürün ayrıntıları ekranı. Liste ekranında bir ürünü tıklayan kullanıcılar, ürün hakkında daha fazla bilgi edinebilecekleri bir ayrıntılar ekranına yönlendirilir.

Bu örnekte, liste ve ayrıntılar ekranları şu anda ayrı etkinliklerdir.

Kullanıcı Arayüzünü Barındırmak için Yeni Bir Düzen Oluşturma

Bir parçayı yerleştirmek için işlemin parçayı barındırması amacıyla yeni bir düzen dosyası oluşturarak başlayın. Bu işlem, etkinliğin mevcut içerik görünümü düzeninin yerini alır.

Basit bir görünüm için aşağıdaki product_list_host örnekte gösterildiği gibi bir FrameLayout kullanabilirsiniz:

<FrameLayout
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/main_content"
   android:layout_height="match_parent"
   android:layout_width="match_parent" />

id özelliği, daha sonra parçayı eklediğimiz içerik bölümünü ifade eder.

Ardından, etkinliğinizin onCreate() işlevinde, etkinliğinizin onCreate işlevindeki düzen dosyası referansını bu yeni düzen dosyasına işaret edecek şekilde değiştirin:

Kotlin

class ProductListActivity : AppCompatActivity() {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Replace setContentView(R.layout.product_list) with the line below
        setContentView(R.layout.product_list_host)
        ...
    }
}

Java

public class ProductListActivity extends AppCompatActivity {
    ...
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        ...
        // Replace setContentView(R.layout.product_list); with the line below
        setContentView(R.layout.product_list_host);
        ...
    }
}

Mevcut düzen (bu örnekte product_list), oluşturmak üzere olduğunuz parça için kök görünüm olarak kullanılır.

Parça oluşturma

Ekranınızın kullanıcı arayüzünü yönetmek için yeni bir parça oluşturun. Etkinlik ana makine adınızla tutarlı olmanız iyi bir uygulamadır. Aşağıdaki snippet'te ProductListFragment kullanılmıştır. Örneğin:

Kotlin

class ProductListFragment : Fragment() {
    // Leave empty for now.
}

Java

public class ProductListFragment extends Fragment {
    // Leave empty for now.
}

Etkinlik mantığını bir parçaya taşıma

Parça tanımı uygulandıktan sonraki adım, bu ekranla ilgili kullanıcı arayüzü mantığını etkinlikten bu yeni parçaya taşımaktır. Etkinliğe dayalı bir mimariden geliyorsanız etkinliğinizin onCreate() işlevinde büyük olasılıkla çok sayıda görüntüleme oluşturma mantığınız vardır.

Taşımamız gereken kullanıcı arayüzü mantığına sahip etkinliğe dayalı bir ekran örneği aşağıda verilmiştir:

Kotlin

class ProductListActivity : AppCompatActivity() {

    // Views and/or ViewDataBinding references, Adapters...
    private lateinit var productAdapter: ProductAdapter
    private lateinit var binding: ProductListActivityBinding

    ...

    // ViewModels, System Services, other Dependencies...
    private val viewModel: ProductListViewModel by viewModels()

    ...

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // View initialization logic
        DataBindingUtil.setContentView(this, R.layout.product_list_activity)

        // Post view initialization logic
        // Connect adapters
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener {...}

        // Subscribe to state
        viewModel.products.observe(this, Observer { myProducts ->
            ...
        })

        // ...and so on
    }
   ...
}

Java

public class ProductListActivity extends AppCompatActivity {

    // Views and/or ViewDataBinding references, adapters...
    private ProductAdapter productAdapter;
    private ProductListActivityBinding binding;

    ...

    // ViewModels, system services, other dependencies...
    private ProductListViewModel viewModel;

    ...

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // View initialization logic
        DataBindingUtil.setContentView(this, R.layout.product_list_activity);

        // Post view initialization logic
        // Connect adapters
        productAdapter = new ProductAdapter(productClickCallback);
        binding.productsList.setAdapter(productAdapter);

        // Initialize ViewModels and other dependencies
        ProductListViewModel viewModel = new ViewModelProvider(this).get(ProductListViewModel.java);

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener(v -> { ... });

        // Subscribe to state
        viewModel.getProducts().observe(this, myProducts ->
            ...
       );

       // ...and so on
   }

Etkinliğiniz, aşağıdaki örnekte gösterildiği gibi, kullanıcının sonraki ekrana ne zaman ve nasıl geçeceğini de kontrol ediyor olabilir:

Kotlin

    // Provided to ProductAdapter in ProductListActivity snippet.
    private val productClickCallback = ProductClickCallback { product ->
        show(product)
    }

    fun show(product: Product) {
        val intent = Intent(this, ProductActivity::class.java)
        intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.id)
        startActivity(intent)
    }

Java

// Provided to ProductAdapter in ProductListActivity snippet.
private ProductClickCallback productClickCallback = this::show;

private void show(Product product) {
    Intent intent = new Intent(this, ProductActivity.class);
    intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.getId());
    startActivity(intent);
}

Parçanızın içinde, bu çalışmayı onCreateView() ile onViewCreated() arasında dağıtırsınız, ancak etkinlikte yalnızca gezinme mantığı kalır:

Kotlin

class ProductListFragment : Fragment() {

    private lateinit var binding: ProductListFragmentBinding
    private val viewModel: ProductListViewModel by viewModels()

     // View initialization logic
    override fun onCreateView(inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?): View? {
        binding = DataBindingUtil.inflate(
                inflater,
                R.layout.product_list,
                container,
                false
        )
        return binding.root
    }

    // Post view initialization logic
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // Connect adapters
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener {...}

        // Subscribe to state
        viewModel.products.observe(this, Observer { myProducts ->
            ...
        })

        // ...and so on
    }

    // Provided to ProductAdapter
    private val productClickCallback = ProductClickCallback { product ->
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            (requireActivity() as ProductListActivity).show(product)
        }
    }
    ...
}

Java

public class ProductListFragment extends Fragment {

    private ProductAdapter productAdapter;
    private ProductListFragmentBinding binding;

    // View initialization logic
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater,
            @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(
                inflater,
                R.layout.product_list_fragment,
                container,
                false);
        return binding.getRoot();
    }

    // Post view initialization logic
    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {

        // Connect adapters
        binding.productsList.setAdapter(productAdapter);

        // Initialize ViewModels and other dependencies
        ProductListViewModel viewModel = new ViewModelProvider(this)
                .get(ProductListViewModel.class);

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener(...)

        // Subscribe to state
        viewModel.getProducts().observe(this, myProducts -> {
            ...
       });

       // ...and so on

    // Provided to ProductAdapter
    private ProductClickCallback productClickCallback = new ProductClickCallback() {
        @Override
        public void onClick(Product product) {
            if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
                ((ProductListActivity) requireActivity()).show(product);
            }
        }
    };
    ...
}

ProductListFragment ürününde, düzeni genişletmek ve bağlamak için setContentView() çağrısı yapılmadığına dikkat edin. Bir parçada onCreateView(), kök görünümü başlatır. onCreateView(), düzen kaynak dosyasına göre kök görünümü genişletmek için kullanılabilecek bir LayoutInflater örneğini alır. Bu örnekte, düzenin kendisinde değişiklik yapılması gerekmediği için etkinlik tarafından kullanılan mevcut product_list düzeni yeniden kullanılır.

Etkinliğinizin onStart(), onResume(), onPause() veya onStop() işlevlerinde gezinmeyle ilgili olmayan kullanıcı arayüzü mantığınız varsa bunları parça üzerinde aynı ada sahip karşılık gelen işlevlere taşıyabilirsiniz.

Ana makine etkinliğinde parçayı başlatın

Tüm kullanıcı arayüzü mantığını parçaya taşıdıktan sonra, etkinlikte yalnızca gezinme mantığı kalır.

Kotlin

class ProductListActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.product_list_host)
    }

    fun show(product: Product) {
        val intent = Intent(this, ProductActivity::class.java)
        intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.id)
        startActivity(intent)
    }
}

Java

public class ProductListActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.product_list_host);
    }

    public void show(Product product) {
        Intent intent = new Intent(this, ProductActivity.class);
        intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.getId());
        startActivity(intent);
    }
}

Son adım, içerik görünümünü ayarladıktan hemen sonra onCreate() içinde parçanın bir örneğini oluşturmaktır:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.product_list_host)

    if (savedInstanceState == null) {
        val fragment = ProductListFragment()
        supportFragmentManager
                .beginTransaction()
                .add(R.id.main_content, fragment)
                .commit()
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.product_list_host);

    if (savedInstanceState == null) {
        ProductListFragment fragment = new ProductListFragment();
        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.main_content, fragment)
                .commit();
    }
}

Bu örnekte gösterildiği gibi FragmentManager, yapılandırma değişikliklerinin üzerine parçaları otomatik olarak kaydeder ve geri yükler. Bu nedenle, yalnızca savedInstanceState null ise parçayı eklemeniz gerekir.

Amaç ekstralarını parçaya iletme

Etkinliğiniz bir amaç aracılığıyla Extras alıyorsa bunları doğrudan bağımsız değişkenler olarak parçaya aktarabilirsiniz.

Bu örnekte, ProductDetailsFragment bağımsız değişkenlerini doğrudan etkinliğin intent ekstralarından alır:

Kotlin

...

if (savedInstanceState == null) {
    val fragment = ProductDetailsFragment()

    // Intent extras and Fragment Args are both of type android.os.Bundle.
    fragment.arguments = intent.extras

    supportFragmentManager
            .beginTransaction()
            .add(R.id.main_content, fragment)
            .commit()
}

...

Java

...

if (savedInstanceState == null) {
    ProductDetailsFragment fragment = new ProductDetailsFragment();

    // Intent extras and fragment Args are both of type android.os.Bundle.
    fragment.setArguments(getIntent().getExtras());

    getSupportFragmentManager()
            .beginTransaction()
            .add(R.id.main_content, fragment)
            .commit();
}

...

Bu noktada, ilk ekranı bir parça kullanacak şekilde güncellenmiş olarak uygulamanızı çalıştırmayı test edebilmeniz gerekir. Etkinliğe dayalı ekranlarınızın geri kalanını taşımaya devam edin ve her iterasyondan sonra test yapın.

Gezinme bileşenini entegre etme

Parça tabanlı bir mimari kullanmaya başladığınızda Gezinme bileşenini entegre etmeye hazırsınız demektir.

Öncelikle, Gezinme kitaplığı sürüm notlarındaki talimatları uygulayarak projenize en yeni Gezinme bağımlılıklarını ekleyin.

Gezinme grafiği oluşturma

Gezinme bileşeni, uygulamanızın görünümlerinin temsil edilmesi gibi, uygulamanızın kaynak dosyasındaki gezinme yapılandırmasını grafik olarak temsil eder. Bu, uygulamanızın gezinme yapısını kod tabanınızın dışında düzenli tutmaya yardımcı olur ve uygulamanızda gezinmeyi görsel olarak düzenlemenizi sağlar.

Gezinme grafiği oluşturmak için navigation adında yeni bir kaynak klasörü oluşturarak başlayın. Grafiği eklemek için bu dizini sağ tıklayın ve Yeni > Gezinme kaynak dosyası'nı seçin.

Gezinme bileşeni, gezinme için ana makine olarak bir etkinlik kullanır ve kullanıcılarınız uygulamanızda gezinirken bağımsız parçaları bu ana makineyle değiştirir. Uygulamanızın gezinme menüsünü görsel olarak düzenlemeye başlamadan önce, etkinliğin içinde bu grafiği barındıracak bir NavHost yapılandırmanız gerekir. Parçalar kullandığımız için Gezinme bileşeninin varsayılan NavHost uygulaması olan NavHostFragment'ı kullanabiliriz.

NavHostFragment, aşağıdaki örnekte gösterildiği gibi bir ana makine etkinliğinin içine yerleştirilen bir FragmentContainerView aracılığıyla yapılandırılır:

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

app:NavGraph özelliği, bu gezinme ana makinesiyle ilişkilendirilmiş gezinme grafiğine işaret eder. Bu özelliğin ayarlanması gezinme grafiğini genişletir ve NavHostFragment üzerindeki grafik özelliğini ayarlar. app:defaultNavHost özelliği, NavHostFragment öğenizin sistemdeki Geri düğmesine müdahale etmesini sağlar.

DrawerLayout veya BottomNavigationView gibi üst düzey gezinme kullanıyorsanız bu FragmentContainerView ana içerik görüntüleme öğenizin yerini alır. Örnekler için NavigationUI ile kullanıcı arayüzü bileşenlerini güncelleme bölümüne bakın.

Basit bir düzen için bu FragmentContainerView öğesini, kök ViewGroup öğesinin alt öğesi olarak ekleyebilirsiniz:

<FrameLayout
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_height="match_parent"
   android:layout_width="match_parent">

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

</FrameLayout>

Alttaki Tasarım sekmesini tıklarsanız aşağıda gösterilene benzer bir grafik görürsünüz. Grafiğin sol üst kısmındaki Hedefler bölümünün altında, NavHost etkinliğine layout_name (resource_id) biçiminde bir referans görebilirsiniz.

Parçalarınızı bu grafiğe eklemek için üst kısımdaki artı düğmesini tıklayın.

Gezinme bileşeni, bağımsız ekranları hedefler olarak ifade eder. Hedefler parçalar, etkinlikler veya özel hedefler olabilir. Grafiğinize herhangi bir türde hedef ekleyebilirsiniz ancak bir etkinlik hedefine gittiğinizde ayrı bir gezinme ana makinesi ve grafiği içinde çalıştığınız için etkinlik hedeflerinin terminal hedefler olarak kabul edildiğini unutmayın.

Gezinme bileşeni, kullanıcıların bir varış noktasından diğerine eylemler olarak ulaşma yöntemidir. İşlemler de geçiş animasyonlarını ve pop davranışını açıklayabilir.

Parça kaldırma işlemleri

Artık Gezinme bileşenini kullandığınıza göre, aynı etkinlik altındaki parçaya dayalı ekranlar arasında geziniyorsanız FragmentManager etkileşimlerini kaldırabilirsiniz.

Uygulamanız aynı etkinlik veya çekmece düzeni ya da alt gezinme gibi üst düzey gezinme altında birden fazla parça kullanıyorsa kullanıcı arayüzünüzün ana içerik bölümündeki parçaları eklemek veya değiştirmek için muhtemelen FragmentManager ve FragmentTransactions kullanıyorsunuzdur. Grafiğinizdeki hedefleri bağlamak için işlemler sağlayıp NavController kullanarak gezinme olanağı sağlayarak bu artık Gezinme bileşeni ile değiştirilebilir ve basitleştirilebilir.

Karşılaşabileceğiniz birkaç senaryoyu ve her senaryo için taşıma yaklaşımını nasıl uygulayabileceğinizi burada bulabilirsiniz.

Birden fazla parçayı yöneten tek bir etkinlik

Birden çok parçayı yöneten tek bir etkinliğiniz varsa etkinlik kodunuz aşağıdaki gibi görünebilir:

Kotlin

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Logic to load the starting destination
        //  when the Activity is first created
        if (savedInstanceState == null) {
            val fragment = ProductListFragment()
            supportFragmentManager.beginTransaction()
                    .add(R.id.fragment_container, fragment, ProductListFragment.TAG)
                    .commit()
        }
    }

    // Logic to navigate the user to another destination.
    // This may include logic to initialize and set arguments on the destination
    // fragment or even transition animations between the fragments (not shown here).
    fun navigateToProductDetail(productId: String) {
        val fragment = new ProductDetailsFragment()
        val args = Bundle().apply {
            putInt(KEY_PRODUCT_ID, productId)
        }
        fragment.arguments = args

        supportFragmentManager.beginTransaction()
                .addToBackStack(ProductDetailsFragment.TAG)
                .replace(R.id.fragment_container, fragment, ProductDetailsFragment.TAG)
                .commit()
    }
}

Java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Logic to load the starting destination when the activity is first created.
        if (savedInstanceState == null) {
            val fragment = ProductListFragment()
            supportFragmentManager.beginTransaction()
                    .add(R.id.fragment_container, fragment, ProductListFragment.TAG)
                    .commit();
        }
    }

    // Logic to navigate the user to another destination.
    // This may include logic to initialize and set arguments on the destination
    // fragment or even transition animations between the fragments (not shown here).
    public void navigateToProductDetail(String productId) {
        Fragment fragment = new ProductDetailsFragment();
        Bundle args = new Bundle();
        args.putInt(KEY_PRODUCT_ID, productId);
        fragment.setArguments(args);

        getSupportFragmentManager().beginTransaction()
                .addToBackStack(ProductDetailsFragment.TAG)
                .replace(R.id.fragment_container, fragment, ProductDetailsFragment.TAG)
                .commit();
    }
}

Aşağıda gösterildiği gibi, kaynak hedefin içinde bir etkinliğe yanıt olarak bir gezinme işlevi çağırıyor olabilirsiniz:

Kotlin

class ProductListFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // In this example a callback is passed to respond to an item clicked
        //  in a RecyclerView
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)
    }
    ...

    // The callback makes the call to the activity to make the transition.
    private val productClickCallback = ProductClickCallback { product ->
            (requireActivity() as MainActivity).navigateToProductDetail(product.id)
    }
}

Java

public class ProductListFragment extends Fragment  {
    ...
    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {
    // In this example a callback is passed to respond to an item clicked in a RecyclerView
        productAdapter = new ProductAdapter(productClickCallback);
        binding.productsList.setAdapter(productAdapter);
    }
    ...

    // The callback makes the call to the activity to make the transition.
    private ProductClickCallback productClickCallback = product -> (
        ((MainActivity) requireActivity()).navigateToProductDetail(product.getId())
    );
}

Bunun yerine, başlangıç hedefini ve hedeflerinizi bağlamak üzere işlemleri belirlemek ve gerektiğinde bağımsız değişkenler tanımlamak için gezinme grafiğinizi güncelleyebilirsiniz:

<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/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List"
        tools:layout="@layout/product_list">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_detail" />
    </fragment>
    <fragment
        android:id="@+id/product_detail"
        android:name="com.example.android.persistence.ui.ProductDetailFragment"
        android:label="Product Detail"
        tools:layout="@layout/product_detail">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>
</navigation>

Ardından, etkinliğinizi güncelleyebilirsiniz:

Kotlin

class MainActivity : AppCompatActivity() {

    // No need to load the start destination, handled automatically by the Navigation component
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

Java

public class MainActivity extends AppCompatActivity {

    // No need to load the start destination, handled automatically by the Navigation component
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Etkinlik için artık bir navigateToProductDetail() yöntemi gerekmiyor. Bir sonraki bölümde, bir sonraki ürün ayrıntıları ekranına gitmek için NavController değerini kullanacak şekilde ProductListFragment uygulamasını güncelliyoruz.

Bağımsız değişkenleri güvenli bir şekilde iletme

Gezinme bileşeni, hedefler ve işlemler için belirtilen bağımsız değişkenlere tür güvenli erişim için basit nesne ve oluşturucu sınıfları oluşturan Safe Args adlı bir Gradle eklentisine sahiptir.

Eklenti uygulandıktan sonra, gezinme grafiğinizdeki bir hedefte tanımlanan bağımsız değişkenler, Gezinme bileşeni çerçevesinin hedef hedefe tür güvenli bağımsız değişkenler sağlayan bir Arguments sınıfı oluşturmasına neden olur. Bir işlem tanımlanması, eklentinin bir Directions yapılandırma sınıfı oluşturmasına neden olur. Bu sınıf, NavController öğesine kullanıcıyı hedef hedefe nasıl yönlendireceğini bildirmek için kullanılabilir. Bir işlem, bağımsız değişken gerektiren bir hedefe işaret ettiğinde oluşturulan Directions sınıfı, bu parametreleri gerektiren oluşturucu yöntemleri içerir.

Aşağıdaki örnekte gösterildiği gibi, parçanın içinde NavController ve oluşturulan Directions sınıfını kullanarak hedef hedefe tür güvenli bağımsız değişkenler sağlayın:

Kotlin

class ProductListFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // In this example a callback is passed to respond to an item clicked in a RecyclerView
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)
    }
    ...

    // The callback makes the call to the NavController to make the transition.
    private val productClickCallback = ProductClickCallback { product ->
        val directions = ProductListDirections.navigateToProductDetail(product.id)
        findNavController().navigate(directions)
    }
}

Java

public class ProductListFragment extends Fragment  {
    ...
    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {
        // In this example a callback is passed to respond to an item clicked in a RecyclerView
        productAdapter = new ProductAdapter(productClickCallback);
        binding.productsList.setAdapter(productAdapter);
    }
    ...

    // The callback makes the call to the activity to make the transition.
    private ProductClickCallback productClickCallback = product -> {
        ProductListDirections.ViewProductDetails directions =
                ProductListDirections.navigateToProductDetail(product.getId());
        NavHostFragment.findNavController(this).navigate(directions);
    };
}

Üst Düzey Gezinme

Uygulamanız DrawerLayout kullanıyorsa etkinliğinizde çekmeceyi açıp kapatmayı ve diğer hedeflere gitmeyi yöneten birçok yapılandırma mantığınız olabilir.

Sonuçta elde edilen etkinlik aşağıdaki gibi görünebilir:

Kotlin

class MainActivity : AppCompatActivity(),
    NavigationView.OnNavigationItemSelectedListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val toolbar: Toolbar = findViewById(R.id.toolbar)
        setSupportActionBar(toolbar)

        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        val navView: NavigationView = findViewById(R.id.nav_view)
        val toggle = ActionBarDrawerToggle(
                this,
                drawerLayout,
                toolbar,
                R.string.navigation_drawer_open, 
                R.string.navigation_drawer_close
        )
        drawerLayout.addDrawerListener(toggle)
        toggle.syncState()

        navView.setNavigationItemSelectedListener(this)
    }

    override fun onBackPressed() {
        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
            drawerLayout.closeDrawer(GravityCompat.START)
        } else {
            super.onBackPressed()
        }
    }

    override fun onNavigationItemSelected(item: MenuItem): Boolean {
        // Handle navigation view item clicks here.
        when (item.itemId) {
            R.id.home -> {
                val homeFragment = HomeFragment()
                show(homeFragment)
            }
            R.id.gallery -> {
                val galleryFragment = GalleryFragment()
                show(galleryFragment)
            }
            R.id.slide_show -> {
                val slideShowFragment = SlideShowFragment()
                show(slideShowFragment)
            }
            R.id.tools -> {
                val toolsFragment = ToolsFragment()
                show(toolsFragment)
            }
        }
        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        drawerLayout.closeDrawer(GravityCompat.START)
        return true
    }
}

private fun show(fragment: Fragment) {

    val drawerLayout = drawer_layout as DrawerLayout
    val fragmentManager = supportFragmentManager

    fragmentManager
            .beginTransaction()
            .replace(R.id.main_content, fragment)
            .commit()

    drawerLayout.closeDrawer(GravityCompat.START)
}

Java

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this,
                drawer,
                toolbar,
                R.string.navigation_drawer_open,
                R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        navigationView.setNavigationItemSelectedListener(this);
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.home) {
            Fragment homeFragment = new HomeFragment();
            show(homeFragment);
        } else if (id == R.id.gallery) {
            Fragment galleryFragment = new GalleryFragment();
            show(galleryFragment);
        } else if (id == R.id.slide_show) {
            Fragment slideShowFragment = new SlideShowFragment();
            show(slideShowFragment);
        } else if (id == R.id.tools) {
            Fragment toolsFragment = new ToolsFragment();
            show(toolsFragment);
        }

        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

    private void show(Fragment fragment) {

        DrawerLayout drawerLayout = findViewById(R.id.drawer_layout);
        FragmentManager fragmentManager = getSupportFragmentManager();

        fragmentManager
                .beginTransaction()
                .replace(R.id.main_content, fragment)
                .commit();

        drawerLayout.closeDrawer(GravityCompat.START);
    }
}

Projenize Gezinme bileşenini ekledikten ve bir gezinme grafiği oluşturduktan sonra, grafiğinizdeki içerik hedeflerinin her birini (yukarıdaki örnekte bulunan Ana Sayfa, Galeri, Slayt Gösterisi ve Araçlar gibi) ekleyin. Menü öğesi id değerlerinizin, aşağıda gösterildiği gibi ilişkili hedef id değerleriyle eşleştiğinden emin olun:

<!-- activity_main_drawer.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:showIn="navigation_view">

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/home"
            android:icon="@drawable/ic_menu_camera"
            android:title="@string/menu_home" />
        <item
            android:id="@+id/gallery"
            android:icon="@drawable/ic_menu_gallery"
            android:title="@string/menu_gallery" />
        <item
            android:id="@+id/slide_show"
            android:icon="@drawable/ic_menu_slideshow"
            android:title="@string/menu_slideshow" />
        <item
            android:id="@+id/tools"
            android:icon="@drawable/ic_menu_manage"
            android:title="@string/menu_tools" />
    </group>
</menu>
<!-- activity_main_graph.xml -->
<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/main_graph"
    app:startDestination="@id/home">

    <fragment
        android:id="@+id/home"
        android:name="com.example.HomeFragment"
        android:label="Home"
        tools:layout="@layout/home" />

    <fragment
        android:id="@+id/gallery"
        android:name="com.example.GalleryFragment"
        android:label="Gallery"
        tools:layout="@layout/gallery" />

    <fragment
        android:id="@+id/slide_show"
        android:name="com.example.SlideShowFragment"
        android:label="Slide Show"
        tools:layout="@layout/slide_show" />

    <fragment
        android:id="@+id/tools"
        android:name="com.example.ToolsFragment"
        android:label="Tools"
        tools:layout="@layout/tools" />

</navigation>

Menünüzdeki ve grafiğinizdeki id değerlerini eşleştirirseniz bu etkinlik için, gezinmeyi menü öğesine göre otomatik olarak işlemek için NavController parametresini bağlayabilirsiniz. NavController ayrıca DrawerLayout öğesinin açılıp kapatılmasını, Yukarı ve Geri düğmesinin davranışını uygun şekilde ele alır.

Ardından MainActivity cihazınız, NavController cihazını Toolbar ve NavigationView'e bağlamak için güncellenebilir.

Örnek için aşağıdaki snippet'i inceleyin:

Kotlin

class MainActivity : AppCompatActivity()  {

    val drawerLayout by lazy { findViewById<DrawerLayout>(R.id.drawer_layout) }
    val navController by lazy {
      (supportFragmentManager.findFragmentById(R.id.main_content) as NavHostFragment).navController
    }
    val navigationView by lazy { findViewById<NavigationView>(R.id.nav_view) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        setSupportActionBar(toolbar)

        // Show and Manage the Drawer and Back Icon
        setupActionBarWithNavController(navController, drawerLayout)

        // Handle Navigation item clicks
        // This works with no further action on your part if the menu and destination id’s match.
        navigationView.setupWithNavController(navController)

    }

    override fun onSupportNavigateUp(): Boolean {
        // Allows NavigationUI to support proper up navigation or the drawer layout
        // drawer menu, depending on the situation
        return navController.navigateUp(drawerLayout)
    }
}

Java

public class MainActivity extends AppCompatActivity {

    private DrawerLayout drawerLayout;
    private NavController navController;
    private NavigationView navigationView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        drawerLayout = findViewById(R.id.drawer_layout);
        NavHostFragment navHostFragment = (NavHostFragment)
            getSupportFragmentManager().findFragmentById(R.id.main_content);
        navController = navHostFragment.getNavController();
        navigationView = findViewById(R.id.nav_view);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // Show and Manage the Drawer and Back Icon
        NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);

        // Handle Navigation item clicks
        // This works with no further action on your part if the menu and destination id’s match.
        NavigationUI.setupWithNavController(navigationView, navController);

    }

    @Override
    public boolean onSupportNavigateUp() {
        // Allows NavigationUI to support proper up navigation or the drawer layout
        // drawer menu, depending on the situation.
        return NavigationUI.navigateUp(navController, drawerLayout);

    }
}

Bu tekniği hem Bottom NavigationView tabanlı gezinme hem de Menü tabanlı gezinme için kullanabilirsiniz. Daha fazla örnek için NavigationUI ile kullanıcı arayüzü bileşenlerini güncelleme bölümüne bakın.

Aktivite hedefleri ekleyin

Uygulamanızdaki her ekran Gezinme bileşenini kullanacak şekilde bağlandıktan ve parça tabanlı hedefler arasında geçiş için artık FragmentTransactions uygulamasını kullanmadığınızda, bir sonraki adım startActivity çağrılarını ortadan kaldırmaktır.

Öncelikle, uygulamanızda iki ayrı gezinme grafiğinizin olduğu ve bunlar arasında geçiş yapmak için startActivity kullandığınız yerleri belirleyin.

Bu örnekte, iki grafik (A ve B) ve A'dan B'ye geçiş için bir startActivity() çağrısı bulunmaktadır.

Kotlin

fun navigateToProductDetails(productId: String) {
    val intent = Intent(this, ProductDetailsActivity::class.java)
    intent.putExtra(KEY_PRODUCT_ID, productId)
    startActivity(intent)
}

Java

private void navigateToProductDetails(String productId) {
    Intent intent = new Intent(this, ProductDetailsActivity.class);
    intent.putExtra(KEY_PRODUCT_ID, productId);
    startActivity(intent);

Daha sonra, bunları Grafik A'da Grafik B'nin ana makine etkinliğine gitmeyi temsil eden bir etkinlik hedefiyle değiştirin. Grafik B'nin başlangıç hedefine aktarılacak bağımsız değişkenleriniz varsa bunları etkinlik hedefi tanımında belirtebilirsiniz.

Aşağıdaki örnekte Grafik A, işlemle birlikte product_id bağımsız değişkenini alan bir etkinlik hedefi tanımlar. B grafiği değişiklik içermiyor.

A ve B Grafiklerinin XML gösterimi aşağıdaki gibi görünebilir:

<!-- Graph A -->
<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/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List"
        tools:layout="@layout/product_list_fragment">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_details_activity" />
    </fragment>

    <activity
        android:id="@+id/product_details_activity"
        android:name="com.example.android.persistence.ui.ProductDetailsActivity"
        android:label="Product Details"
        tools:layout="@layout/product_details_host">

        <argument
            android:name="product_id"
            app:argType="integer" />

    </activity>

</navigation>
<!-- Graph B -->
<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"
    app:startDestination="@id/product_details">

    <fragment
        android:id="@+id/product_details"
        android:name="com.example.android.persistence.ui.ProductDetailsFragment"
        android:label="Product Details"
        tools:layout="@layout/product_details_fragment">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>

</navigation>

Parça hedeflerine gitmek için kullandığınız mekanizmaları kullanarak Grafik B'nin ana makine etkinliğine gidebilirsiniz:

Kotlin

fun navigateToProductDetails(productId: String) {
    val directions = ProductListDirections.navigateToProductDetail(productId)
    findNavController().navigate(directions)
}

Java

private void navigateToProductDetails(String productId) {
    ProductListDirections.NavigateToProductDetail directions =
            ProductListDirections.navigateToProductDetail(productId);
    Navigation.findNavController(getView()).navigate(directions);

Etkinlik hedefi bağımsız değişkenlerini bir başlangıç hedef parçasına geçirme

Önceki örnekte olduğu gibi hedef etkinliği ekstralar alırsa bunları doğrudan başlangıç hedefine bağımsız değişken olarak aktarabilirsiniz, ancak ana makine etkinliğinin onCreate() yöntemi içinde ana makinenizin gezinme grafiğini manuel olarak ayarlamanız gerekir. Böylece, niyet ekstralarını parçaya aşağıda gösterildiği gibi bağımsız değişkenler olarak aktarabilirsiniz:

Kotlin

class ProductDetailsActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.product_details_host)
        val navHostFragment = supportFragmentManager.findFragmentById(R.id.main_content) as NavHostFragment
        val navController = navHostFramgent.navController
        navController
                .setGraph(R.navigation.product_detail_graph, intent.extras)
    }

}

Java

public class ProductDetailsActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.product_details_host);
        NavHostFragment navHostFragment = (NavHostFragment)
            getSupportFragmentManager().findFragmentById(R.id.main_content);
        NavController navController = navHostFragment.getNavController();
        navController
                .setGraph(R.navigation.product_detail_graph, getIntent().getExtras());
    }

}

Veriler, aşağıdaki örnekte gösterildiği gibi, oluşturulan bağımsız değişken sınıfı kullanılarak Bundle parça bağımsız değişkenlerinden alınabilir:

Kotlin

class ProductDetailsFragment : Fragment() {

    val args by navArgs<ProductDetailsArgs>()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val productId = args.productId
        ...
    }
    ...

Java

public class ProductDetailsFragment extends Fragment {

    ProductDetailsArgs args;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        args = ProductDetailsArgs.fromBundle(requireArguments());
    }

    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {
       int productId = args.getProductId();
       ...
    }
    ...

Etkinlikleri birleştir

Birden fazla etkinliğin aynı düzeni paylaştığı durumlarda (ör. tek bir parça içeren basit bir FrameLayout öğesi) gezinme grafiklerini birleştirebilirsiniz. Bu durumların çoğunda, her bir gezinme grafiğindeki tüm öğeleri birleştirebilir ve etkinlik hedefi öğelerini parçalı hedeflere güncelleyebilirsiniz.

Aşağıdaki örnekte, önceki bölümde bulunan A ve B Grafikleri birleştirilmektedir:

Birleştirmeden önce:

<!-- Graph A -->
<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/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List Fragment"
        tools:layout="@layout/product_list">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_details_activity" />
    </fragment>
    <activity
        android:id="@+id/product_details_activity"
        android:name="com.example.android.persistence.ui.ProductDetailsActivity"
        android:label="Product Details Host"
        tools:layout="@layout/product_details_host">
        <argument android:name="product_id"
            app:argType="integer" />
    </activity>

</navigation>
<!-- Graph B -->
<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/product_detail_graph"
    app:startDestination="@id/product_details">

    <fragment
        android:id="@+id/product_details"
        android:name="com.example.android.persistence.ui.ProductDetailsFragment"
        android:label="Product Details"
        tools:layout="@layout/product_details">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>
</navigation>

Birleştirdikten sonra:

<!-- Combined Graph A and B -->
<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/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List Fragment"
        tools:layout="@layout/product_list">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_details" />
    </fragment>

    <fragment
        android:id="@+id/product_details"
        android:name="com.example.android.persistence.ui.ProductDetailsFragment"
        android:label="Product Details"
        tools:layout="@layout/product_details">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>

</navigation>

Birleştirme sırasında işlem adlarınızı aynı tutmak, mevcut kod tabanınızda değişiklik gerektirmeden sorunsuz bir işlem yapılmasını sağlayabilir. Örneğin, navigateToProductDetail burada değişmeden kalır. Tek fark, bu işlemin artık bir etkinlik hedefi yerine aynı NavHost içindeki parça hedefe gitmeyi temsil etmesidir:

Kotlin

fun navigateToProductDetails(productId: String) {
    val directions = ProductListDirections.navigateToProductDetail(productId)
    findNavController().navigate(directions)
}

Java

private void navigateToProductDetails(String productId) {
    ProductListDirections.NavigateToProductDetail directions =
            ProductListDirections.navigateToProductDetail(productId);
    Navigation.findNavController(getView()).navigate(directions);

Ek Kaynaklar

Gezinmeyle ilgili daha fazla bilgi için aşağıdaki konulara bakın: