Hiểu Navigation cơ bản trong Android thông qua Demo App !!!
Bài đăng này đã không được cập nhật trong 3 năm
1. Giới thiệu:
Navigation Component
là một thư viện thành phần trong bộ thư viện củaAndroid Jetpack
. Mục đích chính của nó là giúp cho việc điều hướng giữa các màn hình trở nên nhanh gọn và đơn giản hơn, dễ maintain, dễ quản lí cácStack
và được rất nhiều lập trình viên sử dụng trong code base của mình. Trong bài viết này mình sẽ cùng các bạn code một demo cơ bản và dễ hiểu nhất vềNavigation
nhé !
2. Setup ban đầu:
- Chúng ta sẽ code trực tiếp trên project demo sẵn, trong đó đã có sẵn 4
fragment
để chúng ta không phải mất thêm thời gian vẽ layout cũng như tạo các class fragment.kt. Ta sẽ clone về từ link sau : - https://github.com/google-developer-training/android-basics-kotlin-cupcake-app/tree/viewmodel
- Tại đây nếu các bạn checkout đến nhánh
viewmodel
để sử dụng luôn viewmodel có sẵn.
3. Setup navigation cho nút back
tại actionBar
:
- Tại lớp
MainActivity.kt
ta sẽ khai báo biến toàn cụcnavController
và sẽ gọi phương thứcsetUpActionBarWithNavController
trong phương thứconCreate
:
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
setupActionBarWithNavController(navController)
}
}
- Tiếp đến ta gi đè phương thức
onSupportNavigateUp
để chỉ định activity này làhost
củanavigation
:
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp()
}
4. Tìm hiểu thêm về ngăn xếp:
- Trong Android chúng ta tương tác với thiết bị thông qua các
Activity
vàFragment
mỗi thao tác của chúng ta từ khi khởi động app đều được xếp chồng lên nhau trong ngăn xếp (Back Stack), ta hình dung nó như một chồng bánh quy ( miếng bánh trên cùng mà chúng ta nhìn thấy chính làActivity
hoặcFragment
đang hiển thị trên thiết bị. Còn những stack được xếp ở phía dưới nó sẽ được tạm dừng hoạt động và sẵn sàng quay trở lại màn hình chính nếu người dùng bấm nútBack
cho đến khi tới stack đó. Quy trình củaBack Stack
là Last In First Out, có nghĩa là Stack nào được thêm vào ngăn xếp sau cùng thì khi bấmBack
nó sẽ được mang ra trước rồi lần lượt như vậy. - Lấy ví dụ trong demo app
CupCake
mà chúng ta đang viết, mặc dù chỉ có một activity nhưng lại có nhiều fragment nằm trong nó, nên khi chạy ứng dụngStartFragment
được đẩy vào ngăn xếp và hiện tại đang đứng trên cùng :
- Sau khi bạn chọn số lượng bánh cho đơn hàng,
navigation
sẽ điều hướng bạn đếnFlavorFragment
vàFlavorFragment
được đẩy vàoBackStack
và đứng trên cùng của ngăn xếp :
- Tiếp đến bạn chọn hương vị cho đơn bánh và
navigation
sẽ điều hướng bạn đếnPickUpFragment
và cũng tương tự fragment này được đẩy tiếp vào trên cùng củaBackStack
:
- Sau khi bấm
Next
bạn sẽ được điều hướng đếnSummaryFragment
và cũng tương tự được đẩy vào ngăn xếp :
- Tại thời điểm này bạn bấm nút
Back
trên ActionBar hoặc nút back vật lí trên devices thìSummaryFragment
sẽ bật ra khỏiBackStack
và bị hủy hoàn toàn và bạn sẽ thấyPickUpFragment
hiển thị lên sau thao tác này:
- Tiếp tục bấm nút
Back
thêm 2 lần nữa thìPickUpFragment
vàFlavorFragment
cũng lần lượt bị bật ra khỏiBackStack
và chỉ cònStartFragment
hiển thị trên màn hình :
-
Để làm được việc đó ta cần add action cho navigation, các bước như sau :
- Mở trình chỉnh sửa điều hướng:
res
>navigation
>nav_graph.xml
sau đó chọnDesign
- Hiện tại ta đã thấy được từ
StartFragment
tớiFlavorFragment
đã có một hành động điều hướng đi theo chiều của mũi tên và lần lượt cũng vậy từFlavorFragment
cũng có hành động điều hướng đi tớiPickUpFragment
và tương tự vớiSummaryFragment
. Nhưng vấn đề ở chỗ khi bạn đặt hàng xong đơn hàng hoặc đã đến màn nào đó gần cuối nhưng bạn muốn hủy đơn hàng do sai sót gì đó và bạn muốn mình quay trở lạiStartFragment
để tạo một đơn hàng mới hoàn toàn mà không phải Back lại quá nhiều lần, thì hãy thêm những action khác vào navigation này bằng cách sau. - Click vào
FlavorFragment
kéo đếnStartFragment
sẽ xuất hiện một mũi tên nối giữa hai fragment đó, làm điều tương tự vớiPickUpFragment
vàSummaryFragment
. - Cuối cùng
nav_graph
hoàn chỉnh sẽ trông như sau :
- Mở trình chỉnh sửa điều hướng:
5. Thêm button Cancel
vào các fragment:
- Việc này như đã nói ở trên sẽ giúp bạn không phải bấm
Back
quá nhiều lần mà chỉ cần bấmCancel
thì lập tức sẽ trở về fragment đầu tiên và sẵn sàng cho đơn hàng mới. - Thêm đoạn mã sau vào
fragment_flavor.xml
:
<Button
android:id="@+id/cancel_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/cancel"
style="?attr/materialButtonOutlinedStyle"
android:layout_marginEnd="@dimen/side_margin"
app:layout_constraintEnd_toStartOf="@id/next_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/next_button" />
- Và sửa
next_button
thànhapp:layout_constraintStart_toEndOf="@id/cancel_button"
- Thực hiện tương tự tại
fragment_flavor
vàfragment_summary
- Tại class
FravorFragment.kt
ta thêm phương thức sau :
fun cancelOrder() {
sharedViewModel.resetOrder()
findNavController().navigate(R.id.action_flavorFragment_to_startFragment)
}
- Phương thức này gọi đến
action
điều hướng từFlavorFragment
tớiStarFragment
- Quay lại
fragment_flavor.xml
và thêmonClick
cho buttonCancel
:
android:onClick="@{() -> flavorFragment.cancelOrder()}"
- Ta sẽ làm tương tự với
PickUpFragment
vàSummaryFragment
. - Tại đây ta có thể chạy ứng dụng và sử dụng nút
Cancel
để tạo đơn hàng mới từ bất cứ fragment nào. - Nhưng có một vấn đề phát sinh ở đây là khi bạn đã đến
SummaryFragment
và bấmCancel
thì màn hìnhStartFragment
hiển thị lên, điều này trông có vẻ đúng. Nhưng nếu tiếp tục bấmBack
thì sẽ trở lạiSummaryFragment
với thông tin bị bỏ trống. Qua đây ta có thể hình dung ra được khi bấmCancel
làStartFragment
được đẩy tiếp tục vào trongBackStack
và nhữngstack
trước đó vẫn còn tồn tại gây ra trải nghiệm xấu cho người dùng :
-
Để giải quyết vấn đề trên ta cần 2 bước:
- Thêm thuộc tính
app:popUpTo = "@id/startFragment"
vào trong action của navigation sẽ giúp bật lần lượt các ngăn xếp cho đến khi gặp đượcStartFragment
. Sau bước nàyBackStack
trông như sau:
- Nhưng trên hình ta cũng có thể thấy được là nếu bấm
Back
tiếp thì sẽ lại được điều hướng đến một bản sao củaStartFragment
nên ta cần thêm thuộc tínhapp:popUpToInclusive="true"
vào trong action của navigation. Điều này sẽ giúp các ngăn xếp được bật ra ngoài lần lượt đến và luôn cảStartFragment
cũ. - Cách thực hiện thì các bạn quay lại thao tác giống bước trên để mở ra cửa sổ chỉnh sửa điều hướng, sau đó click vào mũi tên từ
SummaryFragment
chỉ tớiStartFragment
và mũi tên đó sẽ sáng lên màu xanh, bạn nhìn sang bảngAttribute
bên phải và chọnpopUpTo
chọnStartFragment
còn dòng bên dướipopUpToInclusive
chọntrue
- Ta làm tương tự với
PickUpFragment
vàFlavorFragment
- Thêm thuộc tính
-
Đến đây ta đã giải quyết tất cả các vấn đề liên quan đến
navigation
thông qua việc sử dụng nútBack
trên actionBar, nútBack
vật lí và cả nútCancel
nữa, hi vọng các bạn sẽ hiểu hơn về việc sửNavigation
và sẽ áp dụng nó vào project của mình. -
Cảm ơn các bạn đã theo dõi bài viết !!
-
Nguồn : https://developer.android.com/codelabs/basic-android-kotlin-training-navigation-backstack#0
All rights reserved