Đọc và cập nhật dữ liệu thông qua Room

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.

771c6a677ecd96c7.png

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ã

  1. 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.
  2. 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.

5b0a76c50478a73f.png

  1. 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.
  2. 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)).
  3. 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

  1. Khởi động Android Studio.
  2. 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).

36cc44fcf0f89a1d.png

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).

21f3eec988dcfbe9.png

  1. 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)).
  2. Nhấp đúp vào thư mục dự án đó.
  3. Chờ Android Studio mở dự án.
  4. Nhấp vào nút Run (Chạy) 11c34fc5e516fb1c.png để tạo và chạy ứng dụng. Đảm bảo ứng dụng chạy đúng như mong đợi.
  5. 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.

d6e7b7b9f12e7a16.png

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.

  1. 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.

  1. 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ượng Item dưới dạng tham số.
  2. Thay đổi chữ ký lớp ItemListAdapter để mở rộng ListAdapter. Truyền vào ItemItemListAdapter.ItemViewHolder dưới dạng tham số.
  3. 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.
  4. Ghi đè phương thức bắt buộc onCreateViewHolder()onBindViewHolder().
  5. Phương thức onCreateViewHolder() sẽ trả về ViewHolder mới khi RecyclerView cần.
  6. Bên trong phương thức onCreateViewHolder(), hãy tạo một View mới và tăng cường từ tệp bố cục item_list_item.xml bằng cách sử dụng lớp ItemListItemBinding liên kết được tạo tự động.
  7. 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ức getItem() với giá trị truyền vào là vị trí.
  8. Thiết lập trình nghe lượt nhấp (click listener) trên itemView, gọi hàm onItemClicked() bên trong trình nghe đó.
  9. Định nghĩa lớp ItemViewHolder, mở rộng lớp này từ RecyclerView.ViewHolder.. Ghi đè hàm bind(), truyền đối tượng Item vào hàm.
  10. Đị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ại DiffUtil.ItemCallback<Item>() tên là DiffCallback. Ghi đè các phương thức cần có là areItemsTheSame()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.

9c416f2fbf1e5ae2.png

  1. Trong ItemListAdapter.kt, hãy triển khai hàm bind() trong lớp ItemViewHolder. Liên kết TextView itemName với item.itemName. Lấy giá ở định dạng tiền tệ bằng cách sử dụng hàm mở rộng getFormattedPrice() và liên kết giá trị đó với TextView itemPrice. Chuyển đổi giá trị quantityInStock thành String và liên kết với TextView itemQuantity. 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 InventoryViewModelItemListFragment để 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.

  1. Ở đầu lớp InventoryViewModel, tạo một val tên là allItems thuộc loại LiveData<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.

  1. Gọi getItems() trên itemDao và chỉ định cho allItems. Hàm getItems() trả về Flow. Để sử dụng dữ liệu dưới dạng giá trị LiveData, hãy sử dụng hàm asLiveData(). Đị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.

  1. Trong ItemListFragment, ở đầu lớp, khai báo thuộc tính private không thể thay đổi được gọi là viewModel thuộc loại InventoryViewModel. Sử dụng đối tượng uỷ quyền bằng by để chuyển giao lệnh khởi tạo thuộc tính cho lớp activityViewModels. Truyền vào hàm khởi tạo InventoryViewModelFactory.
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.

  1. Vẫn trong ItemListFragment, hãy cuộn đến hàm onViewCreated(). Bên dưới lệnh gọi tới super.onViewCreated(), hãy khai báo val có tên adapter. Khởi tạo thuộc tính adapter mới bằng hàm khởi tạo mặc định, ItemListAdapter{} không truyền giá trị nào vào.
  2. Liên kết adapter mới tạo với recyclerView như sau:
val adapter = ItemListAdapter {
}
binding.recyclerView.adapter = adapter
  1. 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ên allItems để theo dõi các thay đổi về dữ liệu.
  2. Bên trong trình quan sát, hãy gọi submitList() trên adapter 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)
   }
}
  1. Đả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)
   }
}

9c416f2fbf1e5ae2.png

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.

d699618f5d9437df.png

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

  1. Trong ItemListFragment, hãy di chuyển đến hàm onViewCreated() để cập nhật định nghĩa trình chuyển đổi.
  2. Thêm một lambda làm tham số hàm khởi tạo vào ItemListAdapter{}.
val adapter = ItemListAdapter {
}
  1. 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
}
  1. Gọi phương thức actionItemListFragmentToItemDetailFragment() trên ItemListFragmentDirections với giá trị truyền vào là mục id. Chỉ định đối tượng NavDirections được trả về cho action.
val adapter = ItemListAdapter {
   val action =    ItemListFragmentDirections.actionItemListFragmentToItemDetailFragment(it.id)
}
  1. Dưới định nghĩa action, hãy truy xuất một phiên bản NavController bằng cách sử dụng this.findNavController() rồi gọi navigate() 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)
}
  1. 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.

196553111ee69beb.png

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).

  1. Trong InventoryViewModel, hãy thêm một hàm tên là retrieveItem(). Hàm này chứa Int 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> {
}
  1. Bên trong hàm mới, hãy gọi getItem() trên itemDao, với tham số truyền vào là id. Hàm getItem() trả về Flow. Để sử dụng giá trị Flow dưới dạng LiveData, hãy gọi hàm asLiveData() và sử dụng hàm này làm hàm trả về của hàm retrieveItem(). 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.

  1. 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ại Item. 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.

  1. Ở đầu lớp ItemDetailFragment, hãy khai báo thuộc tính private không thể thay đổi tên là viewModel thuộc loại InventoryViewModel. Sử dụng đối tượng uỷ quyền bằng by để chuyển giao lệnh khởi tạo thuộc tính cho lớp activityViewModels. Truyền vào hàm khởi tạo InventoryViewModelFactory.
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.

  1. Vẫn trong ItemDetailFragment, hãy tạo một hàm private 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) {
}
  1. Triển khai hàm bind(), tương tự như những gì bạn đã làm trong ItemListAdapter. Thiết lập thuộc tính text của TextView itemName thành item.itemName. Gọi getFormattedPrice() trên thuộc tính item để định dạng giá của mặt hàng và thiết lập giá trị đó thành thuộc tính text của TextView itemPrice. Chuyển đổi quantityInStock thành String và thiết lập thành thuộc tính text của TextView itemQuantity.
private fun bind(item: Item) {
   binding.itemName.text = item.itemName
   binding.itemPrice.text = item.getFormattedPrice()
   binding.itemCount.text = item.quantityInStock.toString()
}
  1. Cập nhật hàm bind() để sử dụng hàm phạm vi apply{} đố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()
   }
}
  1. Vẫn ở trong ItemDetailFragment, hãy ghi đè onViewCreated().
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
   super.onViewCreated(view, savedInstanceState)
}
  1. 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 trong onViewCreated(), 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
  1. 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 trong onViewCreated(), hãy gọi hàm retrieveItem() trên viewModel 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) {
   }
  1. 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 cho item. Gọi hàm bind(), truyền vào item. 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)
   }
}
  1. 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

  1. 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:

  1. 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) {
}
  1. Triển khai phương thức mới, updateItem(). Để gọi phương thức tạm ngưng update() qua lớp ItemDao, hãy chạy một coroutine bằng viewModelScope. Bên trong khối khởi động, hãy thực hiện lệnh gọi hàm update() trên itemDao, truyền vào item. Phương thức hoàn chỉnh sẽ có dạng như sau.
private fun updateItem(item: Item) {
   viewModelScope.launch {
       itemDao.update(item)
   }
}
  1. 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) {
}
  1. Bên trong hàm sellItem(), hãy thêm một điều kiện if để kiểm tra xem item.quantityInStock có lớn hơn 0 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)
  1. Quay lại hàm sellItem() trong InventoryViewModel. Bên trong khối if, hãy tạo một thuộc tính không thể thay đổi có tên là newItem. Gọi hàm copy() trên phiên bản item 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 đi 1.
val newItem = item.copy(quantityInStock = item.quantityInStock - 1)
  1. Bên dưới định nghĩa newItem, hãy thực hiện lệnh gọi đến hàm updateItem() 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)
   }
}
  1. Để 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àm bind(). Bên trong khối apply, hãy thiết lập trình nghe lượt nhấp thành nút Sell (Bán) và gọi hàm sellItem() trên viewModel.
private fun bind(item: Item) {
binding.apply {

...
    sellItem.setOnClickListener { viewModel.sellItem(item) }
    }
}
  1. 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.

aa63ca761dc8f009.png

  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.

3e099d3c55596938.png

  1. Để 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ơn 0 hay không. Đặt tên cho hàm là isStockAvailable(). Hàm này lấy một phiên bản Item và trả về một Boolean.
fun isStockAvailable(item: Item): Boolean {
   return (item.quantityInStock > 0)
}
  1. Di chuyển đến ItemDetailFragment, rồi di chuyển đến hàm bind(). Trong khối áp dụng, hãy gọi hàm isStockAvailable() trên viewModel với giá trị truyền vào là item. Thiết lập giá trị trả về thành thuộc tính isEnabled 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) }
   }
}
  1. 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.

5e49db8451e77c2b.png

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:

  1. 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àm deleteItem(), hãy khởi động một coroutine bằng viewModelScope. Trong khối launch, hãy gọi phương thức delete() trên itemDao với giá trị truyền vào là item.
fun deleteItem(item: Item) {
   viewModelScope.launch {
       itemDao.delete(item)
   }
}
  1. Trong ItemDetailFragment, hãy di chuyển đến đầu hàm deleteItem(). Gọi cho deleteItem() trên viewModel, truyền vào item. Phiên bản item 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()
}
  1. Vẫn trong ItemDetailFragment, hãy di chuyển đến hàm showConfirmationDialog(). 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àm deleteItem() 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:

728bfcbb997c8017.png

  1. Trong ItemDetailFragment, ở cuối hàm bind(), bên trong khối apply, hãy thiết lập trình nghe lượt nhấp thành nút xoá. Gọi showConfirmationDialog() 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() }
   }
}
  1. 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á.

c05318ab8c216fa1.png

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

  1. Trong ItemDetailFragment, hãy thêm một hàm private 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ại fragment_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.

bcd407af7c515a21.png

  1. 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 đến actionItemDetailFragmentToAddItemFragment() trên ItemDetailFragmentDirections, truyền vào chuỗi tiêu đề, edit_fragment_titleid của mặt hàng. Chỉ định giá trị được trả về cho action. Bên dưới định nghĩa action, hãy gọi this.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)
}
  1. Vẫn trong ItemDetailFragment, hãy di chuyển đến hàm bind(). Bên trong khối apply, hãy đặt trình nghe lượt nhấp thành nút hành động nổi, gọi hàm editItem() 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() }
   }
}
  1. 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.

a6a6583171b68230.png

Đ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.

  1. Trong AddItemFragment, hãy thêm hàm private 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) {
}
  1. Cách triển khai hàm bind() rất giống với những gì bạn đã làm trong ItemDetailFragment. Bên trong hàm bind(), hãy làm tròn giá đến hai chữ số thập phân bằng cách sử dụng hàm format() và gán giá trị này cho một val có tên là price, như minh hoạ dưới đây.
val price = "%.2f".format(item.itemPrice)
  1. Bên dưới định nghĩa price, hãy sử dụng hàm phạm vi apply trên thuộc tính binding như minh hoạ dưới đây.
binding.apply {
}
  1. Bên trong khối mã của hàm phạm vi apply, hãy thiết lập item.itemName thành thuộc tính văn bản của itemName. Sử dụng hàm setText() rồi truyền vào chuỗi item.itemNameTextView.BufferType.SPANNABLE dưới dạng BufferType.
binding.apply {
   itemName.setText(item.itemName, TextView.BufferType.SPANNABLE)
}

Nhập android.widget.TextView nếu được Android Studio nhắc.

  1. 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ượng text của EditText, hãy nhớ chuyển đổi item.quantityInStock thành String. 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)
   }
}
  1. Vẫn bên trong AddItemFragment, hãy di chuyển đến hàm onViewCreated(). Sau lệnh gọi đến hàm siêu lớp. Tạo val có tên là id và truy xuất itemId trong các đối số điều hướng.
val id = navigationArgs.itemId
  1. Thêm một khối if-else có điều kiện để kiểm tra xem id 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ối else. Trong khối if, hãy truy xuất thực thể bằng cách sử dụng id 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ính item rồi gọi bind() 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()
       }
   }
}
  1. 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.

829ceb9dd7993215.png

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!

  1. Trong InventoryViewModel, hãy thêm một hàm private có tên là getUpdatedItemEntry(). Hàm này chứa một Int và 3 chuỗi cho thông tin về thực thể, các chuỗi này gồm itemName, itemPriceitemCount. Trả về một phiên bản của Item 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 {
}
  1. 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ản Item 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()
   )
}
  1. 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ứa Int 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
) {
}
  1. Bên trong hàm updateItem(), gọi hàm getUpdatedItemEntry() 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)
  1. Ngay bên dưới lệnh gọi hàm getUpdatedItemEntry(), hãy thực hiện lệnh gọi hàm updateItem(), truyền vào updatedItem. 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)
}
  1. 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ện if để xác thực hoạt động đầu vào của người dùng bằng cách gọi hàm isEntryValid().
private fun updateItem() {
   if (isEntryValid()) {
   }
}
  1. Trong khối if, hãy thực hiện lệnh gọi đến viewModel.updateItem() với giá trị truyền vào là thông tin về thực thể. Sử dụng itemId 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()
)
  1. Bên dưới lệnh gọi hàm updateItem(), hãy xác định một val có tên là action. Gọi actionAddItemFragmentToItemListFragment() trên AddItemFragmentDirections rồi chỉ định giá trị được trả về cho action. Di chuyển đến ItemListFragment, gọi findNavController().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)
   }
}
  1. Vẫn trong AddItemFragment, hãy di chuyển đến hàm bind(). Bên trong khối hàm phạm vi binding.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àm updateItem() bên trong lambda như minh hoạ dưới đây.
private fun bind(item: Item) {
   ...
   binding.apply {
       ...
       saveAction.setOnClickListener { updateItem() }
   }
}
  1. 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.

1bbd094a77c25fc4.png

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ã

  1. 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.
  2. 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.

5b0a76c50478a73f.png

  1. 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.
  2. 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)).
  3. 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

  1. Khởi động Android Studio.
  2. 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).

36cc44fcf0f89a1d.png

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).

21f3eec988dcfbe9.png

  1. 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)).
  2. Nhấp đúp vào thư mục dự án đó.
  3. Chờ Android Studio mở dự án.
  4. Nhấp vào nút Run (Chạy) 11c34fc5e516fb1c.png để tạo và chạy ứng dụng. Đảm bảo ứng dụng chạy đúng như mong đợi.
  5. 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àm asLiveData().
  • 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

Tài liệu tham khảo về API

Tài liệu tham khảo về Kotlin