1. Trước khi bắt đầu
Trong các lớp học lập trình trước, bạn đã tìm hiểu cách sử dụng thư viện dữ liệu cố định Room, một tầng trừu tượng ở đầu cơ sở dữ liệu SQLite để lưu trữ dữ liệu ứng dụng. Trong lớp học lập trình này, bạn sẽ thêm nhiều tính năng hơn vào ứng dụng Inventory (Kiểm kho) và tìm hiểu cách đọc, hiển thị, cập nhật và xoá dữ liệu khỏi cơ sở dữ liệu SQLite thông qua Room. Bạn sẽ sử dụng RecyclerView để hiển thị dữ liệu từ cơ sở dữ liệu và tự động cập nhật dữ liệu khi dữ liệu cơ sở trong cơ sở dữ liệu được thay đổi.
Điều kiện tiên quyết
- Bạn biết cách tạo và tương tác với cơ sở dữ liệu SQLite bằng cách sử dụng thư viện Room.
- Bạn biết cách tạo thực thể, đối tượng truy cập dữ liệu (DAO) và lớp cơ sở dữ liệu.
- Bạn biết cách sử dụng đối tượng truy cập dữ liệu (DAO) để ánh xạ hàm Kotlin đến truy vấn SQL.
- Bạn biết cách hiển thị các mục của một danh sách trong
RecyclerView. - Bạn đã tham gia lớp học lập trình trước trong bài Duy trì dữ liệu cố định bằng Room
Kiến thức bạn sẽ học được
- Cách đọc và hiển thị thực thể từ cơ sở dữ liệu SQLite.
- Cách cập nhật và xoá thực thể trong cơ sở dữ liệu SQLite bằng cách sử dụng thư viện Room.
Sản phẩm bạn sẽ tạo ra
- Bạn sẽ tạo một ứng dụng Inventory (Kiểm kê) có chức năng cung cấp danh sách mục hàng tồn kho. Ứng dụng có thể cập nhật, chỉnh sửa và xoá các mục trong cơ sở dữ liệu của ứng dụng bằng cách sử dụng Room.
2. Tổng quan về ứng dụng khởi động
Lớp học lập trình này sử dụng đoạn mã giải pháp của ứng dụng Inventory từ lớp học lập trình trước làm đoạn mã khởi đầu. Ứng dụng khởi đầu lưu dữ liệu bằng cách sử dụng thư viện dữ liệu cố định Room. Người dùng có thể thêm dữ liệu vào cơ sở dữ liệu ứng dụng bằng cách sử dụng màn hình Add Item (Thêm mặt hàng).
Lưu ý: Phiên bản hiện tại của ứng dụng khởi đầu không cho thấy ngày được lưu trữ trong cơ sở dữ liệu.
Trong lớp học lập trình này, bạn sẽ mở rộng ứng dụng này để đọc và hiển thị dữ liệu cũng như cập nhật và xoá thực thể trên cơ sở dữ liệu bằng thư viện Room.
Tải đoạn mã khởi đầu cho lớp học lập trình này
Đoạn mã khởi đầu này cũng là đoạn mã giải pháp của lớp học lập trình trước đó.
Để lấy mã cho lớp học lập trình này và mở mã trong Android Studio, hãy làm theo các bước sau.
Lấy mã
- Nhấp vào URL được cung cấp. Thao tác này sẽ mở trang GitHub của dự án trong một trình duyệt.
- Trên trang GitHub của dự án, hãy nhấp vào nút Code (Mã), một hộp thoại sẽ xuất hiện.

- Trong hộp thoại này, hãy 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 an existing Android Studio project (Mở một dự án hiện có trong Android Studio).

Lưu ý: Nếu Android Studio đã mở sẵn thì thay vào đó, hãy chọn tuỳ chọn sau đây trong trình đơn File > New > Import Project (Tệp > Mới > Nhập dự án).

- Trong hộp thoại Import Project (Nhập dự án), hãy chuyển đến nơi chứa thư mục dự án đã 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. Đảm bảo ứng dụng chạy đúng như mong đợi. - Duyệt qua các tệp của dự án trong cửa sổ công cụ Project (Dự án) để xem cách triển khai ứng dụng.
3. Thêm RecyclerView
Trong nhiệm vụ này, bạn sẽ thêm RecyclerView vào ứng dụng để hiển thị dữ liệu đã lưu trữ trong cơ sở dữ liệu.
Thêm hàm trợ giúp để định dạng giá
Dưới đây là ảnh chụp màn hình của ứng dụng hoàn thiện.

Lưu ý rằng giá sẽ thể hiện ở định dạng đơn vị tiền tệ. Để chuyển đổi một giá trị double sang định dạng đơn vị tiền tệ mong muốn, bạn sẽ thêm hàm mở rộng vào lớp Item.
Hàm mở rộng
Kotlin cho phép mở rộng một lớp thông qua chức năng mới mà không cần phải kế thừa từ lớp đó hay sửa đổi định nghĩa hiện có của lớp đó. Tức là bạn có thể thêm hàm vào một lớp hiện có mà không phải truy cập vào mã nguồn của lớp đó. Bạn có thể làm việc này qua các phần khai báo đặc biệt gọi là phần mở rộng (extension).
Ví dụ: bạn có thể viết các hàm mới cho một lớp từ thư viện bên thứ ba mà bạn không thể chỉnh sửa. Bạn có thể gọi các hàm như vậy theo cách thông thường như thể đó là phương thức của lớp gốc. Các hàm này được gọi là hàm mở rộng (extension function). (Ngoài ra còn có các thuộc tính mở rộng cho phép bạn xác định thuộc tính mới cho lớp hiện có, nhưng các thuộc tính này nằm ngoài phạm vi của lớp học lập trình này.)
Hàm mở rộng không chỉnh sửa lớp nhưng cho phép bạn sử dụng ký hiệu dấu chấm khi gọi hàm trên các đối tượng của lớp đó.
Ví dụ: trong đoạn mã sau đây, bạn có một lớp (class) tên là Square. Lớp này có một thuộc tính cho cạnh bên và một hàm để tính diện tích của hình vuông. Hãy để ý hàm mở rộng Square.perimeter(). Tên hàm bắt đầu bằng lớp mà tại đó hàm này hoạt động. Bên trong hàm, bạn có thể tham chiếu các thuộc tính công khai của lớp Square.
Quan sát việc sử dụng hàm mở rộng trong hàm main(). Hàm mở rộng đã tạo, perimeter(), được gọi dưới dạng một hàm thông thường bên trong lớp Square đó.
Ví dụ:
class Square(val side: Double){
fun area(): Double{
return side * side;
}
}
// Extension function to calculate the perimeter of the square
fun Square.perimeter(): Double{
return 4 * side;
}
// Usage
fun main(args: Array<String>){
val square = Square(5.5);
val perimeterValue = square.perimeter()
println("Perimeter: $perimeterValue")
val areaValue = square.area()
println("Area: $areaValue")
}
Trong bước này, bạn sẽ định dạng giá mặt hàng thành một chuỗi định dạng đơn vị tiền tệ. Nói chung, bạn không nên thay đổi một lớp thực thể biểu thị dữ liệu chỉ để định dạng dữ liệu (xem nguyên tắc trách nhiệm duy nhất), vì vậy, bạn sẽ thêm một hàm mở rộng.
- Trong
Item.kt, bên dưới định nghĩa lớp, hãy thêm hàm mở rộng có tên làItem.getFormattedPrice(). Hàm này không chứa tham số và trả vềString. Hãy lưu ý tên lớp và ký hiệu dấu chấm trong tên hàm.
fun Item.getFormattedPrice(): String =
NumberFormat.getCurrencyInstance().format(itemPrice)
Nhập java.text.NumberFormat khi được Android Studio nhắc.
Thêm ListAdapter
Ở bước này, bạn sẽ thêm trình chuyển đổi danh sách (list adapter) vào RecyclerView. Vì bạn đã quen với việc triển khai trình chuyển đổi từ các lớp học lập trước nên nội dung hướng dẫn sẽ được tóm tắt như dưới đây. Tệp ItemListAdapter đã hoàn thành nằm ở cuối bước này để thuận tiện cho bạn, đồng thời giúp bạn hiểu rõ hơn về các khái niệm liên quan đến Room trong lớp học lập trình này.
- Trong gói
com.example.inventory, hãy thêm một lớp Kotlin tên làItemListAdapter. Truyền vào một hàm tên làonItemClicked()dưới dạng tham số hàm khởi tạo, hàm này chứa đối tượngItemdưới dạng tham số. - Thay đổi chữ ký lớp
ItemListAdapterđể mở rộngListAdapter. Truyền vàoItemvàItemListAdapter.ItemViewHolderdưới dạng tham số. - Thêm tham số hàm khởi tạo
DiffCallback;ListAdaptersẽ sử dụng tham số này để xác định thay đổi trong danh sách. - Ghi đè phương thức bắt buộc
onCreateViewHolder()vàonBindViewHolder(). - Phương thức
onCreateViewHolder()sẽ trả vềViewHoldermới khi RecyclerView cần. - Bên trong phương thức
onCreateViewHolder(), hãy tạo mộtViewmới và tăng cường từ tệp bố cụcitem_list_item.xmlbằng cách sử dụng lớpItemListItemBindingliên kết được tạo tự động. - Triển khai phương thức
onBindViewHolder(). Lấy mục hiện tại bằng cách sử dụng phương thứcgetItem()với giá trị truyền vào là vị trí. - Thiết lập trình nghe lượt nhấp (click listener) trên
itemView, gọi hàmonItemClicked()bên trong trình nghe đó. - Định nghĩa lớp
ItemViewHolder, mở rộng lớp này từRecyclerView.ViewHolder.. Ghi đè hàmbind(), truyền đối tượngItemvào hàm. - Định nghĩa đối tượng đồng hành. Bên trong đối tượng đồng hành (companion object), hãy xác định
valthuộc loạiDiffUtil.ItemCallback<Item>()tên làDiffCallback. Ghi đè các phương thức cần có làareItemsTheSame()vàareContentsTheSame()rồi định nghĩa các phương thức đó.
Lớp hoàn chỉnh sẽ có dạng như sau:
package com.example.inventory
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.inventory.data.Item
import com.example.inventory.data.getFormattedPrice
import com.example.inventory.databinding.ItemListItemBinding
/**
* [ListAdapter] implementation for the recyclerview.
*/
class ItemListAdapter(private val onItemClicked: (Item) -> Unit) :
ListAdapter<Item, ItemListAdapter.ItemViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
return ItemViewHolder(
ItemListItemBinding.inflate(
LayoutInflater.from(
parent.context
)
)
)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val current = getItem(position)
holder.itemView.setOnClickListener {
onItemClicked(current)
}
holder.bind(current)
}
class ItemViewHolder(private var binding: ItemListItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
}
}
companion object {
private val DiffCallback = object : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
return oldItem.itemName == newItem.itemName
}
}
}
}
Quan sát màn hình danh sách hàng tồn kho trong ứng dụng đã hoàn tất (ứng dụng giải pháp ở cuối lớp học lập trình này). Xin lưu ý rằng mỗi phần tử danh sách đều hiển thị tên của mục hàng tồn kho, giá ở định dạng đơn vị tiền tệ và số lượng tồn kho hiện tại. Trong các bước trước đó, bạn đã dùng tệp bố cục item_list_item.xml có ba TextView để tạo hàng. Trong bước tiếp theo, bạn sẽ liên kết thông tin về thực thể đó vào các TextView này.

- Trong
ItemListAdapter.kt, hãy triển khai hàmbind()trong lớpItemViewHolder. Liên kết TextViewitemNamevớiitem.itemName. Lấy giá ở định dạng tiền tệ bằng cách sử dụng hàm mở rộnggetFormattedPrice()và liên kết giá trị đó với TextViewitemPrice. Chuyển đổi giá trịquantityInStockthànhStringvà liên kết với TextViewitemQuantity. Phương thức hoàn chỉnh sẽ có dạng như sau:
fun bind(item: Item) {
binding.apply {
itemName.text = item.itemName
itemPrice.text = item.getFormattedPrice()
itemQuantity.text = item.quantityInStock.toString()
}
}
Khi được Android Studio nhắc, hãy nhập com.example.inventory.data.getFormattedPrice.
Sử dụng ListAdapter
Trong nhiệm vụ này, bạn sẽ cập nhật InventoryViewModel và ItemListFragment để hiển thị thông tin về mặt hàng trên màn hình bằng trình chuyển đổi danh sách mà bạn đã tạo ở bước trước.
- Ở đầu lớp
InventoryViewModel, tạo mộtvaltên làallItemsthuộc loạiLiveData<List<Item>>cho các mặt hàng từ cơ sở dữ liệu. Đừng lo về lỗi xuất hiện, bạn sẽ sớm khắc phục nó.
val allItems: LiveData<List<Item>>
Nhập androidx.lifecycle.LiveData khi được Android Studio nhắc.
- Gọi
getItems()trênitemDaovà chỉ định choallItems. HàmgetItems()trả vềFlow. Để sử dụng dữ liệu dưới dạng giá trịLiveData, hãy sử dụng hàmasLiveData(). Định nghĩa hoàn chỉnh sẽ có dạng như sau:
val allItems: LiveData<List<Item>> = itemDao.getItems().asLiveData()
Nhập androidx.lifecycle.asLiveData khi được Android Studio nhắc.
- Trong
ItemListFragment, ở đầu lớp, khai báo thuộc tínhprivatekhông thể thay đổi được gọi làviewModelthuộc loạiInventoryViewModel. Sử dụng đối tượng uỷ quyền bằngbyđể chuyển giao lệnh khởi tạo thuộc tính cho lớpactivityViewModels. Truyền vào hàm khởi tạoInventoryViewModelFactory.
private val viewModel: InventoryViewModel by activityViewModels {
InventoryViewModelFactory(
(activity?.application as InventoryApplication).database.itemDao()
)
}
Nhập androidx.fragment.app.activityViewModels khi được Android Studio yêu cầu.
- Vẫn trong
ItemListFragment, hãy cuộn đến hàmonViewCreated(). Bên dưới lệnh gọi tớisuper.onViewCreated(), hãy khai báovalcó tênadapter. Khởi tạo thuộc tínhadaptermới bằng hàm khởi tạo mặc định,ItemListAdapter{}không truyền giá trị nào vào. - Liên kết
adaptermới tạo vớirecyclerViewnhư sau:
val adapter = ItemListAdapter {
}
binding.recyclerView.adapter = adapter
- Vẫn bên trong
onViewCreated(), sau khi thiết lập trình chuyển đổi. Đính kèm một trình quan sát (observer) trênallItemsđể theo dõi các thay đổi về dữ liệu. - Bên trong trình quan sát, hãy gọi
submitList()trênadapterrồi truyền danh sách mới vào. Thao tác này sẽ cập nhật RecyclerView bằng các mục mới trong danh sách.
viewModel.allItems.observe(this.viewLifecycleOwner) { items ->
items.let {
adapter.submitList(it)
}
}
- Đảm bảo rằng phương thức
onViewCreated()hoàn chỉnh có dạng như dưới đây. Chạy ứng dụng. Lưu ý rằng danh sách hàng tồn kho sẽ xuất hiện nếu bạn lưu các mặt hàng trong cơ sở dữ liệu ứng dụng. Thêm một số mặt hàng tồn kho vào cơ sở dữ liệu ứng dụng nếu danh sách trống.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val adapter = ItemListAdapter {
}
binding.recyclerView.adapter = adapter
viewModel.allItems.observe(this.viewLifecycleOwner) { items ->
items.let {
adapter.submitList(it)
}
}
binding.recyclerView.layoutManager = LinearLayoutManager(this.context)
binding.floatingActionButton.setOnClickListener {
val action = ItemListFragmentDirections.actionItemListFragmentToAddItemFragment(
getString(R.string.add_fragment_title)
)
this.findNavController().navigate(action)
}
}

4. Hiển thị thông tin về mặt hàng
Trong nhiệm vụ này, bạn sẽ đọc và hiển thị thông tin về thực thể trên màn hình Item Details (Thông tin về mặt hàng). Bạn sẽ sử dụng khoá chính (id của mặt hàng) để đọc thông tin, chẳng hạn như tên, giá và số lượng trong cơ sở dữ liệu của ứng dụng kiểm kho rồi hiển thị thông tin này trên màn hình Item Details (Thông tin về mặt hàng) bằng tệp bố cục fragment_item_detail.xml. Tệp bố cục fragment_item_detail.xml được thiết kế sẵn cho bạn và có 3 TextView hiển thị thông tin về mặt hàng.

Bạn sẽ triển khai các bước sau trong nhiệm vụ này:
- Thêm một trình xử lý lượt nhấp vào RecyclerView để chuyển ứng dụng đến màn hình Item Details (Thông tin về mặt hàng).
- Trong mảnh (fragment)
ItemListFragment, hãy truy xuất dữ liệu từ cơ sở dữ liệu rồi hiển thị dữ liệu đó. - Liên kết TextView với dữ liệu ViewModel.
Thêm trình xử lý lượt nhấp
- Trong
ItemListFragment, hãy di chuyển đến hàmonViewCreated()để cập nhật định nghĩa trình chuyển đổi. - Thêm một lambda làm tham số hàm khởi tạo vào
ItemListAdapter{}.
val adapter = ItemListAdapter {
}
- Bên trong lambda, hãy tạo một
valcó tên làaction. Bạn sẽ sớm khắc phục lỗi khởi tạo.
val adapter = ItemListAdapter {
val action
}
- Gọi phương thức
actionItemListFragmentToItemDetailFragment()trênItemListFragmentDirectionsvới giá trị truyền vào là mụcid. Chỉ định đối tượngNavDirectionsđược trả về choaction.
val adapter = ItemListAdapter {
val action = ItemListFragmentDirections.actionItemListFragmentToItemDetailFragment(it.id)
}
- Dưới định nghĩa
action, hãy truy xuất một phiên bảnNavControllerbằng cách sử dụngthis.findNavController()rồi gọinavigate()trên đó với giá trị truyền vào làaction. Định nghĩa trình chuyển đổi sẽ có dạng như sau:
val adapter = ItemListAdapter {
val action = ItemListFragmentDirections.actionItemListFragmentToItemDetailFragment(it.id)
this.findNavController().navigate(action)
}
- Chạy ứng dụng. Nhấp vào một mục trong
RecyclerView. Ứng dụng sẽ chuyển đến màn hình Item Details (Thông tin về mặt hàng). Bạn sẽ thấy phần thông tin bị trống. Khi nhấn vào nút, sẽ không có gì xảy ra.

Trong các bước sau, bạn sẽ hiển thị thông tin về thực thể trên màn hình Item Details (Thông tin về mặt hàng) và thêm chức năng vào nút bán và nút xoá.
Truy xuất thông tin về mặt hàng
Ở bước này, bạn sẽ thêm một hàm mới vào InventoryViewModel để truy xuất thông tin về mặt hàng qua cơ sở dữ liệu dựa trên id của mục. Trong bước tiếp theo, bạn sẽ sử dụng chức năng này để hiển thị thông tin về thực thể trên màn hình Item Details (Thông tin về mặt hàng).
- Trong
InventoryViewModel, hãy thêm một hàm tên làretrieveItem(). Hàm này chứaIntdưới dạng giá trị nhận dạng mặt hàng và trả vềLiveData<Item>. Bạn sẽ sớm khắc phục lỗi biểu thức trả về.
fun retrieveItem(id: Int): LiveData<Item> {
}
- Bên trong hàm mới, hãy gọi
getItem()trênitemDao, với tham số truyền vào làid. HàmgetItem()trả vềFlow. Để sử dụng giá trịFlowdưới dạngLiveData, hãy gọi hàmasLiveData()và sử dụng hàm này làm hàm trả về của hàmretrieveItem(). Hàm hoàn chỉnh sẽ có dạng như sau:
fun retrieveItem(id: Int): LiveData<Item> {
return itemDao.getItem(id).asLiveData()
}
Liên kết dữ liệu với TextView
Trong bước này, bạn sẽ tạo một phiên bản ViewModel trong ItemDetailFragment và liên kết dữ liệu ViewModel với TextView trong màn hình Item Details (Thông tin về mặt hàng). Bạn cũng sẽ đính kèm trình quan sát vào dữ liệu trong ViewModel để cập nhật danh sách hàng tồn kho trên màn hình nếu dữ liệu cơ sở trong cơ sở dữ liệu thay đổi.
- Trong
ItemDetailFragment, hãy thêm thuộc tính có thể thay đổi có tên làitemcủa thực thể loạiItem. Bạn sẽ sử dụng thuộc tính này để lưu trữ thông tin về một thực thể. Thuộc tính này sẽ được khởi động sau, vì vậy hãy thêm vào tiền tốlateinit.
lateinit var item: Item
Nhập com.example.inventory.data.Item khi được Android Studio nhắc.
- Ở đầu lớp
ItemDetailFragment, hãy khai báo thuộc tínhprivatekhông thể thay đổi tên làviewModelthuộc loạiInventoryViewModel. Sử dụng đối tượng uỷ quyền bằngbyđể chuyển giao lệnh khởi tạo thuộc tính cho lớpactivityViewModels. Truyền vào hàm khởi tạoInventoryViewModelFactory.
private val viewModel: InventoryViewModel by activityViewModels {
InventoryViewModelFactory(
(activity?.application as InventoryApplication).database.itemDao()
)
}
Nhập androidx.fragment.app.activityViewModels nếu được Android Studio nhắc.
- Vẫn trong
ItemDetailFragment, hãy tạo một hàmprivatecó tên làbind(). Hàm này sẽ lấy một phiên bản của thực thểItemlàm tham số và không trả về giá trị nào.
private fun bind(item: Item) {
}
- Triển khai hàm
bind(), tương tự như những gì bạn đã làm trongItemListAdapter. Thiết lập thuộc tínhtextcủa TextViewitemNamethànhitem.itemName. GọigetFormattedPrice()trên thuộc tínhitemđể định dạng giá của mặt hàng và thiết lập giá trị đó thành thuộc tínhtextcủa TextViewitemPrice. Chuyển đổiquantityInStockthànhStringvà thiết lập thành thuộc tínhtextcủa TextViewitemQuantity.
private fun bind(item: Item) {
binding.itemName.text = item.itemName
binding.itemPrice.text = item.getFormattedPrice()
binding.itemCount.text = item.quantityInStock.toString()
}
- Cập nhật hàm
bind()để sử dụng hàm phạm viapply{}đối với khối mã như minh hoạ bên dưới.
private fun bind(item: Item) {
binding.apply {
itemName.text = item.itemName
itemPrice.text = item.getFormattedPrice()
itemCount.text = item.quantityInStock.toString()
}
}
- Vẫn ở trong
ItemDetailFragment, hãy ghi đèonViewCreated().
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
- Trong một trong các bước trước đó, bạn đã truyền giá trị nhận dạng mặt hàng làm đối số điều hướng vào
ItemDetailFragmenttừItemListFragment. Bên trongonViewCreated(), bên dưới lệnh gọi hàm lớp cha, hãy tạo một biến không thể thay đổi có tên làid. Truy xuất và gán đối số điều hướng cho biến mới này.
val id = navigationArgs.itemId
- Bây giờ, bạn sẽ sử dụng biến
idnày để truy xuất thông tin về mặt hàng. Vẫn bên trongonViewCreated(), hãy gọi hàmretrieveItem()trênviewModelvới giá trị truyền vào làid. Đính kèm một trình quan sát vào giá trị được trả về với giá trị truyền vào làviewLifecycleOwnervà một lambda.
viewModel.retrieveItem(id).observe(this.viewLifecycleOwner) {
}
- Bên trong lambda, hãy truyền vào
selectedItemdưới dạng tham số chứa thực thểItemđược truy xuất từ cơ sở dữ liệu. Trong nội dung hàm lambda, hãy gán giá trịselectedItemchoitem. Gọi hàmbind(), truyền vàoitem. Hàm hoàn chỉnh sẽ có dạng như sau.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val id = navigationArgs.itemId
viewModel.retrieveItem(id).observe(this.viewLifecycleOwner) { selectedItem ->
item = selectedItem
bind(item)
}
}
- Chạy ứng dụng. Nhấp vào phần tử trong danh sách bất kỳ trên màn hình Inventory (Kiểm kho), màn hình Item Details (Thông tin về mặt hàng) sẽ xuất hiện. Bạn sẽ nhận thấy rằng màn hình lúc này không trống nữa, màn hình sẽ cho thấy thông tin về thực thể được truy xuất từ cơ sở dữ liệu kho hàng
|
|
- Nhấn vào các nút Sell (Bán), Delete (Xoá) và nút hành động nổi. Không có gì xảy ra cả! Trong các nhiệm vụ tiếp theo, bạn sẽ triển khai chức năng của các nút này.
5. Triển khai mục bán hàng
Trong nhiệm vụ này, bạn sẽ mở rộng các tính năng của ứng dụng, triển khai chức năng bán hàng. Sau đây là thông tin hướng dẫn chi tiết cho bước này.
- Thêm một hàm trong ViewModel để cập nhật một thực thể
- Tạo một phương thức mới để giảm số lượng và cập nhật thực thể trong cơ sở dữ liệu ứng dụng.
- Đính kèm trình nghe lượt nhấp vào nút Sell (Bán)
- Tắt nút Sell (Bán) nếu số lượng là 0.
Hãy lập trình như sau:
- Trong
InventoryViewModel, hãy thêm một hàm riêng tư có tên làupdateItem(). Hàm này sẽ chứa một phiên bản của lớp thực thểItemvà không trả về giá trị nào.
private fun updateItem(item: Item) {
}
- Triển khai phương thức mới,
updateItem(). Để gọi phương thức tạm ngưngupdate()qua lớpItemDao, hãy chạy một coroutine bằngviewModelScope. Bên trong khối khởi động, hãy thực hiện lệnh gọi hàmupdate()trênitemDao, truyền vàoitem. Phương thức hoàn chỉnh sẽ có dạng như sau.
private fun updateItem(item: Item) {
viewModelScope.launch {
itemDao.update(item)
}
}
- Vẫn bên trong
InventoryViewModel, hãy thêm một phương thức khác có tên làsellItem(). Phương thức này sẽ nhận một phiên bản của lớp thực thểItemvà không trả về giá trị nào.
fun sellItem(item: Item) {
}
- Bên trong hàm
sellItem(), hãy thêm một điều kiệnifđể kiểm tra xemitem.quantityInStockcó lớn hơn0hay không.
fun sellItem(item: Item) {
if (item.quantityInStock > 0) {
}
}
Bên trong khối if, bạn sẽ sử dụng hàm copy() cho lớp Data (Dữ liệu) để cập nhật thực thể.
Lớp dữ liệu: copy()
Theo mặc định, hàm copy() được cung cấp cho mọi phiên bản lớp dữ liệu. Hàm này dùng để sao chép một đối tượng để thay đổi một số thuộc tính của đối tượng đó nhưng giữ nguyên các thuộc tính còn lại.
Ví dụ: hãy xem xét lớp User và phiên bản jack của lớp đó như minh hoạ dưới đây. Nếu bạn muốn tạo một phiên bản mới chỉ cập nhật thuộc tính age, thì cách triển khai sẽ như sau:
Ví dụ
// Data class
data class User(val name: String = "", val age: Int = 0)
// Data class instance
val jack = User(name = "Jack", age = 1)
// A new instance is created with its age property changed, rest of the properties unchanged.
val olderJack = jack.copy(age = 2)
- Quay lại hàm
sellItem()trongInventoryViewModel. Bên trong khốiif, hãy tạo một thuộc tính không thể thay đổi có tên lànewItem. Gọi hàmcopy()trên phiên bảnitemvới giá trị truyền vào làquantityInStockđã cập nhật, thao tác này sẽ làm số lượng hàng tồn kho giảm đi1.
val newItem = item.copy(quantityInStock = item.quantityInStock - 1)
- Bên dưới định nghĩa
newItem, hãy thực hiện lệnh gọi đến hàmupdateItem()trong đối tượng mới cập nhật, đó lànewItem. Phương thức hoàn chỉnh sẽ có dạng như sau.
fun sellItem(item: Item) {
if (item.quantityInStock > 0) {
// Decrease the quantity by 1
val newItem = item.copy(quantityInStock = item.quantityInStock - 1)
updateItem(newItem)
}
}
- Để thêm tính năng bán hàng tồn kho, hãy di chuyển đến
ItemDetailFragment. Di chuyển đến cuối hàmbind(). Bên trong khốiapply, hãy thiết lập trình nghe lượt nhấp thành nút Sell (Bán) và gọi hàmsellItem()trênviewModel.
private fun bind(item: Item) {
binding.apply {
...
sellItem.setOnClickListener { viewModel.sellItem(item) }
}
}
- Chạy ứng dụng. Trên màn hình Inventory (Kiểm kho), hãy nhấp vào một phần tử trong danh sách có số lượng lớn hơn 0. Màn hình Item Details (Thông tin về mặt hàng) sẽ xuất hiện. Nhấn vào nút Sell (Bán), bạn sẽ nhận thấy giá trị của số lượng đã giảm đi 1.

- Trên màn hình Item Details (Thông tin về mặt hàng), hãy liên tục nhấn vào nút Sell (Bán) để làm cho số lượng trở thành 0. (Mẹo: Hãy chọn một thực thể có ít hàng tồn kho hoặc tạo một thực thể mới với số lượng ít). Khi số lượng bằng 0, hãy nhấn vào nút Sell (Bán). Sẽ không có thay đổi nào xuất hiện. Điều này là do hàm
sellItem()của bạn kiểm tra xem số lượng có lớn hơn 0 hay không trước khi cập nhật số lượng.

- Để cung cấp cho người dùng phản hồi tốt hơn, bạn nên vô hiệu hoá nút Sell (Bán) khi không có mặt hàng nào để bán. Trong
InventoryViewModel, hãy thêm một hàm để kiểm tra xem số lượng có lớn hơn0hay không. Đặt tên cho hàm làisStockAvailable(). Hàm này lấy một phiên bảnItemvà trả về mộtBoolean.
fun isStockAvailable(item: Item): Boolean {
return (item.quantityInStock > 0)
}
- Di chuyển đến
ItemDetailFragment, rồi di chuyển đến hàmbind(). Trong khối áp dụng, hãy gọi hàmisStockAvailable()trênviewModelvới giá trị truyền vào làitem. Thiết lập giá trị trả về thành thuộc tínhisEnabledcủa nút Sell (Bán). Mã của bạn sẽ có dạng như sau.
private fun bind(item: Item) {
binding.apply {
...
sellItem.isEnabled = viewModel.isStockAvailable(item)
sellItem.setOnClickListener { viewModel.sellItem(item) }
}
}
- Chạy ứng dụng, bạn sẽ nhận thấy rằng nút Sell (Bán) bị tắt khi số lượng hàng tồn kho bằng 0. Chúc mừng bạn đã triển khai thành công tính năng bán hàng cho ứng dụng của mình.

Thực thể xoá mục
Tương tự như nhiệm vụ trước, bạn có thể mở rộng tính năng của ứng dụng bằng cách triển khai chức năng xoá. Sau đây là hướng dẫn nâng cao cho bước này, cách thực hiện dễ hơn nhiều so với việc triển khai tính năng bán.
- Thêm một hàm trong ViewModel để xoá một thực thể khỏi cơ sở dữ liệu
- Thêm một phương thức mới trong
ItemDetailFragmentđể gọi hàm xoá mới và xử lý việc điều hướng. - Đính kèm trình nghe lượt nhấp vào nút Delete (Xoá)
Hãy tiếp tục lập trình:
- Trong
InventoryViewModel, hãy thêm một hàm mới có tên làdeleteItem(). Hàm này sẽ chứa một phiên bản của lớp thực thểItemcó tên làitemvà không trả về giá trị nào. Bên trong hàmdeleteItem(), hãy khởi động một coroutine bằngviewModelScope. Trong khốilaunch, hãy gọi phương thứcdelete()trênitemDaovới giá trị truyền vào làitem.
fun deleteItem(item: Item) {
viewModelScope.launch {
itemDao.delete(item)
}
}
- Trong
ItemDetailFragment, hãy di chuyển đến đầu hàmdeleteItem(). Gọi chodeleteItem()trênviewModel, truyền vàoitem. Phiên bảnitemchứa thực thể hiện đang xuất hiện trên màn hình Item Details (Thông tin chi tiết về mặt hàng). Phương thức hoàn chỉnh sẽ có dạng như sau.
private fun deleteItem() {
viewModel.deleteItem(item)
findNavController().navigateUp()
}
- Vẫn trong
ItemDetailFragment, hãy di chuyển đến hàmshowConfirmationDialog(). Hàm này được cung cấp cho bạn trong đoạn mã khởi đầu. Phương thức này sẽ cung cấp hộp thoại cảnh báo để lấy xác nhận của người dùng trước khi xoá mục và gọi hàmdeleteItem()khi người dùng nhấn vào nút xác nhận.
private fun showConfirmationDialog() {
MaterialAlertDialogBuilder(requireContext())
...
.setPositiveButton(getString(R.string.yes)) { _, _ ->
deleteItem()
}
.show()
}
Hàm showConfirmationDialog() cung cấp hộp thoại cảnh báo như sau:

- Trong
ItemDetailFragment, ở cuối hàmbind(), bên trong khốiapply, hãy thiết lập trình nghe lượt nhấp thành nút xoá. GọishowConfirmationDialog()bên trong hàm lambda của trình nghe lượt nhấp.
private fun bind(item: Item) {
binding.apply {
...
deleteItem.setOnClickListener { showConfirmationDialog() }
}
}
- Chạy ứng dụng! Chọn một phần tử trong danh sách trên màn hình danh sách Inventory (Kiểm kho), trên màn hình Item Details (Thông tin về mặt hàng), nhấn vào nút Delete (Xoá). Nhấn vào Yes (Đồng ý), ứng dụng sẽ quay lại màn hình Inventory (Kiểm kho). Lưu ý rằng đối tượng bạn đã xoá không còn nằm trong cơ sở dữ liệu của ứng dụng. Chúc mừng bạn đã triển khai thành công tính năng xoá.

Thực thể chỉnh sửa mục
Tương tự như các nhiệm vụ trước, trong nhiệm vụ này, bạn sẽ thêm một tính năng nâng cao khác cho ứng dụng. Bạn sẽ triển khai thực thể chỉnh sửa mục.
Sau đây là hướng dẫn ngắn gọn về các bước để chỉnh sửa thực thể trong cơ sở dữ liệu ứng dụng:
- Sử dụng lại màn hình Add Item (Thêm mặt hàng) bằng cách cập nhật tiêu đề mảnh thành Edit Item (Chỉnh sửa mặt hàng).
- Thêm trình nghe lượt nhấp vào nút hành động nổi để chuyển đến màn hình Edit Item (Chỉnh sửa mặt hàng).
- Điền thông tin về thực thể vào TextView.
- Cập nhật thực thể trong cơ sở dữ liệu bằng cách sử dụng Room.
Thêm trình nghe lượt nhấp vào FAB
- Trong
ItemDetailFragment, hãy thêm một hàmprivatemới tên làeditItem(). Hàm này không chứa tham số và không trả về giá trị nào. Trong bước tiếp theo, bạn sẽ sử dụng lạifragment_add_item.xml, bằng cách cập nhật tiêu đề màn hình thành Edit Item (Chỉnh sửa mặt hàng). Để làm được điều này, bạn sẽ gửi chuỗi tiêu đề mảnh cùng với giá trị nhận dạng mặt hàng trong hành động đó.
private fun editItem() {
}
Sau khi bạn cập nhật tiêu đề mảnh, màn hình Edit Item (Chỉnh sửa mặt hàng) sẽ có dạng như sau.

- Bên trong hàm
editItem(), hãy tạo một biến không thể thay đổi (immutable variable) tên làaction. Gọi đếnactionItemDetailFragmentToAddItemFragment()trênItemDetailFragmentDirections, truyền vào chuỗi tiêu đề,edit_fragment_titlevàidcủa mặt hàng. Chỉ định giá trị được trả về choaction. Bên dưới định nghĩaaction, hãy gọithis.findNavController().navigate()với giá trị truyền vào làactionđể chuyển đến màn hình Edit Item (Chỉnh sửa mặt hàng).
private fun editItem() {
val action = ItemDetailFragmentDirections.actionItemDetailFragmentToAddItemFragment(
getString(R.string.edit_fragment_title),
item.id
)
this.findNavController().navigate(action)
}
- Vẫn trong
ItemDetailFragment, hãy di chuyển đến hàmbind(). Bên trong khốiapply, hãy đặt trình nghe lượt nhấp thành nút hành động nổi, gọi hàmeditItem()từ lambda để chuyển đến màn hình Edit Item (Chỉnh sửa mặt hàng).
private fun bind(item: Item) {
binding.apply {
...
editItem.setOnClickListener { editItem() }
}
}
- Chạy ứng dụng. Di chuyển đến màn hình Item Details (Thông tin chi tiết về mặt hàng). Nhấp vào nút hành động nổi. Lưu ý rằng tiêu đề của màn hình sẽ được cập nhật thành Edit Item (Chỉnh sửa mặt hàng), nhưng mọi trường văn bản đều trống. Trong bước tiếp theo, bạn sẽ khắc phục vấn đề này.

Điền TextView
Trong bước này, bạn sẽ điền thông tin về thực thể vào các trường văn bản trong màn hình Edit Item (Chỉnh sửa mặt hàng). Vì chúng tôi đang sử dụng màn hình Add Item nên bạn sẽ thêm các hàm mới vào tệp Kotlin AddItemFragment.kt.
- Trong
AddItemFragment, hãy thêm hàmprivatemới để liên kết các trường văn bản với thông tin về thực thể. Đặt tên cho hàm làbind(). Hàm này chứa một phiên bản của lớp thực thể Item và không trả về giá trị nào.
private fun bind(item: Item) {
}
- Cách triển khai hàm
bind()rất giống với những gì bạn đã làm trongItemDetailFragment. Bên trong hàmbind(), hãy làm tròn giá đến hai chữ số thập phân bằng cách sử dụng hàmformat()và gán giá trị này cho mộtvalcó tên làprice, như minh hoạ dưới đây.
val price = "%.2f".format(item.itemPrice)
- Bên dưới định nghĩa
price, hãy sử dụng hàm phạm viapplytrên thuộc tínhbindingnhư minh hoạ dưới đây.
binding.apply {
}
- Bên trong khối mã của hàm phạm vi
apply, hãy thiết lậpitem.itemNamethành thuộc tính văn bản củaitemName. Sử dụng hàmsetText()rồi truyền vào chuỗiitem.itemNamevàTextView.BufferType.SPANNABLEdưới dạngBufferType.
binding.apply {
itemName.setText(item.itemName, TextView.BufferType.SPANNABLE)
}
Nhập android.widget.TextView nếu được Android Studio nhắc.
- Tương tự như bước trên, hãy thiết lập thuộc tính văn bản của giá
EditTextnhư dưới đây. Để thiết lập thuộc tính số lượngtextcủa EditText, hãy nhớ chuyển đổiitem.quantityInStockthànhString. Hàm hoàn chỉnh sẽ có dạng như sau.
private fun bind(item: Item) {
val price = "%.2f".format(item.itemPrice)
binding.apply {
itemName.setText(item.itemName, TextView.BufferType.SPANNABLE)
itemPrice.setText(price, TextView.BufferType.SPANNABLE)
itemCount.setText(item.quantityInStock.toString(), TextView.BufferType.SPANNABLE)
}
}
- Vẫn bên trong
AddItemFragment, hãy di chuyển đến hàmonViewCreated(). Sau lệnh gọi đến hàm siêu lớp. Tạovalcó tên làidvà truy xuấtitemIdtrong các đối số điều hướng.
val id = navigationArgs.itemId
- Thêm một khối
if-elsecó điều kiện để kiểm tra xemidcó lớn hơn 0 hay không rồi di chuyển trình nghe lượt nhấp nút Save (Lưu) vào khốielse. Trong khốiif, hãy truy xuất thực thể bằng cách sử dụngidrồi thêm trình quan sát trên thực thể đó. Bên trong trình quan sát, hãy cập nhật thuộc tínhitemrồi gọibind()với giá trị truyền vào làitem. Hàm hoàn chỉnh được cung cấp cho bạn để sao chép và dán. Hàm này rất đơn giản và dễ hiểu; bạn có thể tự giải mã.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val id = navigationArgs.itemId
if (id > 0) {
viewModel.retrieveItem(id).observe(this.viewLifecycleOwner) { selectedItem ->
item = selectedItem
bind(item)
}
} else {
binding.saveAction.setOnClickListener {
addNewItem()
}
}
}
- Chạy ứng dụng, chuyển đến phần Item Details (Thông tin về mặt hàng), nhấn vào nút hành động nổi +. Bạn sẽ nhận thấy các trường đã được điền thông tin về mặt hàng. Chỉnh sửa số lượng hàng hoặc các trường khác và nhấn vào nút lưu. Không có gì xảy ra cả! Điều này là do bạn không cập nhật thực thể trong cơ sở dữ liệu của ứng dụng. Bạn sẽ sớm khắc phục vấn đề này.

Sử dụng Room để cập nhật thực thể
Trong nhiệm vụ cuối cùng này, hãy thêm các đoạn mã cuối cùng để triển khai chức năng cập nhật. Bạn sẽ xác định các hàm cần thiết trong ViewModel và sử dụng các hàm đó trong AddItemFragment.
Lại đến lúc lập trình rồi!
- Trong
InventoryViewModel, hãy thêm một hàmprivatecó tên làgetUpdatedItemEntry(). Hàm này chứa mộtIntvà 3 chuỗi cho thông tin về thực thể, các chuỗi này gồmitemName,itemPricevàitemCount. Trả về một phiên bản củaItemqua hàm này. Chúng tôi cung cấp mã để bạn tham khảo.
private fun getUpdatedItemEntry(
itemId: Int,
itemName: String,
itemPrice: String,
itemCount: String
): Item {
}
- Bên trong hàm
getUpdatedItemEntry(), hãy tạo một thực thể Item bằng cách sử dụng các tham số hàm, như minh hoạ dưới đây. Trả về phiên bảnItemqua hàm này.
private fun getUpdatedItemEntry(
itemId: Int,
itemName: String,
itemPrice: String,
itemCount: String
): Item {
return Item(
id = itemId,
itemName = itemName,
itemPrice = itemPrice.toDouble(),
quantityInStock = itemCount.toInt()
)
}
- Vẫn bên trong
InventoryViewModel, hãy thêm một hàm khác có tên làupdateItem(). Hàm này cũng chứaIntvà 3 chuỗi cho thông tin về thực thể và không trả về giá trị nào. Hãy sử dụng tên biến trong đoạn mã sau đây.
fun updateItem(
itemId: Int,
itemName: String,
itemPrice: String,
itemCount: String
) {
}
- Bên trong hàm
updateItem(), gọi hàmgetUpdatedItemEntry()rồi truyền vào thông tin về thực thể dưới dạng tham số hàm, như minh hoạ dưới đây. Chỉ định giá trị trả về cho một biến không thể thay đổi có tên làupdatedItem.
val updatedItem = getUpdatedItemEntry(itemId, itemName, itemPrice, itemCount)
- Ngay bên dưới lệnh gọi hàm
getUpdatedItemEntry(), hãy thực hiện lệnh gọi hàmupdateItem(), truyền vàoupdatedItem. Hàm hoàn chỉnh sẽ có dạng như sau:
fun updateItem(
itemId: Int,
itemName: String,
itemPrice: String,
itemCount: String
) {
val updatedItem = getUpdatedItemEntry(itemId, itemName, itemPrice, itemCount)
updateItem(updatedItem)
}
- Quay lại
AddItemFragment, thêm một hàm riêng tư có tên làupdateItem(), hàm này không có tham số và không trả về giá trị nào. Bên trong hàm này, hãy thêm điều kiệnifđể xác thực hoạt động đầu vào của người dùng bằng cách gọi hàmisEntryValid().
private fun updateItem() {
if (isEntryValid()) {
}
}
- Trong khối
if, hãy thực hiện lệnh gọi đếnviewModel.updateItem()với giá trị truyền vào là thông tin về thực thể. Sử dụngitemIdqua các đối số điều hướng và các thông tin khác về thực thể như tên, giá và số lượng trong EditText như minh hoạ dưới đây.
viewModel.updateItem(
this.navigationArgs.itemId,
this.binding.itemName.text.toString(),
this.binding.itemPrice.text.toString(),
this.binding.itemCount.text.toString()
)
- Bên dưới lệnh gọi hàm
updateItem(), hãy xác định mộtvalcó tên làaction. GọiactionAddItemFragmentToItemListFragment()trênAddItemFragmentDirectionsrồi chỉ định giá trị được trả về choaction. Di chuyển đếnItemListFragment, gọifindNavController().navigate()với giá trị truyền vào làaction.
private fun updateItem() {
if (isEntryValid()) {
viewModel.updateItem(
this.navigationArgs.itemId,
this.binding.itemName.text.toString(),
this.binding.itemPrice.text.toString(),
this.binding.itemCount.text.toString()
)
val action = AddItemFragmentDirections.actionAddItemFragmentToItemListFragment()
findNavController().navigate(action)
}
}
- Vẫn trong
AddItemFragment, hãy di chuyển đến hàmbind(). Bên trong khối hàm phạm vibinding.apply, hãy thiết lập trình nghe lượt nhấp cho nút Save (Lưu). Thực hiện lệnh gọi đến hàmupdateItem()bên trong lambda như minh hoạ dưới đây.
private fun bind(item: Item) {
...
binding.apply {
...
saveAction.setOnClickListener { updateItem() }
}
}
- Chạy ứng dụng! Thử chỉnh sửa các mặt hàng tồn kho; bạn sẽ chỉnh sửa được mọi mặt hàng trong cơ sở dữ liệu của ứng dụng Inventory.

Chúc mừng bạn đã tạo xong ứng dụng đầu tiên sử dụng Room để quản lý cơ sở dữ liệu ứng dụng!
6. Mã giải pháp
Mã nguồn giải pháp cho lớp học lập trình này nằm trong kho lưu trữ và nhánh GitHub dưới đây.
Để lấy mã cho lớp học lập trình này và mở mã trong Android Studio, hãy làm theo các bước sau.
Lấy mã
- Nhấp vào URL được cung cấp. Thao tác này sẽ mở trang GitHub của dự án trong một trình duyệt.
- Trên trang GitHub của dự án, hãy nhấp vào nút Code (Mã), một hộp thoại sẽ xuất hiện.

- Trong hộp thoại này, hãy 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 an existing Android Studio project (Mở một dự án hiện có trong Android Studio).

Lưu ý: Nếu Android Studio đã mở sẵn thì thay vào đó, hãy chọn tuỳ chọn sau đây trong trình đơn File > New > Import Project (Tệp > Mới > Nhập dự án).

- Trong hộp thoại Import Project (Nhập dự án), hãy chuyển đến nơi chứa thư mục dự án đã 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. Đảm bảo ứng dụng chạy đúng như mong đợi. - Duyệt qua các tệp của dự án trong cửa sổ công cụ Project (Dự án) để xem cách triển khai ứng dụng.
7. Tóm tắt
- Kotlin cho phép mở rộng một lớp thông qua chức năng mới mà không cần phải kế thừa từ lớp đó hay sửa đổi định nghĩa hiện có của lớp đó. Bạn có thể làm việc này qua các phần khai báo đặc biệt gọi là phần mở rộng (extension).
- Để sử dụng dữ liệu
Flowdưới dạng giá trịLiveData, hãy sử dụng hàmasLiveData(). - Theo mặc định, hàm
copy()được cung cấp cho mọi phiên bản lớp dữ liệu. Công cụ này cho phép bạn sao chép đối tượng và thay đổi một số thuộc tính của đối tượng đó mà vẫn giữ nguyên các thuộc tính còn lại.
8. Tìm hiểu thêm
Tài liệu dành cho nhà phát triển Android
- Truyền dữ liệu giữa các đích đến
- Chuỗi trong Android
- Trình định dạng Android
- Gỡ lỗi về cơ sở dữ liệu bằng Trình kiểm tra cơ sở dữ liệu
- Lưu dữ liệu trong cơ sở dữ liệu cục bộ bằng Room
Tài liệu tham khảo về API
Tài liệu tham khảo về Kotlin

