Duyarlı gezinme oluşturma

Gezinme, kullanıcıların içerik hedeflerine erişmek için bir uygulamanın kullanıcı arayüzüyle etkileşimidir. Android'in gezinme ilkeleri, tutarlı ve sezgisel bir uygulama gezinme deneyimi oluşturmanıza yardımcı olacak yönergeler sağlar.

Duyarlı/uyarlanabilir kullanıcı arayüzleri, duyarlı içerik hedefleri sağlar ve genellikle ekran boyutu değişikliklerine yanıt olarak farklı türde gezinme öğeleri içerir (ör. küçük ekranlarda alt gezinme çubuğu, orta boy ekranlarda gezinme çubuğu veya büyük ekranlarda kalıcı gezinme çekmecesi). Ancak duyarlı/uyarlanabilir kullanıcı arayüzleri, gezinme ilkelerine uymaya devam etmelidir.

Jetpack Gezinme bileşeni, gezinme ilkelerini uygular ve duyarlı/uyarlanabilir kullanıcı arayüzlerine sahip uygulamaların geliştirilmesini kolaylaştırır.

Şekil 1. Gezinme çekmecesi, çubuğu ve alt çubuğu içeren genişletilmiş, orta ve kompakt ekranlar.

Duyarlı kullanıcı arayüzü gezinme

Bir uygulamanın kapladığı ekran penceresinin boyutu, ergonomiyi ve kullanılabilirliği etkiler. Pencere boyutu sınıfları, uygun gezinme öğelerini (ör. gezinme çubukları, raylar veya çekmeceler) belirlemenizi ve bunları kullanıcının en kolay erişebileceği yere yerleştirmenizi sağlar. Materyal Tasarım düzenleme kurallarına göre gezinme öğeleri, ekranın ön kenarında kalıcı bir alan kaplar ve uygulamanın genişliği kompakt olduğunda alt kenara taşınabilir. Gezinme öğesi seçiminiz büyük ölçüde uygulama penceresinin boyutuna ve öğenin barındırması gereken öğe sayısına bağlıdır.

Pencere boyutu sınıfı Az sayıda öğe Çok sayıda öğe
kompakt genişlik alt gezinme çubuğu gezinme çekmecesi (ön kenar veya alt kısım)
orta genişlik gezinme çubuğu gezinme çekmecesi (ön kenar)
genişletilmiş genişlik gezinme çubuğu kalıcı gezinme çekmecesi (ön kenar)

Düzen kaynak dosyaları, farklı görüntü boyutları için farklı gezinme öğeleri kullanmak üzere pencere boyutu sınıfı kesme noktalarına göre tanımlanabilir.

<!-- res/layout/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>

<!-- res/layout-w600dp/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.navigationrail.NavigationRailView
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>

<!-- res/layout-w1240dp/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.navigation.NavigationView
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>

Uyumlu içerik hedefleri

Duyarlı kullanıcı arayüzünde her içerik hedefine ait düzen, pencere boyutundaki değişikliklere uyum sağlar. Uygulamanız, düzen aralığını ayarlayabilir, öğeleri yeniden konumlandırabilir, içerik ekleyebilir veya kaldırabilir ya da gezinme öğeleri dahil olmak üzere kullanıcı arayüzü öğelerini değiştirebilir.

Her hedef, yeniden boyutlandırma etkinliklerini işlediğinde değişiklikler kullanıcı arayüzünde izole edilir. Navigasyon da dahil olmak üzere uygulama durumunun geri kalanı bu durumdan etkilenmez.

Gezinme, pencere boyutu değişikliklerinin bir yan etkisi olarak gerçekleşmemelidir. Yalnızca farklı pencere boyutlarına uyum sağlamak için içerik hedefleri oluşturmayın. Örneğin, katlanabilir bir cihazın farklı ekranları için farklı içerik hedefleri oluşturmayın.

Pencere boyutu değişikliklerinin bir yan etkisi olarak içerik hedeflerine gitmek aşağıdaki sorunlara neden olur:

  • Yeni hedefe gitmeden önce eski hedef (önceki pencere boyutu için) kısa bir süre görünür olabilir
  • Geri döndürülebilirliği korumak için (ör. bir cihaz katlandığında ve açıldığında) her pencere boyutu için gezinme gerekir.
  • Gezinme, arka yığının açılması üzerine durumu bozabileceğinden, hedefler arasında uygulama durumunu korumak zor olabilir.

Ayrıca, pencere boyutu değiştiğinde uygulamanız ön planda bile olmayabilir. Uygulamanızın düzeni, ön plan uygulamasından daha fazla alan gerektirebilir. Kullanıcı uygulamanıza geri döndüğünde yön ve pencere boyutu değişmiş olabilir.

Uygulamanız, pencere boyutuna göre benzersiz içerik hedefleri gerektiriyorsa ilgili hedefleri alternatif, uyarlanabilir düzenler içeren tek bir hedefte birleştirebilirsiniz.

Alternatif düzenlere sahip içerik hedefleri

Duyarlı/uyarlanabilir tasarımın bir parçası olarak tek bir gezinme hedefi, uygulama penceresi boyutuna bağlı olarak alternatif düzenlere sahip olabilir. Her düzen pencerenin tamamını kaplar ancak farklı pencere boyutları için farklı düzenler sunulur (uyarlanabilir tasarım).

Liste-ayrıntı görünümü, bu tür bir görünüme örnek olarak verilebilir. Kompakt pencere boyutlarında uygulamanız, liste için bir içerik düzeni ve ayrıntılar için bir içerik düzeni gösterir. Liste ayrıntıları görünümüne gittiğinizde başlangıçta yalnızca liste düzeni gösterilir. Bir liste öğesi seçildiğinde uygulamanız, listenin yerini alarak ayrıntı düzenini gösterir. Geri kontrolü seçildiğinde ayrıntının yerine liste düzeni gösterilir. Ancak genişletilmiş pencere boyutlarında liste ve ayrıntı düzenleri yan yana gösterilir.

SlidingPaneLayout, büyük ekranlarda iki içerik bölmesini yan yana, geleneksel telefonlar gibi küçük ekranlarda ise aynı anda yalnızca bir bölmeyi gösteren tek bir gezinme hedefi oluşturmanıza olanak tanır.

<!-- Single destination for list and detail. -->

<navigation ...>

    <!-- Fragment that implements SlidingPaneLayout. -->
    <fragment
        android:id="@+id/article_two_pane"
        android:name="com.example.app.ListDetailTwoPaneFragment" />

    <!-- Other destinations... -->
</navigation>

SlidingPaneLayout kullanarak liste-ayrıntı düzeni uygulamayla ilgili ayrıntılar için İki bölmeli düzen oluşturma başlıklı makaleyi inceleyin.

Bir gezinme grafiği

Herhangi bir cihazda veya pencere boyutunda tutarlı bir kullanıcı deneyimi sunmak için her içerik hedefine ait düzenin duyarlı olduğu tek bir gezinme grafiği kullanın.

Her pencere boyutu sınıfı için farklı bir gezinme grafiği kullanıyorsanız uygulama bir boyut sınıfından diğerine geçtiğinde kullanıcının diğer grafiklerdeki mevcut hedefini belirlemeniz, geri yığın oluşturmanız ve grafikler arasında farklı olan durum bilgilerini uyumlu hale getirmeniz gerekir.

İç içe yerleştirilmiş gezinme ana makinesi

Uygulamanız, kendi içerik hedeflerine sahip bir içerik hedefi içerebilir. Örneğin, liste ayrıntıları düzeninde öğe ayrıntıları bölmesi, öğe ayrıntısının yerini alan içeriğe giden kullanıcı arayüzü öğeleri içerebilir.

Bu tür bir alt gezinmeyi uygulamak için ayrıntı bölmesini, ayrıntı bölmesinden erişilen hedefleri belirten kendi gezinme grafiğine sahip iç içe yerleştirilmiş bir gezinme ana makinesi haline getirin:

<!-- layout/two_pane_fragment.xml -->

<androidx.slidingpanelayout.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list_pane"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"/>

    <!-- Detail pane is a nested navigation host. Its graph is not connected
         to the main graph that contains the two_pane_fragment destination. -->
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/detail_pane"
        android:layout_width="300dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/detail_pane_nav_graph" />
</androidx.slidingpanelayout.widget.SlidingPaneLayout>

İç içe yerleştirilmiş NavHost öğesinin gezinme grafiği ana gezinme grafiğine bağlı olmadığı için bu, iç içe yerleştirilmiş gezinme grafiğinden farklıdır. Yani bir grafikteki hedeflerden doğrudan diğer grafikteki hedeflere gidemezsiniz.

Daha fazla bilgi için İç içe yerleştirilmiş gezinme grafikleri başlıklı makaleyi inceleyin.

Korunmuş durum

Uygulamanızın, cihaz döndürüldüğünde, katlandığında veya uygulama penceresi yeniden boyutlandırıldığında durumunu koruması gerekir. Varsayılan olarak bu tür yapılandırma değişiklikleri, uygulamanın etkinliklerini, parçalarını ve görüntüleme hiyerarşisini yeniden oluşturur. Kullanıcı arayüzü durumunu kaydetmenin önerilen yolu, yapılandırma değişikliklerinde korunan bir ViewModel kullanmaktır. (Kullanıcı arayüzü durumlarını kaydetme başlıklı makaleyi inceleyin.)

Boyut değişiklikleri geri alınabilir olmalıdır. Örneğin, kullanıcı cihazı döndürüp tekrar döndürdüğünde.

Duyarlı/uyarlanabilir düzenler farklı pencere boyutlarında farklı içerikler gösterebilir. Bu nedenle, duyarlı düzenler genellikle mevcut pencere boyutu için geçerli olmasa bile içerikle ilgili ek durum bilgisi kaydetmelidir. Örneğin, bir düzende yalnızca daha geniş pencere genişliklerinde ek bir kaydırılabilir widget gösterilecek alan olabilir. Bir yeniden boyutlandırma etkinliği, pencere genişliğinin çok küçük olmasına neden olursa widget gizlenir. Uygulama önceki boyutlarına yeniden ayarlandığında kaydırma widget'ı tekrar görünür hale gelir ve orijinal kaydırma konumu geri yüklenir.

ViewModel kapsamları

Gezinme bileşenine taşıma geliştirici kılavuzunda, hedeflerin parça olarak uygulandığı ve veri modellerinin ViewModel kullanılarak uygulandığı tek etkinlikli bir mimari önerilmektedir.

ViewModel her zaman bir yaşam döngüsü kapsamına alınır ve bu yaşam döngüsü kalıcı olarak sona erdiğinde ViewModel temizlenir ve atılabilir. ViewModel'ün kapsamına dahil edildiği yaşam döngüsü (ve dolayısıyla ViewModel'ün ne kadar geniş kapsamlı olarak paylaşılabileceği) ViewModel'ü almak için kullanılan mülk temsilcisine bağlıdır.

En basit durumda, her gezinme hedefi tamamen izole bir kullanıcı arayüzü durumuna sahip tek bir parçadır. Bu nedenle, her parça, ilgili parçaya özel bir ViewModel elde etmek için viewModels() mülk temsilcisini kullanabilir.

Kullanıcı arayüzü durumunu parçalar arasında paylaşmak için parçalarda activityViewModels()'i çağırarak ViewModel'ü etkinliğe göre kapsamlandırın (Activity için eşdeğeri yalnızca viewModels()'tir). Bu, etkinliğin ve kendisine eklenen tüm parçaların ViewModel örneğini paylaşmasına olanak tanır. Ancak tek etkinlikli bir mimaride bu ViewModel kapsamı, uygulama kadar etkili bir şekilde sürer. Bu nedenle, hiçbir parça kullanmasa bile ViewModel bellekte kalır.

Gezinme grafiğinizin, ödeme akışını temsil eden bir dizi parça hedefi olduğunu ve ödeme deneyiminin tamamının mevcut durumunun, parçalar arasında paylaşılan bir ViewModel içinde olduğunu varsayalım. ViewModel etkinliğine yönelik kapsamın çok geniş olması yalnızca çok geniş olmakla kalmaz, aynı zamanda başka bir sorunu da ortaya çıkarır: Kullanıcı bir sipariş için ödeme akışında ilerler ve ardından ikinci bir sipariş için bu akışı tekrar kullanırsa her iki sipariş de ödeme ViewModel etkinliğinin aynı örneğini kullanır. İkinci siparişin ödemesini yapmadan önce ilk siparişteki verileri manuel olarak temizlemeniz gerekir. Hatalar kullanıcı için maliyetli olabilir.

Bunun yerine, ViewModel'ü mevcut NavController içindeki bir gezinme grafiğiyle sınırlandırın. Ödeme akışının bir parçası olan hedefleri kapsayacak şekilde iç içe yerleştirilmiş bir gezinme grafiği oluşturun. Ardından, bu parça hedeflerin her birinde navGraphViewModels() mülk temsilcisini kullanın ve paylaşılan ViewModel'ı almak için gezinme grafiğinin kimliğini iletin. Bu sayede, kullanıcı ödeme akışından çıktığında ve iç içe yerleştirilmiş gezinme grafiği kapsam dışında kaldığında, ViewModel öğesinin ilgili örneği atılır ve sonraki ödeme için kullanılmaz.

Kapsam Mülk temsilcisi ViewModel şu kullanıcılarla paylaşılabilir:
Parça Fragment.viewModels() Yalnızca parça
Etkinlik Activity.viewModels() veya Fragment.activityViewModels() Etkinlik ve ona bağlı tüm parçalar
Gezinme grafiği Fragment.navGraphViewModels() Aynı gezinme grafiğindeki tüm parçalar

İç içe yerleştirilmiş bir gezinme ana makinesi kullanıyorsanız (İç içe yerleştirilmiş gezinme ana makinesi bölümüne bakın) grafikler bağlı olmadığından, bu ana makinedeki hedeflerin navGraphViewModels() kullanılırken ViewModel örneklerini ana makinenin dışındaki hedeflerle paylaşamayacağını unutmayın. Bu durumda bunun yerine etkinlik kapsamını kullanabilirsiniz.

Ek kaynaklar