Android Kotlin Fundamentals 02.4: Kiến thức cơ bản về liên kết dữ liệu

Lớp học lập trình này nằm trong khóa học về Khái niệm cơ bản về Android Kotlin. Bạn sẽ nhận được nhiều giá trị nhất từ khóa học này nếu bạn làm việc qua các lớp học lập trình theo trình tự. Tất cả các lớp học lập trình trong khóa học đều có trên trang đích của các lớp học lập trình cơ bản về Android Kotlin.

Giới thiệu

Trong các lớp học lập trình trước đây trong khóa học này, bạn đã sử dụng hàm findViewById() để lấy thông tin tham chiếu đến chế độ xem. Khi ứng dụng của bạn có hệ phân cấp chế độ xem phức tạp, findViewById() sẽ tốn kém và làm chậm ứng dụng vì Android chuyển qua hệ phân cấp chế độ xem, bắt đầu từ gốc cho đến khi tìm được chế độ xem mong muốn. Rất may là có một cách tốt hơn.

Để đặt dữ liệu trong các chế độ xem, bạn đã sử dụng tài nguyên chuỗi và đặt dữ liệu từ hoạt động. Sẽ hiệu quả hơn nếu chế độ xem đó có dữ liệu. Và may mắn thay, điều này hoàn toàn có thể xảy ra.

Trong lớp học lập trình này, bạn sẽ tìm hiểu cách sử dụng liên kết dữ liệu để loại bỏ nhu cầu sử dụng findViewById(). Bạn cũng tìm hiểu cách sử dụng liên kết dữ liệu để truy cập dữ liệu trực tiếp từ một chế độ xem.

Kiến thức bạn cần có

Bạn cần thông thạo:

  • Hoạt động là gì và cách thiết lập hoạt động bằng bố cục trong onCreate().
  • Tạo chế độ xem văn bản và đặt văn bản mà chế độ xem văn bản hiển thị.
  • Sử dụng findViewById() để lấy tệp tham chiếu đến một chế độ xem.
  • Tạo và chỉnh sửa bố cục XML cơ bản cho chế độ xem.

Kiến thức bạn sẽ học được

  • Cách sử dụng Thư viện liên kết dữ liệu để loại bỏ các lệnh gọi không hiệu quả đến findViewById().
  • Cách truy cập dữ liệu ứng dụng ngay từ XML.

Bạn sẽ thực hiện

  • Sửa đổi ứng dụng để dùng liên kết dữ liệu thay vì findViewById() và truy cập dữ liệu ngay từ tệp XML của bố cục.

Trong lớp học lập trình này, bạn bắt đầu bằng ứng dụng aboutMe và thay đổi ứng dụng để dùng liên kết dữ liệu. Ứng dụng sẽ trông giống hệt nhau khi bạn hoàn tất!

Sau đây là chức năng của ứng dụng Giới thiệu:

  • Khi người dùng mở ứng dụng, ứng dụng sẽ hiển thị tên, trường để nhập biệt hiệu, nút Xong, hình ảnh dấu sao và văn bản có thể cuộn.
  • Người dùng có thể nhập biệt hiệu rồi nhấn vào nút Xong. Trường và nút có thể chỉnh sửa sẽ được thay thế bằng chế độ xem văn bản hiển thị biệt hiệu đã nhập.


Bạn có thể sử dụng mã đã tạo trong lớp học lập trình trước đó hoặc bạn có thể tải mã AboutMeDataBinding-Starter xuống từ GitHub.

Mã bạn đã viết trong các lớp học lập trình trước đây sử dụng hàm findViewById() để lấy thông tin tham chiếu đến chế độ xem.

Mỗi khi bạn sử dụng findViewById() để tìm kiếm một chế độ xem sau khi chế độ xem được tạo hoặc tạo lại, hệ thống Android sẽ phân cấp hệ phân cấp chế độ xem vào thời gian chạy để tìm chế độ xem đó. Khi ứng dụng của bạn chỉ có một số ít lượt xem thì đây không phải là vấn đề. Tuy nhiên, ứng dụng chính thức có thể có hàng chục chế độ xem trong bố cục và thậm chí với thiết kế tốt nhất, sẽ có các chế độ xem lồng nhau.

Hãy nghĩ về bố cục tuyến tính chứa chế độ xem cuộn có chứa chế độ xem văn bản. Đối với một hệ phân cấp chế độ xem lớn hoặc sâu, việc tìm chế độ xem có thể mất đủ thời gian để có thể làm chậm đáng kể ứng dụng cho người dùng. Bạn có thể sử dụng chế độ xem bộ nhớ đệm trong các biến, nhưng vẫn phải khởi chạy biến cho mỗi chế độ xem, trong từng vùng chứa tên. Với nhiều chế độ xem và nhiều hoạt động, cộng đồng này cũng tăng lên.

Một giải pháp là tạo đối tượng chứa thông tin tham chiếu đến từng chế độ xem. Đối tượng này (được gọi là đối tượng Binding) có thể được toàn bộ ứng dụng của bạn dùng. Kỹ thuật này được gọi là liên kết dữ liệu. Sau khi đối tượng liên kết đã được tạo cho ứng dụng của bạn, bạn có thể truy cập vào các chế độ xem và dữ liệu khác thông qua đối tượng liên kết đó mà không phải chuyển qua hệ phân cấp chế độ xem hoặc tìm kiếm dữ liệu.

Liên kết dữ liệu có những lợi ích sau:

  • Mã ngắn hơn, dễ đọc hơn và dễ bảo trì hơn mã sử dụng findByView().
  • Dữ liệu và chế độ xem được phân tách rõ ràng. Lợi ích của việc liên kết dữ liệu ngày càng trở nên quan trọng trong khóa học này.
  • Hệ thống Android chỉ chuyển qua hệ phân cấp chế độ xem một lần để lấy từng chế độ xem. Điều này xảy ra trong quá trình khởi động của ứng dụng chứ không phải vào thời gian chạy khi người dùng đang tương tác với ứng dụng.
  • Bạn nhận được loại an toàn để truy cập vào các chế độ xem. (An toàn về loại có nghĩa là trình biên dịch xác thực các loại trong khi biên dịch và gây ra lỗi nếu bạn cố chỉ định sai loại cho một biến.)

Trong tác vụ này, bạn thiết lập liên kết dữ liệu và sử dụng liên kết dữ liệu để thay thế các lệnh gọi tới findViewById() bằng các lệnh gọi đến đối tượng liên kết.

Bước 1: Bật liên kết dữ liệu

Để sử dụng liên kết dữ liệu, bạn cần bật liên kết dữ liệu trong tệp Gradle của mình vì chỉ số này không được bật theo mặc định. Lý do là việc liên kết dữ liệu làm tăng thời gian biên dịch và có thể ảnh hưởng đến thời gian khởi động ứng dụng.

  1. Nếu bạn không có ứng dụng aboutMe từ một lớp học lập trình trước, hãy lấy mã AboutMeDataBinding-Starter từ GitHub. Mở Android Studio.
  2. Mở tệp build.gradle (Module: app).
  3. Bên trong phần android, trước dấu ngoặc đóng, hãy thêm phần dataBinding và đặt enabled thành true.
dataBinding {
    enabled = true
}
  1. Khi được nhắc, hãy Đồng bộ hóa dự án. Nếu bạn không thấy lời nhắc, hãy chọn Tệp > Dự án đồng bộ hóa với tệp Gradle.
  2. Bạn có thể chạy ứng dụng, nhưng sẽ không thấy bất kỳ thay đổi nào.

Bước 2: Thay đổi tệp bố cục để dùng được bằng cách liên kết dữ liệu

Để làm việc với liên kết dữ liệu, bạn cần bao gồm bố cục XML bằng thẻ <layout>. Mục đích là để lớp gốc không còn là nhóm chế độ xem nữa, mà là một bố cục chứa các nhóm chế độ xem và chế độ xem. Sau đó, đối tượng liên kết có thể biết về bố cục và các chế độ xem trong bố cục.

  1. Mở tệp activity_main.xml.
  2. Chuyển sang thẻ Văn bản.
  3. Thêm <layout></layout> làm thẻ ngoài cùng xung quanh <LinearLayout>.
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
  1. Chọn Mã > Định dạng lại mã để sửa thụt lề mã.

    Khai báo vùng chứa tên cho bố cục phải nằm trong thẻ ngoài cùng.
  1. Cắt phần khai báo vùng chứa tên khỏi <LinearLayout> và dán vào thẻ <layout>. Thẻ <layout> mở của bạn sẽ trông giống như bên dưới và thẻ <LinearLayout> chỉ được chứa các thuộc tính chế độ xem.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
  1. Tạo và chạy ứng dụng của bạn để xác minh rằng bạn đã thực hiện đúng việc này.

Bước 3: Tạo một đối tượng liên kết trong hoạt động chính

Thêm một thông tin tham chiếu đến đối tượng liên kết vào hoạt động chính để bạn có thể sử dụng thông tin tham chiếu đến các chế độ xem:

  1. Mở tệp MainActivity.kt.
  2. Trước onCreate(), ở cấp cao nhất, hãy tạo một biến cho đối tượng liên kết. Biến này thường được gọi là binding.

    Loại binding, lớp ActivityMainBinding, do trình biên dịch tạo riêng cho hoạt động chính này. Tên này được lấy từ tên của tệp bố cục, tức là activity_main + Binding.
private lateinit var binding: ActivityMainBinding
  1. Nếu Android Studio nhắc, hãy nhập ActivityMainBinding. Nếu bạn không được nhắc, hãy nhấp vào ActivityMainBinding và nhấn Alt+Enter (Option+Enter trên máy Mac) để nhập lớp học bị thiếu này. (Để biết thêm phím tắt, hãy xem phần Phím tắt.)

    Câu lệnh import sẽ giống với câu lệnh bên dưới.
import com.example.android.aboutme.databinding.ActivityMainBinding

Tiếp theo, bạn thay thế hàm setContentView() hiện tại bằng một lệnh có tác dụng:

  • Tạo đối tượng liên kết.
  • Dùng hàm setContentView() trong lớp DataBindingUtil để liên kết bố cục activity_main với MainActivity. Hàm setContentView() này cũng đảm nhiệm một số thiết lập liên kết dữ liệu cho các chế độ xem.
  1. Trong onCreate(), hãy thay thế lệnh gọi setContentView() bằng dòng mã sau.
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  1. Nhập DataBindingUtil.
import androidx.databinding.DataBindingUtil

Bước 4: Sử dụng đối tượng liên kết để thay thế tất cả các lệnh gọi tới findViewById()

Giờ đây, bạn có thể thay thế tất cả các lệnh gọi thành findViewById() bằng thông tin tham chiếu đến các chế độ xem nằm trong đối tượng liên kết. Khi đối tượng liên kết được tạo, trình biên dịch sẽ tạo tên của các chế độ xem trong đối tượng liên kết từ mã của các chế độ xem trong bố cục, chuyển đổi các chế độ xem đó thành chữ hoa lạc đà. Ví dụ: done_buttondoneButton trong đối tượng liên kết, nickname_edit trở thành nicknameEditnickname_text trở thành nicknameText.

  1. Trong onCreate(), hãy thay mã sử dụng findViewById() để tìm done_button bằng mã tham chiếu đến nút trong đối tượng liên kết.

    Thay thế mã này: findViewById<Button>(R.id.done_button)
    bằng: binding.doneButton

    Mã đã hoàn tất để đặt trình xử lý lượt nhấp trong onCreate() sẽ có dạng như sau.
binding.doneButton.setOnClickListener {
   addNickname(it)
}
  1. Làm như vậy cho tất cả các lệnh gọi đến findViewById() trong hàm addNickname().
    Thay thế tất cả các lần xuất hiện của findViewById<View>(R.id.id_view) bằng binding.idView. Hãy làm theo cách sau:
  • Xóa định nghĩa cho các biến editTextnicknameTextView cùng với các lệnh gọi của chúng cho findViewById(). Việc này sẽ gây ra lỗi cho bạn.
  • Sửa lỗi bằng cách lấy chế độ xem nicknameText, nicknameEditdoneButton từ đối tượng binding thay vì biến (đã xóa).
  • Thay thế view.visibility với binding.doneButton.visibility. Việc sử dụng binding.doneButton thay vì view đã chuyển sẽ giúp mã này nhất quán hơn.

    Kết quả là mã sau:
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
  • Không có thay đổi nào về chức năng. Giờ đây, bạn có thể loại bỏ thông số view và cập nhật tất cả các cách sử dụng view để sử dụng binding.doneButton bên trong hàm này.
  1. nicknameText yêu cầu phải có String, còn nicknameEdit.textEditable. Khi sử dụng liên kết dữ liệu, bạn cần phải chuyển đổi Editable thành String một cách rõ ràng.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
  1. Bạn có thể xóa dữ liệu nhập màu xám.
  2. Lập trình hàm bằng cách sử dụng apply{}.
binding.apply {
   nicknameText.text = nicknameEdit.text.toString()
   nicknameEdit.visibility = View.GONE
   doneButton.visibility = View.GONE
   nicknameText.visibility = View.VISIBLE
}
  1. Tạo và chạy ứng dụng của bạn...và ứng dụng sẽ trông giống như trước đây.

Bạn có thể tận dụng liên kết dữ liệu để cung cấp trực tiếp một lớp dữ liệu cho một chế độ xem. Kỹ thuật này đơn giản hóa mã và cực kỳ có giá trị để xử lý các trường hợp phức tạp hơn.

Ví dụ này, thay vì đặt tên và biệt hiệu bằng tài nguyên chuỗi, bạn sẽ tạo một lớp dữ liệu cho tên và biệt hiệu. Bạn cung cấp lớp dữ liệu cho chế độ xem bằng cách sử dụng liên kết dữ liệu.

Bước 1: Tạo lớp dữ liệu MyName

  1. Trong Android Studio trong thư mục java, hãy mở tệp MyName.kt. Nếu bạn không có tệp này, hãy tạo một tệp Kotlin mới và gọi tệp đó là MyName.kt.
  2. Xác định một lớp dữ liệu cho tên và biệt hiệu. Sử dụng các chuỗi trống làm giá trị mặc định.
data class MyName(var name: String = "", var nickname: String = "")

Bước 2: Thêm dữ liệu vào bố cục

Trong tệp activity_main.xml, tên hiện được đặt trong TextView từ một tài nguyên chuỗi. Bạn cần phải thay thế tham chiếu đến tên bằng tham chiếu đến dữ liệu trong lớp dữ liệu.

  1. Mở activity_main.xml trong thẻ Văn bản.
  2. Ở đầu bố cục, giữa các thẻ <layout><LinearLayout>, hãy chèn thẻ <data></data>. Đây là nơi bạn sẽ kết nối chế độ xem với dữ liệu.
<data>
  
</data>

Bên trong các thẻ dữ liệu, bạn có thể khai báo các biến được đặt tên để tham chiếu đến một lớp.

  1. Bên trong thẻ <data>, hãy thêm thẻ <variable>.
  2. Thêm thông số name để đặt tên cho biến là "myName". Thêm một thông số type và đặt loại đó thành tên đủ điều kiện cho lớp dữ liệu MyName (tên gói + tên biến).
<variable
       name="myName"
       type="com.example.android.aboutme.MyName" />

Bây giờ, thay vì dùng tài nguyên chuỗi cho tên, bạn có thể tham chiếu đến biến myName.

  1. Thay thế android:text="@string/name" bằng mã bên dưới.

@={} là một lệnh để nhận dữ liệu được tham chiếu bên trong dấu ngoặc nhọn.

myName tham chiếu biến myName mà bạn đã xác định trước đó. Biến này trỏ đến lớp dữ liệu myName và tìm nạp thuộc tính name từ lớp này.

android:text="@={myName.name}"

Bước 3: Tạo dữ liệu

Giờ đây, bạn có thể tham chiếu đến dữ liệu trong tệp bố cục. Tiếp theo, bạn sẽ tạo dữ liệu thực tế.

  1. Mở tệp MainActivity.kt.
  2. Trên onCreate(), hãy tạo một biến riêng tư, còn được gọi là myName theo quy ước. Gán biến cho một dữ liệu của lớp MyName, chuyển vào tên.
private val myName: MyName = MyName("Aleks Haecky")
  1. Trong onCreate(), hãy đặt giá trị của biến myName trong tệp bố cục thành giá trị của biến myName mà bạn vừa khai báo. Bạn không thể truy cập trực tiếp vào biến trong XML. Bạn cần truy cập vào thông qua đối tượng liên kết.
binding.myName = myName
  1. Thao tác này có thể hiển thị lỗi vì bạn cần làm mới đối tượng liên kết sau khi thực hiện thay đổi. Tạo ứng dụng của bạn và lỗi sẽ biến mất.

Bước 4: Dùng lớp dữ liệu cho biệt hiệu trong Chế độ xem văn bản

Bước cuối cùng là dùng lớp dữ liệu cho biệt hiệu trong TextView.

  1. Mở activity_main.xml.
  2. Trong chế độ xem văn bản nickname_text, hãy thêm một thuộc tính text. Tham chiếu đến nickname trong lớp dữ liệu, như minh họa bên dưới.
android:text="@={myName.nickname}"
  1. Trong ActivityMain, hãy thay thế
    nicknameText.text = nicknameEdit.text.toString()
    bằng mã để đặt biệt hiệu trong biến myName.
myName?.nickname = nicknameEdit.text.toString()

Sau khi đặt biệt hiệu, bạn muốn mã làm mới giao diện người dùng bằng dữ liệu mới. Để làm điều này, bạn phải vô hiệu hóa tất cả biểu thức liên kết để các biểu thức này được tạo lại với đúng dữ liệu.

  1. Thêm invalidateAll() sau khi đặt biệt hiệu để giao diện người dùng được làm mới với giá trị trong đối tượng liên kết đã cập nhật.
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
  1. Tạo và chạy ứng dụng của bạn cũng như hoạt động giống như trước đây.

Dự án Android Studio: AboutMeDataBinding

Các bước để sử dụng liên kết dữ liệu để thay thế lệnh gọi tới findViewById():

  1. Bật liên kết dữ liệu trong phần Android của tệp build.gradle:
    dataBinding { enabled = true }
  2. Hãy dùng <layout> làm chế độ xem gốc trong bố cục XML của bạn.
  3. Xác định một biến liên kết:
    private lateinit var binding: ActivityMainBinding
  4. Tạo một đối tượng liên kết trong MainActivity, thay thế setContentView:
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  5. Thay thế các lệnh gọi tới findViewById() bằng tệp tham chiếu đến chế độ xem trong đối tượng liên kết. Ví dụ:
    findViewById<Button>(R.id.done_button) ⇒ binding.doneButton
    (Trong ví dụ này, tên của chế độ xem được tạo từ trường hợp lạc đà từ id3 của chế độ xem).

Các bước để liên kết chế độ xem với dữ liệu:

  1. Tạo một lớp dữ liệu cho dữ liệu của bạn.
  2. Thêm một khối <data> bên trong thẻ <layout>.
  3. Xác định <variable> bằng tên và loại là lớp dữ liệu.
<data>
   <variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
</data>
  1. Trong MainActivity, hãy tạo một biến có một bản sao của lớp dữ liệu. Ví dụ:
    private val myName: MyName = MyName("Aleks Haecky")
  1. Trong đối tượng liên kết, hãy đặt biến thành biến mà bạn vừa tạo:
    binding.myName = myName
  1. Trong XML, hãy đặt nội dung của chế độ xem thành biến mà bạn đã xác định trong khối <data>. Sử dụng ký hiệu dấu chấm để truy cập dữ liệu bên trong lớp dữ liệu.
    android:text="@={myName.name}"

Khóa học từ Udacity:

Tài liệu dành cho nhà phát triển Android:

Phần này liệt kê các bài tập về nhà có thể được giao cho học viên đang làm việc qua lớp học lập trình này trong khóa học do người hướng dẫn tổ chức. Người hướng dẫn có thể làm những việc sau:

  • Giao bài tập về nhà nếu được yêu cầu.
  • Trao đổi với học viên cách nộp bài tập về nhà.
  • Chấm điểm bài tập về nhà.

Người hướng dẫn có thể sử dụng những đề xuất này ít hay nhiều tùy ý. Do đó, họ có thể thoải mái giao bất kỳ bài tập về nhà nào khác mà họ cảm thấy phù hợp.

Nếu bạn đang tự mình làm việc qua lớp học lập trình này, hãy thoải mái sử dụng các bài tập về nhà này để kiểm tra kiến thức của bạn.

Trả lời những câu hỏi này

Câu hỏi 1

Tại sao bạn muốn giảm thiểu cuộc gọi tục tĩu và ngầm ẩn cho findViewById()?

  • Mỗi khi được gọi, findViewById() chuyển tải qua hệ phân cấp chế độ xem.
  • findViewById() chạy trên luồng chính hoặc luồng giao diện người dùng.
  • Các lệnh gọi này có thể làm chậm giao diện người dùng.
  • Ứng dụng của bạn ít có khả năng gặp sự cố hơn.

Câu hỏi 2

Bạn sẽ mô tả liên kết dữ liệu như thế nào?

Ví dụ: dưới đây là một số điều bạn có thể nói về liên kết dữ liệu:

  • Ý tưởng lớn về liên kết dữ liệu là tạo một đối tượng kết nối/liên kết/liên kết hai phần thông tin với nhau vào thời điểm biên dịch để bạn không phải tìm kiếm dữ liệu trong thời gian chạy.
  • Đối tượng hiển thị các liên kết này với bạn được gọi là đối tượng liên kết.
  • Đối tượng liên kết do trình biên dịch tạo ra.

Câu hỏi 3

Câu nào sau đây KHÔNG phải là lợi ích của việc liên kết dữ liệu?

  • Mã ngắn hơn, dễ đọc hơn và dễ bảo trì hơn.
  • Dữ liệu và chế độ xem được phân tách rõ ràng.
  • Hệ thống Android chỉ chuyển qua hệ phân cấp chế độ xem một lần để lấy mỗi chế độ xem.
  • Việc gọi findViewById() sẽ tạo ra lỗi trình biên dịch.
  • Nhập an toàn để truy cập vào chế độ xem.

Câu hỏi 4

Chức năng của thẻ <layout> là gì?

  • Bạn gói xung quanh chế độ xem gốc trong bố cục.
  • Liên kết được tạo cho tất cả chế độ xem trong bố cục.
  • Lệnh này chỉ định chế độ xem cấp cao nhất trong bố cục XML sử dụng liên kết dữ liệu.
  • Bạn có thể sử dụng thẻ <data> bên trong <layout> để liên kết một biến với một lớp dữ liệu.

Câu hỏi 5

Đâu là cách chính xác để tham chiếu dữ liệu liên kết trong bố cục XML?

  • android:text="@={myDataClass.property}"
  • android:text="@={myDataClass}"
  • android:text="@={myDataClass.property.toString()}"
  • android:text="@={myDataClass.bound_data.property}"

Bắt đầu bài học tiếp theo: 3.1: Tạo một mảnh

Để biết đường liên kết đến các lớp học lập trình khác trong khóa học này, hãy xem trang đích của các lớp học lập trình cơ bản về Android Kotlin.