1. Trước khi bắt đầu
Trong lớp học lập trình về Hoạt động và Ý định, bạn đã thêm ý định trong ứng dụng Words, để di chuyển giữa hai hoạt động. Đây là một mẫu điều hướng rất hữu ích, nhưng cũng chỉ là một phần trong quá trình xây dựng giao diện người dùng động cho ứng dụng. Nhiều ứng dụng Android không cần một hoạt động riêng biệt trên mỗi màn hình. Trên thực tế, nhiều mẫu giao diện người dùng phổ biến (chẳng hạn như các thẻ) tồn tại trong một hoạt động duy nhất thông qua việc sử dụng các mảnh.
Mảnh (fragment) là một phần giao diện người dùng có thể sử dụng lại và nhúng vào một hoặc nhiều hoạt động. Trong ảnh chụp màn hình ở trên, việc nhấn vào một thẻ sẽ không kích hoạt ý định hiển thị màn hình tiếp theo. Thay vào đó, việc chuyển đổi các thẻ sẽ chỉ hoán đổi mảnh trước đó với một mảnh khác. Những thao tác này xảy ra mà không khởi động một hoạt động khác.
Thậm chí, bạn có thể hiển thị đồng thời nhiều mảnh trên một màn hình, chẳng hạn như bố cục chi tiết/tổng thể cho thiết bị máy tính bảng. Trong ví dụ dưới đây, cả phần giao diện điều hướng ở bên trái và nội dung ở bên phải đều được chứa trong một mảnh riêng biệt. Cả hai mảnh tồn tại đồng thời trong cùng một hoạt động.
Như bạn thấy, mảnh là một phần không thể thiếu để xây dựng các ứng dụng chất lượng cao. Trong lớp học lập trình này, bạn sẽ tìm hiểu kiến thức cơ bản về các mảnh và chuyển đổi ứng dụng Words để sử dụng tính năng này. Bạn cũng sẽ tìm hiểu cách sử dụng thành phần điều hướng (Navigation component) của Jetpack và xử lý một tệp tài nguyên mới có tên Navigation Graph (Biểu đồ điều hướng) để di chuyển giữa các mảnh trong cùng một hoạt động lưu trữ. Kết thúc lớp học lập trình này, bạn sẽ áp dụng những kỹ năng cơ bản đã học để triển khai các mảnh trong ứng dụng tiếp theo.
Điều kiện tiên quyết
Trước khi tham gia lớp học lập trình này, bạn cần biết
- Cách thêm tệp tài nguyên XML và tệp Kotlin vào dự án Android Studio.
- Tổng quan về vòng đời của một hoạt động.
- Cách ghi đè và triển khai phương thức trong một lớp (class) hiện có.
- Cách tạo thực thể lớp Kotlin, truy cập vào các thuộc tính lớp và gọi các phương thức.
- Các khái niệm cơ bản về giá trị có tính chất rỗng (null) và khác rỗng, đồng thời biết cách xử lý an toàn các giá trị rỗng này.
Kiến thức bạn sẽ học được
- Sự khác biệt giữa vòng đời mảnh (fragment lifecycle) và vòng đời hoạt động (activity lifecycle).
- Cách chuyển đổi một hoạt động hiện có thành một mảnh.
- Cách thêm đích đến vào biểu đồ điều hướng và truyền dữ liệu giữa các mảnh khi sử dụng trình bổ trợ Safe Args.
Sản phẩm bạn sẽ tạo ra
- Bạn sẽ chỉnh sửa ứng dụng Words để sử dụng một hoạt động duy nhất và nhiều mảnh, đồng thời di chuyển giữa các mảnh sử dụng thành phần điều hướng (Navigation Component).
Bạn cần có
- Một máy tính đã cài đặt Android Studio.
- Mã giải pháp của ứng dụng Words trong lớp học lập trình về hoạt động và ý định
2. Mã khởi động
Trong lớp học lập trình này, bạn sẽ tiếp tục xử lý ứng dụng Words trong lớp học lập trình về hoạt động và ý định. Nếu bạn đã hoàn thành lớp học lập trình về hoạt động và ý định, hãy dùng mã của bạn làm điểm khởi đầu. Bạn cũng có thể tải mã xuống qua GitHub.
Tải mã khởi đầu xuống cho lớp học lập trình này
Lớp học lập trình này sẽ cung cấp mã khởi đầu, cho phép bạn mở rộng bằng các tính năng được dạy tại đây. Mã khởi đầu có thể chứa mã bạn đã quen thuộc qua các lớp học lập trình trước đây. Đồng thời cũng có những đoạn mã lạ mà bạn sẽ tìm hiểu trong các lớp học lập trình sau này.
Nếu bạn sử dụng mã khởi đầu lấy trên GitHub, hãy lưu ý tên thư mục là android-basics-kotlin-words-app-activities
. Chọn thư mục này khi bạn mở dự án trong Android Studio.
- Chuyển đến trang kho lưu trữ GitHub được cung cấp cho dự án.
- Xác minh rằng tên nhánh khớp với tên nhánh được chỉ định trong lớp học lập trình. Ví dụ: trong ảnh chụp màn hình sau đây, tên nhánh là main.
- Trên trang GitHub cho dự án này, nhấp vào nút Code (Mã). Thao tác này sẽ khiến một cửa sổ bật lên.
- Trong cửa sổ bật lên, nhấp vào nút Download ZIP (Tải tệp ZIP xuống) để lưu dự án vào máy tính. Chờ quá trình tải xuống hoàn tất.
- Xác định vị trí của tệp trên máy tính (thường nằm trong thư mục Downloads (Tệp đã tải xuống)).
- Nhấp đúp vào tệp ZIP để giải nén. Thao tác này sẽ tạo một thư mục mới chứa các tệp dự án.
Mở dự án trong Android Studio
- Khởi động Android Studio.
- Trong cửa sổ Welcome to Android Studio (Chào mừng bạn đến với Android Studio), hãy nhấp vào Open (Mở).
Lưu ý: Nếu Android Studio đã mở thì chuyển sang chọn tuỳ chọn File (Tệp) > Open (Mở) trong trình đơn.
- Trong trình duyệt tệp, hãy chuyển đến vị trí của thư mục dự án chưa giải nén (thường nằm trong thư mục Downloads (Tệp đã tải xuống)).
- Nhấp đúp vào thư mục dự án đó.
- Chờ Android Studio mở dự án.
- Nhấp vào nút Run (Chạy) để tạo và chạy ứng dụng nhằm đảm bảo rằng ứng dụng được xây dựng như mong đợi.
3. Mảnh và vòng đời mảnh
Mảnh (fragment) đơn giản là một phần có thể sử dụng lại trong giao diện người dùng của ứng dụng. Giống như hoạt động (activity), các mảnh cũng có vòng đời và có thể phản hồi hoạt động đầu vào của người dùng. Mảnh luôn nằm trong hệ phân cấp thành phần hiển thị của một hoạt động khi xuất hiện trên màn hình. Do chú trọng vào khả năng tái sử dụng và mô-đun hoá, mỗi hoạt động có thể đồng thời lưu trữ nhiều mảnh. Mỗi mảnh quản lý một vòng đời riêng.
Vòng đời mảnh
Tương tự như hoạt động, các mảnh được khởi động và xoá khỏi bộ nhớ trong suốt quá trình tồn tại, xuất hiện, biến mất rồi xuất hiện lại trên màn hình. Ngoài ra, cũng giống như hoạt động, vòng đời của mỗi mảnh lại có một số trạng thái tương ứng. Đồng thời, mỗi mảnh lại đưa ra nhiều phương thức ghi đè để phản hồi quá trình chuyển đổi giữa các trạng thái. Vòng đời của mảnh có 5 trạng thái, được biểu hiện dưới dạng enum Lifecycle.State.
- INITIALIZED (KHỞI ĐỘNG): Một thực thể mới của mảnh đã được tạo.
- CREATED (TẠO): Phương thức của vòng đời mảnh đầu tiên được gọi. Trong trạng thái này, thành phần hiển thị được liên kết với mảnh này cũng được tạo lập.
- STARTED (BẮT ĐẦU): Mảnh này xuất hiện trên màn hình nhưng không có "tâm điểm" ("focus"), tức là mảnh này không thể phản hồi hoạt động đầu vào của người dùng.
- RESUMED (TIẾP TỤC): Mảnh này đang xuất hiện và có tâm điểm.
- DESTROYED (HUỶ): Đối tượng mảnh này đã bị huỷ tạo.
Tương tự như các hoạt động, lớp Fragment
cũng cung cấp nhiều phương thức ghi đè để phản hồi các sự kiện trong vòng đời.
onCreate()
: Đã tạo biểu hiện của mảnh và mang trạng tháiCREATED
. Tuy nhiên, thành phần hiển thị (view) tương ứng chưa được tạo.onCreateView()
: Đây là phương thức để tăng cường bố cục. Mảnh này đã chuyển sang trạng tháiCREATED
.onViewCreated()
: Phương thức này được gọi sau khi thành phần hiển thị được tạo lập. Trong phương thức này, thường thì bạn liên kết thành phần hiển thị cụ thể với thuộc tính bằng cách gọi đếnfindViewById()
.onStart()
: Mảnh này đã chuyển sang trạng tháiSTARTED
.onResume()
: Mảnh này đã chuyển sang trạng tháiRESUMED
và có tâm điểm (có thể phản hồi hoạt động đầu vào của người dùng).onPause()
: Mảnh này đã trở lại trạng tháiSTARTED
. Người dùng nhìn thấy giao diện người dùng này.onStop()
: Mảnh này đã trở lại trạng tháiCREATED
. Thực thể của đối tượng được tạo lập nhưng không còn hiện trên màn hình.onDestroyView()
: Được gọi ngay trước khi mảnh chuyển sang trạng tháiDESTROYED
. Khung hiển thị đã bị xoá khỏi bộ nhớ nhưng đối tượng mảnh vẫn tồn tại.onDestroy()
: Mảnh này chuyển sang trạng tháiDESTROYED
.
Biểu đồ dưới đây tóm tắt vòng đời của một mảnh cũng như quá trình chuyển đổi giữa các trạng thái.
Các trạng thái và phương thức gọi lại của vòng đời khá giống với trạng thái và phương thức dùng cho các hoạt động. Tuy nhiên, hãy lưu ý sự khác biệt trong phương thức onCreate()
. Với các hoạt động, bạn sẽ sử dụng phương thức này để tăng cường bố cục và liên kết thành phần hiển thị. Tuy nhiên, trong vòng đời mảnh, onCreate()
được gọi trước khi tạo thành phần hiển thị. Do đó, bạn không thể tăng cường bố cục ở đây. Thay vào đó, bạn sẽ thực hiện việc này trong onCreateView()
. Sau khi khung hiển thị đã được tạo, phương thức onViewCreated()
sẽ được gọi. Tại đây, bạn có thể liên kết thuộc tính với khung hiển thị cụ thể.
Nghe hơi lý thuyết, nhưng giờ đây bạn đã nắm được các khái niệm cơ bản về cách thức hoạt động của mảnh cũng như điểm giống và khác giữa mảnh và hoạt động. Trong phần còn lại của lớp học lập trình này, bạn sẽ vận dụng kiến thức đó. Trước hết, bạn sẽ di chuyển ứng dụng Words mà bạn từng xử lý để sử dụng bố cục theo mảnh. Sau đó, bạn sẽ triển khai việc di chuyển giữa các mảnh trong một hoạt động.
4. Tạo mảnh và tệp bố cục
Giống như các hoạt động, mỗi mảnh thêm vào sẽ bao gồm hai tệp — một tệp XML cho bố cục và một lớp Kotlin để hiện dữ liệu và xử lý tương tác của người dùng. Bạn sẽ thêm một mảnh cho cả danh sách chữ cái và danh sách từ.
- Với app được chọn trong Project Navigator (Trình điều hướng dự án), hãy thêm các mảnh sau (File (Tệp) > New (Mới) > Fragment (Mảnh) > Fragment (Blank) (Mảnh (Trống))), đồng thời cả tệp bố cục và lớp đều được tạo cho từng mảnh.
- Đối với mảnh đầu tiên, hãy đặt Fragment Name (Tên mảnh) thành
LetterListFragment
. Fragment Layout Name (Tên bố cục mảnh) nên điền sẵnfragment_letter_list
.
- Đối với mảnh thứ hai, hãy đặt Fragment Name (Tên mảnh) thành
WordListFragment
. Fragment Layout Name (Tên bố cục mảnh) nên điền sẵnfragment_word_list.xml
.
- Các lớp Kotlin được tạo cho cả hai mảnh sẽ chứa nhiều mã nguyên mẫu thường dùng khi triển khai các mảnh. Tuy nhiên, đây là lần đầu tiên bạn tìm hiểu về các mảnh, nên hãy xoá mọi thứ khỏi hai tệp này, ngoại trừ phần khai báo lớp cho
LetterListFragment
vàWordListFragment
. Chúng tôi sẽ hướng dẫn bạn cách triển khai các mảnh từ đầu để bạn biết cách thức hoạt động của toàn bộ mã. Sau khi xoá mã nguyên mẫu, tệp Kotlin sẽ có dạng như sau.
LetterListFragment.kt
package com.example.wordsapp
import androidx.fragment.app.Fragment
class LetterListFragment : Fragment() {
}
WordListFragment.kt
package com.example.wordsapp
import androidx.fragment.app.Fragment
class WordListFragment : Fragment() {
}
- Sao chép nội dung của
activity_main.xml
vàofragment_letter_list.xml
và nội dung củaactivity_detail.xml
vàofragment_word_list.xml
. Cập nhậttools:context
trongfragment_letter_list.xml
thành.LetterListFragment
vàtools:context
trongfragment_word_list.xml
thành.WordListFragment
.
Sau khi thay đổi, tệp bố cục mảnh sẽ có dạng như sau.
fragment_letter_list.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".LetterListFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="16dp" />
</FrameLayout>
fragment_word_list.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".WordListFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:padding="16dp"
tools:listitem="@layout/item_view" />
</FrameLayout>
5. Triển khai LetterListFragment
Cũng như các hoạt động, bạn cần phải tăng cường bố cục và ràng buộc từng thành phần hiển thị riêng lẻ. Chỉ có một số khác biệt nhỏ khi xử lý vòng đời mảnh. Chúng tôi sẽ hướng dẫn bạn quy trình thiết lập LetterListFragment
, sau đó bạn sẽ thực hiện tương tự cho WordListFragment
.
Để liên kết thành phần hiển thị trong LetterListFragment
, trước tiên, bạn cần có một tham chiếu rỗng đến FragmentLetterListBinding
. Android Studio tạo ra những lớp liên kết như thế này cho từng tệp bố cục khi bạn bật thuộc tính viewBinding
trong phần buildFeatures
của tệp build.gradle. Bạn chỉ cần gán thuộc tính trong lớp mảnh của mình cho mỗi thành phần hiển thị trong FragmentLetterListBinding
.
Kiểu phải là FragmentLetterListBinding?
và phải có giá trị ban đầu là null
. Tại sao giá trị ban đầu phải rỗng? Lý do là bạn không thể tăng cường bố cục cho đến khi gọi onCreateView()
. Có một khoảng thời gian giữa thời điểm thực thể LetterListFragment
được tạo (khi vòng đời của thực thể bắt đầu với phương thức onCreate()
) và thời điểm thuộc tính này thực sự hữu dụng. Ngoài ra, hãy lưu ý rằng bạn có thể tạo và huỷ các thành phần hiển thị của mảnh nhiều lần trong suốt thời gian hoạt động của mảnh đó. Vì lý do đó, bạn cũng cần đặt lại giá trị này trong một phương thức vòng đời khác là onDestroyView()
.
- Trong
LetterListFragment.kt
, hãy bắt đầu bằng cách lấy tham chiếu đếnFragmentLetterListBinding
rồi đặt tên tham chiếu đó thành_binding
.
private var _binding: FragmentLetterListBinding? = null
Vì thuộc tính này có giá trị rỗng, nên mỗi khi truy cập vào một thuộc tính của _binding
, (ví dụ: _binding?.someView
), bạn cần thêm toán tử ?
để đảm bảo cơ chế an toàn rỗng. Tuy nhiên, điều đó không có nghĩa là bạn tuỳ tiện thêm dấu chấm hỏi vào mã chỉ vì một giá trị rỗng. Nếu chắc chắn sẽ truy cập một giá trị khác rỗng, bạn có thể thêm !!
vào tên kiểu. Sau đó, bạn có thể truy cập thuộc tính này như mọi thuộc tính khác mà không cần dùng toán tử ?
.
- Tạo một thuộc tính mới, có tên là binding (không có dấu gạch dưới) và đặt thuộc tính đó bằng
_binding!!
.
private val binding get() = _binding!!
Ở đây, get()
có nghĩa là thuộc tính này "chỉ nhận" (get-only). Tức là bạn có thể nhận giá trị, nhưng sau khi được gán (như ở đây), bạn không thể gán giá trị đó cho thứ gì khác.
- Để hiện trình đơn tuỳ chọn, hãy ghi đè phương thức
onCreate()
. Bên trongonCreate()
, hãy gọi đến phương thứcsetHasOptionsMenu()
với giá trị truyền vào làtrue
.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
- Hãy nhớ rằng với các mảnh, bố cục sẽ tăng cường trong
onCreateView()
. Triển khaionCreateView()
bằng cách tăng cường thành phần hiển thị, thiết lập giá trị của_binding
và trả về thành phần hiển thị gốc.
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentLetterListBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
- Bên dưới thuộc tính
binding
, hãy tạo một thuộc tính cho khung hiển thị tuần hoàn (recycler view).
private lateinit var recyclerView: RecyclerView
- Sau đó, thiết lập giá trị của thuộc tính
recyclerView
trongonViewCreated()
rồi gọichooseLayout()
như bạn đã làm trongMainActivity
. Bạn sẽ chuyển phương thứcchooseLayout()
sangLetterListFragment
trong chốc lát. Vì vậy, đừng lo lắng khi xảy ra lỗi.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
recyclerView = binding.recyclerView
chooseLayout()
}
Hãy để ý đến cách thức lớp liên kết này tạo một thuộc tính cho recyclerView
và bạn không cần gọi findViewById()
cho mỗi thành phần hiển thị.
- Cuối cùng, trong
onDestroyView()
, hãy đặt lại thuộc tính_binding
thànhnull
, vì thành phần hiển thị này không còn tồn tại nữa.
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
- Điểm duy nhất cần lưu ý là có một số khác biệt nhỏ trong phương thức
onCreateOptionsMenu()
khi sử dụng các mảnh. Mặc dù lớpActivity
có một thuộc tính toàn cục có tên làmenuInflater
, nhưngFragment
không có thuộc tính này. Thay vào đó, trình tăng cường trình đơn sẽ được truyền vàoonCreateOptionsMenu()
. Ngoài ra, hãy lưu ý rằng phương thứconCreateOptionsMenu()
được dùng với các mảnh không yêu cầu câu lệnh trả về. Triển khai phương thức này như sau:
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.layout_menu, menu)
val layoutButton = menu.findItem(R.id.action_switch_layout)
setIcon(layoutButton)
}
- Di chuyển toàn bộ phần mã còn lại cho
chooseLayout()
,setIcon()
vàonOptionsItemSelected()
từMainActivity
. Điểm khác biệt duy nhất cần lưu ý là không giống như hoạt động, mảnh không phải là mộtContext
. Bạn không thể truyền giá trịthis
(tham chiếu đến đối tượng mảnh) để làm ngữ cảnh của trình quản lý bố cục. Tuy nhiên, các mảnh cung cấp một thuộc tínhcontext
mà bạn cũng có thể chuyển sang sử dụng. Phần còn lại của mã sẽ giống hệt vớiMainActivity
.
private fun chooseLayout() {
when (isLinearLayoutManager) {
true -> {
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = LetterAdapter()
}
false -> {
recyclerView.layoutManager = GridLayoutManager(context, 4)
recyclerView.adapter = LetterAdapter()
}
}
}
private fun setIcon(menuItem: MenuItem?) {
if (menuItem == null)
return
menuItem.icon =
if (isLinearLayoutManager)
ContextCompat.getDrawable(this.requireContext(), R.drawable.ic_grid_layout)
else ContextCompat.getDrawable(this.requireContext(), R.drawable.ic_linear_layout)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_switch_layout -> {
isLinearLayoutManager = !isLinearLayoutManager
chooseLayout()
setIcon(item)
return true
}
else -> super.onOptionsItemSelected(item)
}
}
- Cuối cùng, hãy sao chép thuộc tính
isLinearLayoutManager
từMainActivity
. Đặt phần này ngay bên dưới phần khai báo thuộc tínhrecyclerView
.
private var isLinearLayoutManager = true
- Bây giờ, tất cả chức năng đã được chuyển vào
LetterListFragment
, tất cả những gì lớpMainActivity
cần làm là tăng cường bố cục để mảnh xuất hiện trong thành phần hiển thị. Hãy tiếp tục và xoá mọi thứ trongMainActivity
, ngoại trừonCreate()
. Sau khi thay đổi,MainActivity
chỉ chứa nội dung sau.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
Đến lượt bạn
Đó là những gì cần làm để di chuyển MainActivity
sang LettersListFragment
. Việc di chuyển DetailActivity
gần như giống hệt. Thực hiện các bước sau để di chuyển mã sang WordListFragment
.
- Sao chép đối tượng đồng hành từ
DetailActivity
sangWordListFragment
. Đảm bảo tham chiếu đếnSEARCH_PREFIX
trongWordAdapter
được cập nhật thành tham chiếuWordListFragment
. - Thêm biến
_binding
. Biến này phải rỗng và có giá trị ban đầu lànull
. - Thêm biến chỉ nhận có tên là binding được gán bằng biến
_binding
. - Tăng cường bố cục trong
onCreateView()
, thiết lập giá trị của_binding
và trả về thành phần hiển thị gốc. - Thực hiện các bước thiết lập còn lại trong
onViewCreated()
: tham chiếu đến thành phần hiển thị tái sinh, thiết lập trình quản lý và chuyển đổi bố cục, đồng thời bổ sung phần trang trí cho thành phần hiển thị này. Bạn cần lấy được chữ cái trả về qua ý định. Vì các mảnh không có thuộc tínhintent
và thường không được truy cập vào ý định của hoạt động mẹ. Hiện tại, bạn hãy tham chiếuactivity.intent
(thay vìintent
trongDetailActivity
) để sử dụng các tính năng bổ sung. - Đặt lại giá trị
_binding
bằng rỗng (null) trongonDestroyView
. - Xoá phần mã còn lại khỏi
DetailActivity
, chỉ để lại phương thứconCreate()
.
Bạn hãy thử tự thực hiện các bước trước khi tiếp tục. Hướng dẫn chi tiết sẽ được trình bày ở bước tiếp theo.
6. Chuyển đổi DetailActivity thành WordListFragment
Hy vọng bạn cảm thấy thích thú với việc di chuyển DetailActivity
sang WordListFragment
. Quá trình này gần giống với việc di chuyển MainActivity
sang LetterListFragment
. Nếu bạn gặp vướng mắc ở bất cứ điểm nào, hãy tham khảo các bước tóm tắt dưới đây.
- Trước tiên, hãy sao chép đối tượng đồng hành vào
WordListFragment
.
companion object {
val LETTER = "letter"
val SEARCH_PREFIX = "https://www.google.com/search?q="
}
- Sau đó, trong
LetterAdapter
, trong phương thứconClickListener()
dùng để thực hiện ý định, bạn cần cập nhật lời gọi hàmputExtra()
, thay thếDetailActivity.LETTER
bằngWordListFragment.LETTER
.
intent.putExtra(WordListFragment.LETTER, holder.button.text.toString())
- Tương tự, trong
WordAdapter
, bạn cần cập nhậtonClickListener()
dùng để chuyển kết quả của từ tìm kiếm, thay thếDetailActivity.SEARCH_PREFIX
bằngWordListFragment.SEARCH_PREFIX
.
val queryUrl: Uri = Uri.parse("${WordListFragment.SEARCH_PREFIX}${item}")
- Trở lại
WordListFragment
, bạn sẽ thêm một biến liên kết thuộc kiểuFragmentWordListBinding?
.
private var _binding: FragmentWordListBinding? = null
- Sau đó, bạn sẽ tạo biến chỉ nhận để tham chiếu các thành phần hiển thị mà không cần sử dụng
?
.
private val binding get() = _binding!!
- Sau đó, bạn sẽ tăng cường bố cục, gán biến
_binding
và trả về thành phần hiển thị gốc. Hãy nhớ rằng đối với các mảnh, bạn thực hiện việc này trongonCreateView()
, không phải trongonCreate()
.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentWordListBinding.inflate(inflater, container, false)
return binding.root
}
- Tiếp theo là triển khai
onViewCreated()
. Bước này gần giống với việc định cấu hìnhrecyclerView
trongonCreate()
trong lớpDetailActivity
. Tuy nhiên, vì các mảnh không có quyền truy cập trực tiếp vàointent
, bạn cần tham chiếu ý định thông quaactivity.intent
. Dù vậy bạn phải thực hiện việc này trongonViewCreated()
, vì không có gì đảm bảo rằng hoạt động sẽ xảy ra sớm hơn trong vòng đời.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val recyclerView = binding.recyclerView
recyclerView.layoutManager = LinearLayoutManager(requireContext())
recyclerView.adapter = WordAdapter(activity?.intent?.extras?.getString(LETTER).toString(), requireContext())
recyclerView.addItemDecoration(
DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
)
}
- Cuối cùng, bạn có thể đặt lại giá trị biến
_binding
trongonDestroyView()
.
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
- Sau khi tất cả chức năng trên được chuyển vào WordListFragment, bạn có thể xoá phần mã còn lại khỏi DetailActivity, chỉ để lại phương thức onCreate().
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
}
Xoá DetailActivity
Bạn đã di chuyển thành công chức năng của DetailActivity
sang WordListFragment
và không cần dùng đến DetailActivity
nữa. Bạn có thể tiếp tục và xoá cả DetailActivity.kt
và activity_detail.xml
cũng như thực hiện một thay đổi nhỏ đối với tệp kê khai.
- Trước hết, hãy xoá
DetailActivity.kt
- Hãy nhớ bỏ đánh dấu tuỳ chọn Xoá an toàn rồi nhấp vào OK.
- Tiếp theo, hãy xoá
activity_detail.xml
. Hãy nhớ bỏ đánh dấu tuỳ chọn Xoá an toàn một lần nữa.
- Cuối cùng, do
DetailActivity
không còn tồn tại nữa, hãy xoá nội dung sau khỏiAndroidManifest.xml
.
<activity
android:name=".DetailActivity"
android:parentActivityName=".MainActivity" />
Sau khi xoá activity_detail.xml, sẽ còn lại 2 mảnh (ListFragment và WordListFragment) và 1 hoạt động (MainActivity). Trong phần tiếp theo, bạn sẽ tìm hiểu về thành phần điều hướng của Jetpack (Jetpack Navigation component) và chỉnh sửa activity_main.xml
để hiển thị và di chuyển giữa các mảnh, thay vì lưu trữ một bố cục tĩnh.
7. Thành phần điều hướng Jetpack
Android Jetpack cung cấp Thành phần điều hướng (Navigation component) để giúp bạn xử lý việc triển khai quá trình di chuyển trong ứng dụng, dù đơn giản hay phức tạp. Thành phần điều hướng có ba phần chính được dùng để triển khai việc di chuyển trong ứng dụng Words.
- Biểu đồ điều hướng: Biểu đồ điều hướng là tệp XML cung cấp hình biểu diễn điều hướng trực quan trong ứng dụng. Tệp này bao gồm các đích đến tương ứng với từng hoạt động và mảnh riêng lẻ cũng như thao tác giữa các hoạt động và mảnh có thể sử dụng trong mã để di chuyển từ đích đến này tới đích đến khác. Giống như tệp bố cục, Android Studio cung cấp trình chỉnh sửa trực quan để thêm các đích đến và thao tác vào biểu đồ điều hướng.
NavHost
:NavHost
dùng để hiển thị các đích đến từ biểu đồ điều hướng bên trong một hoạt động. Khi bạn di chuyển giữa các mảnh, đích đến hiện ra trongNavHost
sẽ được cập nhật. Bạn sẽ dùng phương thức triển khai được tích hợp sẵn có tênNavHostFragment
trongMainActivity
.NavController
: Đối tượngNavController
cho phép kiểm soát việc di chuyển giữa các đích đến hiển thị trongNavHost
. Khi xử lý các ý định, bạn phải gọi phương thức startActivity để chuyển đến một màn hình mới. Với thành phần điều hướng, bạn có thể gọi phương thứcnavigate()
củaNavController
để hoán đổi mảnh sẽ xuất hiện.NavController
cũng giúp bạn xử lý các thao tác thông thường như phản hồi nút "up" ("trước") của hệ thống để quay lại mảnh hiển thị trước đó.
Phần phụ thuộc thành phần điều hướng
- Trong tệp
build.gradle
ở cấp dự án, trong buildscript > ext, bên dướimaterial_version
, hãy đặtnav_version
bằng2.5.2
.
buildscript {
ext {
appcompat_version = "1.5.1"
constraintlayout_version = "2.1.4"
core_ktx_version = "1.9.0"
kotlin_version = "1.7.10"
material_version = "1.7.0-alpha2"
nav_version = "2.5.2"
}
...
}
- Trong tệp
build.gradle
ở cấp ứng dụng, hãy thêm nội dung sau đây vào nhóm phần phụ thuộc:
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
Trình bổ trợ Safe Args
Trong lần đầu triển khai tính năng điều hướng trong ứng dụng Words, bạn đã sử dụng một ý định tường minh giữa hai hoạt động. Để truyền dữ liệu giữa hai hoạt động này, bạn đã gọi phương thức putExtra()
, truyền vào chữ cái đã chọn.
Trước khi bắt đầu triển khai thành phần điều hướng trong ứng dụng Words, bạn cũng sẽ thêm một nội dung là Safe Args – một trình bổ trợ của Gradle, hỗ trợ tính năng nhập an toàn (type safety) khi truyền dữ liệu giữa các mảnh.
Thực hiện các bước sau để tích hợp SafeArgs vào dự án.
- Trong tệp
build.gradle
ở cấp cao nhất, trong buildscript > dependencies, hãy thêm biến môi trường classpath sau đây.
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
- Trong tệp
build.gradle
ở cấp ứng dụng, bên trongplugins
ở trên cùng, hãy thêmandroidx.navigation.safeargs.kotlin
.
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'androidx.navigation.safeargs.kotlin'
}
- Sau khi chỉnh sửa các tệp Gradle, bạn sẽ thấy một biểu ngữ màu vàng ở trên cùng đề nghị bạn đồng bộ hoá dự án. Nhấp vào "Sync Now" ("Đồng bộ hoá ngay") rồi đợi một hoặc hai phút trong khi Gradle cập nhật các phần phụ thuộc của dự án để phản ánh các thay đổi của bạn.
Sau khi đồng bộ hoá xong, bạn đã sẵn sàng để bắt đầu thêm biểu đồ điều hướng.
8. Sử dụng Biểu đồ điều hướng
Khi đã nắm được các yếu tố cơ bản về mảnh và vòng đời của mảnh, đã đến lúc bạn có thể khám phá nhiều điều thú vị hơn. Bước tiếp theo là kết hợp thành phần điều hướng. Thành phần điều hướng đơn giản chỉ là tập hợp công cụ để triển khai quá trình di chuyển, đặc biệt là di chuyển giữa các mảnh. Bạn sẽ làm việc với một trình chỉnh sửa trực quan mới có tên Biểu đồ điều hướng (Navigation Graph hoặc viết tắt là NavGraph), để triển khai việc di chuyển giữa các mảnh.
Biểu đồ điều hướng là gì?
Biểu đồ điều hướng (viết tắt là NavGraph) là một liên kết ảo, hỗ trợ quá trình điều hướng trong ứng dụng. Mỗi màn hình hoặc mảnh trong trường hợp của bạn đều có thể trở thành một "đích đến" trong quá trình điều hướng. NavGraph
có thể được biểu diễn bằng một tệp XML biểu hiện mối liên hệ giữa các đích đến với nhau.
Trên thực tế, thao tác này thực sự sẽ tạo ra một thực thể mới của lớp NavGraph
. Tuy nhiên, FragmentContainerView
sẽ cho người dùng thấy các đích đến trong biểu đồ điều hướng. Bạn chỉ cần tạo một tệp XML và định nghĩa những đích đến có thể xảy ra. Sau đó, bạn có thể sử dụng mã được tạo để di chuyển giữa các mảnh.
Sử dụng FragmentContainerView trong MainActivity
Hiện tại, do bố cục của bạn đã nằm trong fragment_letter_list.xml
và fragment_word_list.xml
, tệp activity_main.xml
của bạn không cần chứa bố cục cho màn hình đầu tiên trong ứng dụng nữa. Thay vào đó, bạn sẽ sử dụng lại MainActivity
để chứa FragmentContainerView
, nhằm đóng vai trò là NavHost cho các mảnh trong ứng dụng. Từ thời điểm này trở đi, tất cả thao tác trong ứng dụng sẽ diễn ra trong FragmentContainerView
.
- Thay thế nội dung của
FrameLayout
trong activity_main.xml, trong đóandroidx.recyclerview.widget.RecyclerView
sẽ thay bằngFragmentContainerView
. Gán mã nhận dạng cho thành phần hiển thị này bằng mã nhận dạng củanav_host_fragment
, đồng thời đặt chiều cao và chiều rộng thànhmatch_parent
để lấp đầy toàn bộ bố cục khung.
Thay thế đoạn mã này:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
...
android:padding="16dp" />
bằng:
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- Bên dưới thuộc tính mã nhận dạng (id), hãy thêm thuộc tính
name
rồi đặt thànhandroidx.navigation.fragment.NavHostFragment
. Mặc dù có thể chỉ định một mảnh cụ thể cho thuộc tính này, nhưng việc đặt thuộc tính này thànhNavHostFragment
sẽ cho phépFragmentContainerView
di chuyển giữa các đoạn.
android:name="androidx.navigation.fragment.NavHostFragment"
- Bên dưới các thuộc tính layout_height và layout_width, hãy thêm thuộc tính có tên
app:defaultNavHost
và đặt giá trị bằng"true"
. Thao tác này cho phép vùng chứa mảnh tương tác với hệ thống phân cấp điều hướng. Ví dụ: nếu bạn nhấn nút quay lại trên hệ thống thì vùng chứa sẽ chuyển về mảnh xuất hiện trước đó, tương tự như những gì sẽ xảy ra khi một hoạt động mới được xuất hiện.
app:defaultNavHost="true"
- Thêm một thuộc tính có tên
app:navGraph
rồi đặt thuộc tính đó bằng"@navigation/nav_graph"
. Thao tác này sẽ trỏ đến một tệp XML giúp định nghĩa cách thức các mảnh di chuyển trong ứng dụng. Hiện tại, Android Studio sẽ hiện một lỗi biểu tượng chưa được giải quyết. Bạn sẽ giải quyết vấn đề này trong nhiệm vụ tiếp theo.
app:navGraph="@navigation/nav_graph"
- Cuối cùng, vì bạn đã thêm hai thuộc tính vào không gian tên của ứng dụng, hãy nhớ thêm thuộc tính
xmlns:app
vàoFrameLayout
.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
Trên đây là tất cả những gì cần thay đổi trong activity_main.xml
. Tiếp theo, bạn sẽ tạo tệp nav_graph
.
Thiết lập biểu đồ điều hướng
Thêm một tệp biểu đồ điều hướng (Tệp > Mới > Tệp tài nguyên Android) rồi điền thông tin các trường như sau.
- File Name (Tên tệp):
nav_graph.xml.
, giống với tên bạn đặt cho thuộc tínhapp:navGraph
. - Resource Type (Kiểu tài nguyên): Navigation (Điều hướng). Thông tin trong trường Directory Name (Tên thư mục) sẽ tự động chuyển thành
navigation
. Hệ thống sẽ tạo một thư mục tài nguyên mới có tên "navigation".
Sau khi tạo tệp XML, bạn sẽ thấy một trình chỉnh sửa trực quan mới. Vì bạn đã tham chiếu nav_graph
trong thuộc tính navGraph
của FragmentContainerView
, nên để thêm một đích đến mới, hãy nhấp vào nút lệnh mới ở trên cùng bên trái màn hình rồi tạo đích đến cho từng mảnh (một đích đến cho fragment_letter_list
và một cho fragment_word_list
).
Sau khi thêm, các mảnh này sẽ xuất hiện trên biểu đồ điều hướng ở giữa màn hình. Bạn cũng có thể chọn một đích đến cụ thể bằng cách sử dụng cây thành phần ở bên trái.
Tạo một thao tác điều hướng
Để tạo một thao tác điều hướng giữa các đích đến letterListFragment
và wordListFragment
, hãy di chuột qua đích đến letterListFragment
rồi kéo từ vòng tròn xuất hiện ở bên phải tới đích đến wordListFragment
.
Bây giờ, bạn sẽ thấy một mũi tên được tạo ra để thể hiện thao tác giữa hai đích đến này. Nhấp vào mũi tên, ở trong ngăn thuộc tính bạn sẽ thấy rằng thao tác này có tên action_letterListFragment_to_wordListFragment
có thể tham chiếu trong mã.
Chỉ định đối số cho WordListFragment
Khi di chuyển giữa các hoạt động với một ý định nào đó, bạn đã chỉ định "extra" để truyền chữ cái đã chọn đến wordListFragment
. Tính năng điều hướng cũng hỗ trợ truyền các tham số giữa các đích đến một cách an toàn.
Chọn đích đến wordListFragment
và trong ngăn thuộc tính, dưới phần Arguments (Đối số), hãy nhấp vào nút dấu cộng để tạo một đối số mới.
Đặt tên cho đối số là letter
, thuộc kiểu String
. Đây cũng chính là nơi bạn từng thêm trình bổ trợ Safe Args. Bằng việc chỉ định đối số này dưới dạng chuỗi, bạn có thể đảm bảo String
đã được liệu trước khi thao tác điều hướng của bạn được thực hiện trong mã.
Thiết lập đích đến bắt đầu
Mặc dù NavGraph nhận biết tất cả đích đến cần thiết, nhưng làm sao FragmentContainerView
biết được cần hiện mảnh nào đầu tiên? Trên NavGraph, bạn cần thiết lập danh sách chữ cái dùng làm đích đến bắt đầu.
Thiết lập đích đến bắt đầu bằng cách chọn letterListFragment
rồi nhấp vào nút Assign start destination (Gán đích đến bắt đầu).
- Hiện tại, đó là tất cả những gì bạn cần làm trên trình chỉnh sửa NavGraph. Bây giờ, hãy tiếp tục và xây dựng dự án. Trong Android Studio, hãy chọn Build (Xây dựng) > Rebuild Project (Xây dựng lại dự án) trên thanh trình đơn. Thao tác này sẽ tạo ra một số mã dựa trên biểu đồ điều hướng, cho phép bạn sử dụng thao tác điều hướng vừa tạo.
Thực hiện thao tác điều hướng
Mở LetterAdapter.kt
để thực hiện thao tác điều hướng. Quá trình này chỉ cần đến hai bước.
- Xoá nội dung trong
setOnClickListener()
của nút (button). Thay vào đó, bạn cần truy xuất thao tác điều hướng vừa tạo. Thêm nội dung dưới đây vàosetOnClickListener()
.
val action = LetterListFragmentDirections.actionLetterListFragmentToWordListFragment(letter = holder.button.text.toString())
Có thể bạn không nhận ra một số tên hàm và lớp trong số này. Lý do là những tên hàm và lớp này được tạo tự động sau khi bạn xây dựng dự án. Đó là nơi bạn thêm trình bổ trợ Safe Args trong bước đầu tiên. Các thao tác được tạo trên Nav Graph sẽ được chuyển thành mã mà bạn có thể sử dụng. Tuy nhiên, những tên hàm và lớp này khá dễ hiểu. LetterListFragmentDirections
biểu hiện tất cả đường dẫn điều hướng có thể xuất phát từ letterListFragment
.
Hàm actionLetterListFragmentToWordListFragment()
biểu hiện một thao tác cụ thể để chuyển đến wordListFragment
.
Sau khi tham chiếu đến thao tác điều hướng, bạn chỉ cần tham chiếu đến NavController (đối tượng giúp bạn thực hiện thao tác điều hướng) rồi gọi navigate()
truyền qua thao tác đó.
holder.view.findNavController().navigate(action)
Định cấu hình MainActivity
Bước thiết lập cuối cùng là cho MainActivity
. Chỉ cần thực hiện một vài thay đổi nữa trong MainActivity
là mọi thứ bắt đầu hoạt động được.
- Tạo thuộc tính
navController
. Thuộc tính này sẽ được thiết lập trongonCreate
nên sẽ được đánh dấu làlateinit
.
private lateinit var navController: NavController
- Tiếp đến, sau khi gọi hàm
setContentView()
trongonCreate()
, hãy lấy thông tin tham chiếu đếnnav_host_fragment
(đây là mã nhận dạng củaFragmentContainerView
) rồi gán thông tin này cho thuộc tínhnavController
.
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
- Sau đó, trong
onCreate()
, hãy gọisetupActionBarWithNavController()
, truyền vàonavController
. Bước này giúp đảm bảo các nút xuất hiện được trên thanh thao tác (thanh ứng dụng), chẳng hạn như tuỳ chọn trình đơn trongLetterListFragment
.
setupActionBarWithNavController(navController)
- Cuối cùng, hãy triển khai
onSupportNavigateUp()
. Ngoài việc thiết lậpdefaultNavHost
bằngtrue
bằng XML, phương thức này cho phép bạn xử lý nút up (trước). Tuy nhiên, hoạt động của bạn cần đưa ra việc triển khai.
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp()
}
Bây giờ, tất cả thành phần đều sẵn sàng để chức năng điều hướng hoạt động được giữa các mảnh. Tuy nhiên, việc điều hướng giờ đây được thực hiện thông qua các mảnh thay vì ý định. Ý định bổ sung cho chữ cái sử dụng trong WordListFragment
sẽ không hoạt động nữa. Trong bước tiếp theo, bạn sẽ cập nhật WordListFragment
để lấy đối số letter
.
9. Lấy đối số trong WordListFragment
Trước đây, bạn từng tham chiếu đến activity?.intent
trong WordListFragment
để truy cập vào letter
bổ sung (extra). Mặc dù cách này mang lại hiệu quả, nhưng đây không phải là phương pháp hay nhất vì các mảnh có thể được nhúng vào các bố cục khác và trong một ứng dụng lớn hơn. Do đó, sẽ rất khó để giả định một mảnh thuộc về hoạt động nào. Hơn nữa, nếu thực hiện việc điều hướng bằng nav_graph
và sử dụng các đối số an toàn, thì sẽ không tồn tại bất cứ ý định nào. Do đó, việc cố gắng truy cập vào các tính năng bổ sung của ý định sẽ không hiệu quả.
May mắn là khá đơn giản để truy cập vào các đối số an toàn và bạn cũng không phải đợi đến khi onViewCreated()
được gọi.
- Trong
WordListFragment
, hãy tạo một thuộc tínhletterId
. Bạn có thể đánh dấu lateinit để thuộc tính này không chứa giá trị rỗng.
private lateinit var letterId: String
- Sau đó, hãy ghi đè
onCreate()
(không phảionCreateView()
hoặconViewCreated()
!) rồi thêm đoạn mã sau:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
letterId = it.getString(LETTER).toString()
}
}
arguments
có thể không bắt buộc nên bạn cần gọi phương thức let()
và truyền vào giá trị lambda. Mã này sẽ thực thi với giả định arguments
khác rỗng, truyền các đối số khác rỗng cho tham số it
. Tuy nhiên, nếu arguments
là null
, thì lambda sẽ không thực thi.
Mặc dù đây không phải là một phần mã thực tế, Android Studio cung cấp cho bạn một gợi ý hữu ích để hiểu thêm về tham số it
.
Vậy chính xác thì Bundle
là gì? Hãy xem đây là một cặp khoá-giá trị được dùng để truyền dữ liệu giữa các lớp, chẳng hạn như hoạt động và mảnh. Bạn đã sử dụng gói (bundle) khi gọi intent?.extras?.getString()
để thực hiện một ý định trong phiên bản đầu tiên của ứng dụng này. Cách lấy chuỗi từ đối số khi xử lý các mảnh cũng thực hiện tương tự.
- Cuối cùng, bạn có thể truy cập
letterId
khi thiết lập bộ chuyển đổi của thành phần hiển thị tái sinh. Thay thếactivity?.intent?.extras?.getString(LETTER).toString()
trongonViewCreated()
bằngletterId
.
recyclerView.adapter = WordAdapter(letterId, requireContext())
Bạn đã làm được! Hãy dành một chút thời gian để chạy ứng dụng của bạn. Giờ đây, bạn có thể di chuyển giữa hai màn hình mà không cần ý định nào và tất cả đều thực hiện trong một hoạt động duy nhất.
10. Cập nhật nhãn mảnh
Bạn đã chuyển đổi thành công cả hai màn hình để sử dụng các mảnh. Trước khi thực hiện thay đổi nào, thanh ứng dụng cho từng mảnh sẽ có tiêu đề mô tả cho mỗi hoạt động có trong thanh ứng dụng. Tuy nhiên, sau khi chuyển đổi để sử dụng các mảnh, tiêu đề này đang bị thiếu trong phần hoạt động chi tiết.
Các mảnh có một thuộc tính tên là "label"
, trong đó bạn có thể đặt tiêu đề để hoạt động mẹ sử dụng trong thanh ứng dụng.
- Trong
strings.xml
, sau tên ứng dụng, hãy thêm hằng số sau.
<string name="word_list_fragment_label">Words That Start With {letter}</string>
- Bạn có thể đặt nhãn cho từng mảnh trên biểu đồ điều hướng. Quay lại với
nav_graph.xml
, chọnletterListFragment
trong cây thành phần, sau đó ở ngăn thuộc tính, đặt nhãn này thành chuỗiapp_name
:
- Chọn
wordListFragment
rồi đặt nhãn này thànhword_list_fragment_label
:
Chúc mừng bạn đã đến được đây! Hãy chạy ứng dụng lần nữa và bạn sẽ thấy mọi thứ như khi bắt đầu lớp học lập trình này. Tuy nhiên, toàn bộ phần điều hướng của bạn giờ đây sẽ được lưu trữ trong một hoạt động duy nhất với một mảnh riêng biệt cho mỗi màn hình.
11. Mã giải pháp
Mã giải pháp cho lớp học lập trình này nằm trong dự án dưới đây.
- Chuyển đến trang kho lưu trữ GitHub được cung cấp cho dự án.
- Xác minh rằng tên nhánh khớp với tên nhánh được chỉ định trong lớp học lập trình. Ví dụ: trong ảnh chụp màn hình sau đây, tên nhánh là main.
- Trên trang GitHub cho dự án này, nhấp vào nút Code (Mã). Thao tác này sẽ khiến một cửa sổ bật lên.
- Trong cửa sổ bật lên, nhấp vào nút Download ZIP (Tải tệp ZIP xuống) để lưu dự án vào máy tính. Chờ quá trình tải xuống hoàn tất.
- Xác định vị trí của tệp trên máy tính (thường nằm trong thư mục Downloads (Tệp đã tải xuống)).
- Nhấp đúp vào tệp ZIP để giải nén. Thao tác này sẽ tạo một thư mục mới chứa các tệp dự án.
Mở dự án trong Android Studio
- Khởi động Android Studio.
- Trong cửa sổ Welcome to Android Studio (Chào mừng bạn đến với Android Studio), hãy nhấp vào Open (Mở).
Lưu ý: Nếu Android Studio đã mở thì chuyển sang chọn tuỳ chọn File (Tệp) > Open (Mở) trong trình đơn.
- Trong trình duyệt tệp, hãy chuyển đến vị trí của thư mục dự án chưa giải nén (thường nằm trong thư mục Downloads (Tệp đã tải xuống)).
- Nhấp đúp vào thư mục dự án đó.
- Chờ Android Studio mở dự án.
- Nhấp vào nút Run (Chạy) để tạo bản dựng và chạy ứng dụng. Đảm bảo ứng dụng được xây dựng như mong đợi.
12. Tóm tắt
- Mảnh là những phần giao diện người dùng có thể sử dụng lại và có thể được nhúng vào các hoạt động.
- Vòng đời của một mảnh khác với vòng đời của một hoạt động, việc thiết lập thành phần hiển thị được thực hiện trong
onViewCreated()
, thay vìonCreateView()
. FragmentContainerView
dùng để nhúng các mảnh vào hoạt động khác và có thể quản lý việc di chuyển giữa các mảnh.
Sử dụng thành phần Điều hướng
- Việc thiết lập thuộc tính
navGraph
củaFragmentContainerView
cho phép bạn di chuyển giữa các mảnh trong một hoạt động. - Trình chỉnh sửa
NavGraph
cho phép bạn thêm các thao tác điều hướng và chỉ định đối số giữa nhiều đích đến. - Trong khi phương thức điều hướng sử dụng ý định yêu cầu bạn truyền tham số trong các lớp bổ sung, thành phần Điều hướng sử dụng SafeArgs để tự động tạo các lớp và phương thức cho các thao tác điều hướng, đảm bảo an toàn về kiểu cho các đối số.
Trường hợp sử dụng mảnh
- Bằng cách sử dụng thành phần Điều hướng, nhiều ứng dụng có thể quản lý toàn bộ bố cục trong một hoạt động duy nhất, trong đó toàn bộ quá trình điều hướng diễn ra giữa các mảnh.
- Các mảnh có thể tạo ra các mẫu bố cục phổ biến, chẳng hạn như bố cục chi tiết/tổng thể trên máy tính bảng hoặc nhiều thẻ trong cùng một hoạt động.