Komponen navigasi Android. Hal-hal sederhana yang harus Anda lakukan sendiri

Halo semuanya! Saya ingin berbicara tentang fitur dalam karya Navigasi Arsitektur Komponen , karena itu saya mempunyai kesan ambigu perpustakaan.

Artikel ini bukan panduan langkah demi langkah, ini menghilangkan detail implementasi untuk fokus pada poin-poin utama. Ada banyak kasus penggunaan serupa di Internet (ada juga terjemahan) - mereka akan membantu Anda berkenalan dengan perpustakaan. Juga, sebelum membaca, saya mengusulkan untuk mempelajari dokumentasinya .

gambar

Saya harus mengatakan segera bahwa saya pasti menganggap perpustakaan bermanfaat dan tidak mengecualikan kemungkinan penyalahgunaan, tetapi saya mungkin mencoba semuanya sebelum menulis artikel ini.

Jadi, berikut adalah skenario dalam implementasi yang harapan untuk fungsi tidak sesuai dengan kenyataan dalam implementasi:

  • beralih antar item menu di laci navigasi
  • penemuan Kegiatan baru dengan grafik navigasinya
  • meneruskan parameter ke startDestination

Beralih di antara item menu


Ini adalah salah satu fitur yang mempengaruhi keputusan untuk menggunakan Komponen Navigasi.

Anda hanya perlu membuat id item menu identik

activity_main_drawer.xml
<?xml version="1.0" encoding="utf-8"?> <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/importFragment" android:icon="@drawable/ic_menu_camera" android:title="Import"/> <item android:id="@+id/galleryFragment" android:icon="@drawable/ic_menu_gallery" android:title="Gallery"/> <item android:id="@+id/slideshowFragment" android:icon="@drawable/ic_menu_slideshow" android:title="Slideshow"/> <!--    --> 


dan id layar (tujuan dalam grafik navigasi)

mobile_navigation.xml
 <?xml version="1.0" encoding="utf-8"?> <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/mobile_navigation" app:startDestination="@id/importFragment"> <fragment android:id="@+id/importFragment" android:name="com.xiii.navigationapplication.ImportFragment" android:label="fragment_import" tools:layout="@layout/fragment_import"/> <fragment android:id="@+id/galleryFragment" android:name="com.xiii.navigationapplication.GalleryFragment" android:label="fragment_gallery" tools:layout="@layout/fragment_gallery"/> <fragment android:id="@+id/slideshowFragment" android:name="com.xiii.navigationapplication.SlideshowFragment" android:label="fragment_slideshow" tools:layout="@layout/fragment_slideshow"/> </navigation> 


maka Anda perlu mengaitkan menu dengan pengontrol navigasi:

MainActivity.kt
 class MainActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(toolbar) //   ""  toolbar NavigationUI.setupWithNavController(toolbar, navController, drawer_layout) //      nav_view.setupWithNavController(navController) } //     override fun onSupportNavigateUp() = navController.navigateUp() } 


Navigasi di menu telah berfungsi - bukankah itu keajaiban?!

Navigasi menu

Perhatikan "hamburger" (ikon menu), saat beralih di antara item menu, ia mengubah statusnya menjadi tombol "kembali". Perilaku ini tampak tidak biasa (seperti familier - seperti dalam aplikasi pasar bermain) dan, untuk sementara waktu, saya mencoba mencari tahu apa yang salah?

Itu saja! Setelah membaca dokumentasi tentang prinsip-prinsip navigasi (yaitu, poin dua dan tiga ), saya menyadari bahwa "hamburger" ditampilkan hanya untuk startDestination , atau lebih tepatnya, dengan cara ini: tombol kembali ditampilkan untuk semua orang kecuali startDestination . Situasi dapat diubah dengan menerapkan berbagai trik dalam berlangganan ( addOnNavigatedListener () ) untuk mengubah tujuan , tetapi mereka bahkan tidak boleh dijelaskan. Ini berfungsi seperti ini, Anda harus menyetujui.

Membuka Kegiatan baru


Aktivitas dapat bertindak sebagai host navigasi dan, pada saat yang sama, dalam grafik navigasi dapat bertindak sebagai salah satu tujuan . Membuka suatu Kegiatan tanpa grafik navigasi bersarang berfungsi seperti yang diharapkan , mis. Panggilan:

 navController.navigate(R.id.editActivity) 

akan melakukan transisi (seperti dalam kasus fragmen) dan membuka Aktivitas yang diminta.
Jauh lebih menarik untuk mempertimbangkan kasus ketika Aktivitas target itu sendiri bertindak sebagai host navigasi , yaitu opsi 2 dari dokumentasi :

Skema navigasi

Sebagai contoh, mari kita lihat Kegiatan untuk menambahkan catatan. Ini akan berisi fragmen utama dengan bidang input EditFragment , itu akan menjadi StartDestination dalam grafik navigasi. Mari kita asumsikan bahwa ketika mengedit kita perlu melampirkan foto, untuk ini kita akan pergi ke PhotoFragment untuk mendapatkan gambar dari kamera. Grafik navigasi akan terlihat seperti ini:

edit_navigation.xml
 <?xml version="1.0" encoding="utf-8"?> <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/edit_navigation" app:startDestination="@id/editFragment"> <fragment android:id="@+id/editFragment" android:name="com.xiii.navigationapplication.ui.edit.EditFragment" android:label="fragment_edit" tools:layout="@layout/fragment_edit"> <action android:id="@+id/action_editFragment_to_photoFragment" app:destination="@id/photoFragment"/> </fragment> <fragment android:id="@+id/photoFragment" android:name="com.xiii.navigationapplication.ui.edit.PhotoFragment" android:label="fragment_photo" tools:layout="@layout/fragment_photo"/> </navigation> 


EditActivity tidak jauh berbeda dari MainActivity . Perbedaan utama adalah bahwa tidak ada menu di EditActivity :

EditActivity.kt
 class EditActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit) setSupportActionBar(toolbar) //    ""  toolbar NavigationUI.setupWithNavController(toolbar, navController) } override fun onSupportNavigateUp() = navController.navigateUp() fun takePhoto(view: View) { navController.navigate(R.id.action_editFragment_to_photoFragment) } } 


Aktivitas terbuka, navigasi di dalamnya berfungsi:

Edit navigasi aktivitas

Sekali lagi, perhatikan tombol navigasi di bilah alat - pada awal EditFragment tidak ada tombol "Kembali ke aktivitas Orang Tua " (tapi saya ingin). Dari sudut pandang dokumentasi, semuanya legal di sini: grafik navigasi baru, nilai startDestination baru, tombol "Kembali" tidak ditampilkan di startDestination , akhir.

Bagi mereka yang ingin kembali ke perilaku biasa dengan aktivitas orang tua , sambil mempertahankan fungsionalitas untuk beralih antar fragmen, saya dapat menawarkan pendekatan kruk ini:

1. Tentukan Aktivitas induk dalam manifes
 <activity android:name=".EditActivity" android:parentActivityName=".MainActivity" android:theme="@style/AppTheme.NoActionBar"> <!-- Parent activity meta-data to support 4.0 and lower --> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity" /> </activity> 


2. Tambahkan langganan di mana kami akan mengganti id startDestination
 class EditActivity : AppCompatActivity() { private val navController by lazy(LazyThreadSafetyMode.NONE) { Navigation.findNavController(this, R.id.nav_host_fragment) } private var isStartDestination = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit) setSupportActionBar(toolbar) val startDestinationId = navController.graph.startDestination //    id,  NavigationUI.ActionBarOnNavigatedListener     //      destination    startDestination navController.addOnNavigatedListener { controller, destination -> isStartDestination = destination.id == startDestinationId // R.id.fake_start_destination  id        controller.graph.startDestination = if (isStartDestination) R.id.fake_start_destination else startDestinationId } //    ""  toolbar NavigationUI.setupActionBarWithNavController(this, navController) } override fun onSupportNavigateUp(): Boolean { //  startDestination      Navigation Component return if (isStartDestination) super.onSupportNavigateUp() else navController.navigateUp() } fun takePhoto(view: View) { navController.navigate(R.id.action_editFragment_to_photoFragment) } } 


Berlangganan diperlukan agar untuk NavigationUI.ActionBarOnNavigatedListener semua tujuan tidak startDestination . Dengan demikian, NavigationUI.ActionBarOnNavigatedListener tidak akan menyembunyikan tombol navigasi (lihat sumber untuk detail). Tambahkan ke ini pemrosesan onSupportNavigateUp () secara teratur di startDestination dan dapatkan yang kita inginkan.

Perlu dikatakan bahwa solusinya jauh dari ideal, jika hanya karena itu adalah intervensi yang tidak terlihat dalam perilaku perpustakaan. Saya percaya bahwa masalah dapat muncul ketika menggunakan tautan dalam (saya belum mengujinya).

Melewati parameter ke startDestination


Komponen Navigasi memiliki mekanisme untuk meneruskan parameter dari satu tujuan ke tujuan lain. Bahkan ada alat untuk memastikan keamanan tipe melalui pembuatan kode (tidak buruk).

Sekarang kita akan menganalisis kasus ini, karena itu saya tidak dapat menempatkan lima solid untuk fungsional ini.

Mari kita kembali ke EditActivity , skenario yang cukup akrab ketika satu Kegiatan digunakan untuk membuat dan mengedit objek. Saat Anda membuka objek untuk diedit di Aktivitas, Anda perlu mentransfer, misalnya, id objek - mari kita lakukan secara teratur:

1. Tambahkan parameter ke grafik untuk EditActivity
Saya menambahkan parameter langsung ke elemen root dari grafik (navigasi), tetapi dapat ditambahkan ke fragmen target. Dari ini, hanya metode mendapatkan parameter yang akan berubah.

 <?xml version="1.0" encoding="utf-8"?> <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/edit_navigation" app:startDestination="@id/editFragment"> <argument android:name="id" app:argType="integer"/> <fragment android:id="@+id/editFragment" android:name="com.xiii.navigationapplication.ui.edit.EditFragment" android:label="fragment_edit" tools:layout="@layout/fragment_edit"> <action android:id="@+id/action_editFragment_to_photoFragment" app:destination="@id/photoFragment"/> </fragment> <fragment android:id="@+id/photoFragment" android:name="com.xiii.navigationapplication.ui.edit.PhotoFragment" android:label="fragment_photo" tools:layout="@layout/fragment_photo"/> </navigation> 


2. Tambahkan aksi ke grafik utama
Saya menambahkan menambahkan dan mengedit tindakan ke salah satu fragmen, sehingga hanya akan tersedia dari itu.

 <?xml version="1.0" encoding="utf-8"?> <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/mobile_navigation" app:startDestination="@id/importFragment"> <fragment android:id="@+id/importFragment" android:name="com.xiii.navigationapplication.ImportFragment" android:label="fragment_import" tools:layout="@layout/fragment_import"> <action android:id="@+id/add" app:destination="@id/editActivity"> <argument android:name="id" app:argType="integer" android:defaultValue="0"/> </action> <action android:id="@+id/edit" app:destination="@id/editActivity"> <argument android:name="id" app:argType="integer"/> </action> </fragment> <fragment android:id="@+id/galleryFragment" android:name="com.xiii.navigationapplication.GalleryFragment" android:label="fragment_gallery" tools:layout="@layout/fragment_gallery"/> <fragment android:id="@+id/slideshowFragment" android:name="com.xiii.navigationapplication.SlideshowFragment" android:label="fragment_slideshow" tools:layout="@layout/fragment_slideshow"/> <activity android:id="@+id/editActivity" android:name="com.xiii.navigationapplication.EditActivity" android:label="activity_edit" tools:layout="@layout/activity_edit"/> </navigation> 


3. Siapkan parameter dan minta transisi
Dalam contoh ini, ImportFragmentDirections adalah kelas safe-args yang dibuat secara otomatis.

 val direction = ImportFragmentDirections.edit(123 /* id  */) navController.navigate(direction) 


3. Dapatkan id dalam fragmen
 class EditFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { //    ,  fragment     startDestination // val id = EditFragmentArgs.fromBundle(arguments) val id = EditFragmentArgs.fromBundle(requireActivity().intent.extras) return inflater.inflate(R.layout.fragment_edit, container, false) } } 


Anda, tentu saja, memperhatikan fitur-fitur untuk mendapatkan parameter di EditFragment . Ini berfungsi karena tindakan edit (dari titik 1) meneruskan argumen ke EditActivity , dan untuk sebagian alasannya, adalah serakah bahwa tidak meneruskannya ke grafik (misalnya, dengan memanggil navController.graph.setDefaultArguments () ). Fitur ini dapat dielakkan dengan menyiapkan pengontrol navigasi secara manual. Satu cara dijelaskan pada StackOwerflow .

Mungkin kesulitan terbesar akan muncul ketika digunakan secara bersamaan sebagai startDestination dan tujuan yang biasa. Yaitu, ketika meneruskan dan meneruskan parameter ke startDestination dari tujuan lain mana pun dari grafik ini, fragmen harus menentukan secara independen di mana mengekstrak parameter dari: dari argumen atau dari intent.extras. Ini harus diingat ketika merancang transisi dengan melewati parameter.



Meringkas, saya ingin mencatat bahwa saya sendiri belum berhenti menggunakan perpustakaan dan, meskipun ada kekurangan fitur yang terdaftar, saya merasa cukup berguna untuk merekomendasikan untuk digunakan. Saya sangat berharap bahwa dalam rilis berikutnya situasinya akan berubah, setidaknya dengan transfer parameter ke startDestination .

Terima kasih atas perhatian anda Kode kerja Anda!

Sumber untuk artikel ini diposting di GitHub .

Source: https://habr.com/ru/post/id429152/


All Articles