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ượngItem
dưới dạng tham số. - Thay đổi chữ ký lớp
ItemListAdapter
để mở rộngListAdapter
. Truyền vàoItem
vàItemListAdapter.ItemViewHolder
dưới dạng tham số. - Thêm tham số hàm khởi tạo
DiffCallback
;ListAdapter
sẽ 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ềViewHolder
mới khi RecyclerView cần. - Bên trong phương thức
onCreateViewHolder()
, hãy tạo mộtView
mới và tăng cường từ tệp bố cụcitem_list_item.xml
bằng cách sử dụng lớpItemListItemBinding
liê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ượngItem
và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
val
thuộ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 TextViewitemName
vớ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ịquantityInStock
thànhString
và 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ộtval
tên làallItems
thuộ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ênitemDao
và 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ínhprivate
không thể thay đổi được gọi làviewModel
thuộ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áoval
có tênadapter
. Khởi tạo thuộc tínhadapter
mớ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
adapter
mới tạo vớirecyclerView
như 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ênadapter
rồ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
val
có 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ênItemListFragmentDirections
vớ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ảnNavController
bằ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ứaInt
dướ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ịFlow
dướ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àitem
củ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ínhprivate
không thể thay đổi tên làviewModel
thuộ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àmprivate
có tên làbind()
. Hàm này sẽ lấy một phiên bản của thực thểItem
là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ínhtext
của TextViewitemName
thà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ínhtext
của TextViewitemPrice
. Chuyển đổiquantityInStock
thànhString
và thiết lập thành thuộc tínhtext
củ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
ItemDetailFragment
từ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
id
này để truy xuất thông tin về mặt hàng. Vẫn bên trongonViewCreated()
, hãy gọi hàmretrieveItem()
trênviewModel
vớ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àviewLifecycleOwner
và một lambda.
viewModel.retrieveItem(id).observe(this.viewLifecycleOwner) {
}
- Bên trong lambda, hãy truyền vào
selectedItem
dướ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ịselectedItem
choitem
. 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ểItem
và 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ểItem
và 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.quantityInStock
có lớn hơn0
hay 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ảnitem
vớ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ơn0
hay không. Đặt tên cho hàm làisStockAvailable()
. Hàm này lấy một phiên bảnItem
và 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ênviewModel
với giá trị truyền vào làitem
. Thiết lập giá trị trả về thành thuộc tínhisEnabled
củ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ểItem
có tên làitem
và 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ênitemDao
vớ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ảnitem
chứ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àmprivate
mớ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_title
vàid
củ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àmprivate
mớ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ộtval
có 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 viapply
trên thuộc tínhbinding
như 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.itemName
thành thuộc tính văn bản củaitemName
. Sử dụng hàmsetText()
rồi truyền vào chuỗiitem.itemName
vàTextView.BufferType.SPANNABLE
dướ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á
EditText
như dưới đây. Để thiết lập thuộc tính số lượngtext
của EditText, hãy nhớ chuyển đổiitem.quantityInStock
thà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ạoval
có tên làid
và truy xuấtitemId
trong các đối số điều hướng.
val id = navigationArgs.itemId
- Thêm một khối
if-else
có điều kiện để kiểm tra xemid
có 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ụngid
rồ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ínhitem
rồ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àmprivate
có tên làgetUpdatedItemEntry()
. Hàm này chứa mộtInt
và 3 chuỗi cho thông tin về thực thể, các chuỗi này gồmitemName
,itemPrice
vàitemCount
. Trả về một phiên bản củaItem
qua 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ảnItem
qua 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ứaInt
và 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ụngitemId
qua 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ộtval
có tên làaction
. GọiactionAddItemFragmentToItemListFragment()
trênAddItemFragmentDirections
rồ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
Flow
dướ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