Vai trò của giao diện người dùng là hiển thị dữ liệu ứng dụng trên màn hình cũng như đóng vai trò là điểm chính trong quá trình tương tác của người dùng. Bất cứ khi nào dữ liệu thay đổi, do sự tương tác của người dùng (chẳng hạn như nhấn vào một nút) hoặc đầu vào bên ngoài (chẳng hạn như phản hồi mạng), giao diện người dùng sẽ cập nhật để phản ánh các thay đổi đó. Về mặt hiệu quả, giao diện người dùng là đại diện trực quan của trạng thái ứng dụng như được truy xuất từ lớp dữ liệu. Show
Tuy nhiên, dữ liệu ứng dụng bạn nhận được từ lớp dữ liệu thường ở một định dạng khác với thông tin bạn cần hiển thị. Ví dụ: bạn có thể chỉ cần một phần dữ liệu cho giao diện người dùng hoặc bạn có thể cần hợp nhất hai nguồn dữ liệu khác nhau để trình bày thông tin có liên quan đến người dùng. Bất kể logic bạn áp dụng là gì, bạn đều cần chuyển cho giao diện người dùng tất cả các thông tin cần thiết để giao diện hiển thị đầy đủ. Lớp giao diện người dùng là quy trình chuyển đổi các thay đổi dữ liệu ứng dụng thành dạng thức mà giao diện người dùng có thể trình bày và sau đó hiển thị. ![Trong một cấu trúc thông thường, các thành phần trên giao diện người dùng của lớp giao diện người dùng phụ thuộc vào
Nghiên cứu điển hình cơ bảnXem xét một ứng dụng tìm nạp các bài viết tin tức để người dùng đọc. Ứng dụng có màn hình bài viết trình bày các bài viết có sẵn để đọc, đồng thời cũng cho phép người dùng đã đăng nhập đánh dấu các bài viết thực sự nổi bật. Do có thể có rất nhiều bài viết tại bất cứ thời điểm nào, nên người đọc có thể cần duyệt qua các bài viết theo danh mục. Tóm lại, ứng dụng cho phép người dùng thực hiện những nội dung sau:
Các phần sau đây sử dụng ví dụ này như một nghiên cứu điển hình để giới thiệu các nguyên tắc về luồng dữ liệu một chiều, cũng như minh họa các vấn đề được các nguyên tắc này giúp giải quyết trong bối cảnh cấu trúc ứng dụng cho lớp giao diện người dùng. Cấu trúc lớp giao diện người dùngThuật ngữGiao diện người dùng đề cập đến các thành phần trên giao diện người dùng như các hoạt động và các mảnh hiển thị dữ liệu, độc lập với các API mà các thành phần đó sử dụng để thực hiện việc này (Chế độ xem hoặc Jetpack Compose ). Vì vai trò củalớp dữ liệu là giữ, quản lý và cung cấp quyền truy cập vào dữ liệu ứng dụng, nên lớp giao diện người dùng phải thực hiện các bước sau:
Phần còn lại của hướng dẫn này minh họa cách triển khai lớp giao diện người dùng thực hiện các bước này. Cụ thể, hướng dẫn này bao gồm các tác vụ và khái niệm sau:
Yếu tố cơ bản nhất trong số này là việc xác định trạng thái giao diện người dùng. Xác định trạng thái giao diện người dùngTham khảo đã nêu ở trên. Tóm lại, giao diện người dùng hiển thị danh sách các bài viết cùng với một số siêu dữ liệu của mỗi bài viết. Thông tin này chính là trạng thái giao diện người dùng mà ứng dụng hiển thị cho người dùng. Nói cách khác: nếu giao diện người dùng là nội dung mà người dùng nhìn thấy, thì trạng thái giao diện người dùng chính là nội dung mà ứng dụng biểu thị người dùng sẽ thấy. Giống như hai mặt của một đồng xu, giao diện người dùng là đại diện hình ảnh của trạng thái giao diện người dùng. Bất kỳ thay đổi nào đối với trạng thái giao diện người dùng đều được phản ánh ngay lập tức trong giao diện người dùng. Figure 3. Giao diện người dùng là kết quả của việc liên kết các thành phần trên giao diện người dùng trên màn hình với trạng thái giao diện người dùng.Hãy cân nhắc nghiên cứu điển hình; để đáp ứng các yêu cầu của ứng dụng Tin tức, thông tin cần phải có để hiển thị đầy đủ giao diện người dùng có thể được gói gọn trong một lớp dữ liệu
4 được xác định như sau:
Bất biếnĐịnh nghĩa về trạng thái giao diện người dùng trong ví dụ ở trên là bất biến. Lợi ích chính của thuộc tính này là các đối tượng bất biến cung cấp các đảm bảo đối với trạng thái của ứng dụng ngay lập tức. Việc này giải phóng giao diện người dùng để tập trung vào một vai trò duy nhất: đọc trạng thái và cập nhật các thành phần trên giao diện người dùng cho phù hợp. Do đó, bạn không bao giờ nên trực tiếp sửa đổi trạng thái giao diện người dùng trong giao diện người dùng, trừ khi giao diện người dùng đó là nguồn duy nhất của dữ liệu. Việc vi phạm nguyên tắc này sẽ dẫn đến nhiều nguồn thông tin chính xác cho cùng một phần thông tin, dẫn đến những điểm thiếu đồng nhất của dữ liệu và các lỗi nhỏ. Ví dụ: nếu cập nhật thao tác gắn cờ
5 vào trong đối tượng
6 từ trạng thái giao diện người dùng trong nghiên cứu điển hình tại lớp
7, thì cờ đó sẽ xung đột với lớp dữ liệu làm nguồn trạng thái được đánh dấu trang của một bài viết. Các lớp dữ liệu bất biến rất hữu ích để ngăn chặn kiểu phản mẫu này. Quy ước đặt tên trong hướng dẫn nàyTrong hướng dẫn này, các lớp trạng thái giao diện người dùng được đặt tên dựa trên chức năng của màn hình hoặc một phần của màn hình mà lớp đó mô tả. Quy ước như sau: functionality + UiState. Ví dụ: trạng thái của màn hình hiển thị tin tức có thể gọi lệnh
4, còn trạng thái của một mục tin tức trong danh sách mục tin tức có thể là
6. Quản lý trạng thái bằng Luồng dữ liệu một chiềuPhần trước đã thiết lập trạng thái giao diện người dùng là hiện trạng bất biến của các chi tiết cần thiết để giao diện người dùng hiển thị. Tuy nhiên, tính chất động của dữ liệu trong ứng dụng có nghĩa là trạng thái có thể thay đổi theo thời gian. Điều này có thể là do sự tương tác của người dùng hoặc các sự kiện khác làm thay đổi dữ liệu cơ bản được dùng để điền sẵn ứng dụng. Một trình trung gian có thể mang lại lợi ích xử lý các tương tác này. Thao tác xử lý xác định logic sẽ được áp dụng cho mỗi sự kiện và thực hiện các biến đổi cần thiết cho các nguồn dữ liệu sao lưu để tạo trạng thái giao diện người dùng. Những tương tác này và logic của chúng có thể được đặt trong chính giao diện người dùng. Nhưng điều này có thể nhanh chóng trở nên khó sử dụng vì giao diện người dùng bắt đầu chuyển đổi trở nên phức tạp hơn so với tên gọi của nó: giao diện người dùng trở thành chủ sở hữu dữ liệu, nơi sản xuất, nơi chuyển đổi, v.v Hơn nữa, hoạt động xử lý này có thể ảnh hưởng đến khả năng thử nghiệm vì mã kết quả là một sự kết hợp chặt chẽ mà không có ranh giới rõ ràng. Cuối cùng, hoạt động giảm gánh nặng đó sẽ có lợi cho giao diện người dùng. Trừ khi trạng thái giao diện người dùng rất đơn giản, trách nhiệm duy nhất của giao diện người dùng là sử dụng và hiển thị trạng thái giao diện người dùng. Phần này thảo luận về Luồng dữ liệu một chiều (UDF), một mẫu cấu trúc giúp thực thi quá trình phân tách trách nhiệm lành mạnh này. Chủ thể trạng tháiCác lớp chịu trách nhiệm tạo trạng thái giao diện người dùng và chứa logic cần thiết mà từ đó tác vụ được gọi là chủ thể trạng thái. Các chủ thể trạng thái có nhiều kích thước, tuỳ thuộc vào phạm vi của các thành phần trên giao diện người dùng tương ứng mà người dùng quản lý, từ một tiện ích đơn lẻ như thanh ứng dụng ở dưới cùng cho đến toàn bộ màn hình hoặc một đích đến điều hướng. Trong trường hợp sau, hoạt động triển khai điển hình là phiên bản của ViewModel, mặc dù tuỳ thuộc vào các yêu cầu của ứng dụng, nhưng bạn chỉ nên sử dụng một lớp đơn giản. Ví dụ: Ứng dụng Tin tức từ sử dụng một lớp
0 làm chủ thể trạng thái để tạo trạng thái giao diện người dùng cho màn hình được hiển thị trong phần đó. Có nhiều cách để lập mô hình hiện tượng đồng phụ thuộc giữa giao diện người dùng và tính năng nhà sản xuất trạng thái của giao diện đó. Tuy nhiên, do sự tương tác giữa giao diện người dùng và lớp
1 của giao diện phần lớn có thể được hiểu là sự kiện đầu vào và đảm bảo trạng thái đầu ra sau đó, mối quan hệ có thể được thể hiện như trong sơ đồ sau: Hình 4. Sơ đồ về cách hoạt động của UDF trong ứng dụng kiến trúc .Mô hình mà trạng thái giảm xuống và sự kiện tăng lên được gọi là luồng dữ liệu một chiều (UDF). Các hoạt động di chuyển kéo theo của mô hình này cho cấu trúc ứng dụng là như sau:
Đối với các đích đến hoặc màn hình điều hướng, ViewModel hoạt động với các kho lưu trữ hoặc sử dụng các lớp điển hình để lấy và chuyển đổi dữ liệu thành trạng thái giao diện người dùng trong khi kết hợp tác động của các sự kiện có thể gây ra đột biến trạng thái. đã đề cập trước đó có chứa một danh sách các bài viết, mỗi bài viết có tiêu đề, phần mô tả, nguồn, tên tác giả, ngày xuất bản và có được đánh dấu trang hay không. Giao diện người dùng cho mỗi mục trong bài viết như sau: Hình 5. Giao diện người dùng của một mục bài viết trong ứng dụng điển hình.Việc một người dùng yêu cầu đánh dấu trang một bài viết là ví dụ về một sự kiện có thể gây ra những đột biến trạng thái. Là nhà sản xuất trạng thái, trách nhiệm của ViewModel là xác định tất cả các logic cần phải đó để điền sẵn tất cả các trường trong trạng thái giao diện người dùng và xử lý các sự kiện cần thiết để giao diện người dùng hiển thị đầy đủ. ![Sự kiện giao diện người dùng xảy ra khi người dùng đánh dấu trang một bài viết. ViewModel
Các phần sau sẽ tìm hiểu kỹ hơn về các sự kiện gây ra thay đổi trạng thái và cách xử lý chúng bằng UDF.Các loại logicĐánh dấu một bài viết là ví dụ về logic kinh doanh vì bài viết này cung cấp giá trị cho ứng dụng của bạn. Để tìm hiểu thêm về điều này, hãy xem tranglớp dữ liệu. Tuy nhiên, có nhiều loại logic quan trọng cần xác định:
Logic giao diện người dùng, đặc biệt là khi liên quan đến các loại giao diện người dùng như
3, phải nằm trong giao diện người dùng chứ không phải trong ViewModel. Nếu giao diện người dùng phát triển phức tạp và bạn muốn ủy quyền logic giao diện người dùng cho một lớp khác để ưu tiên khả năng thử nghiệm và phân tách các mối quan ngại, bạn có thể tạo một lớp đơn giản như là một chủ thể trạng thái. Các lớp đơn giản được tạo trong giao diện người dùng có thể sử dụng các phần phụ thuộc SDK Android vì các lớp này tuân theo vòng đời của giao diện người dùng; các đối tượng ViewModel có thời gian tồn tại lâu hơn. Để biết thêm thông tin về chủ thể trạng thái và mức độ phù hợp với bối cảnh của chúng trong việc giúp xây dựng giao diện người dùng, hãy xem . Tại sao nên sử dụng UDF?UDF lập mô hình chu kỳ tạo trạng thái như hiển thị trong Hình 4. Cơ chế này cũng tách biệt nơi bắt đầu thay đổi trạng thái, nơi có thay đổi về trạng thái và cuối cùng nơi sử dụng trạng thái. Việc phân tách này cho phép giao diện người dùng thực hiện chính xác nội dung của tên giao diện: hiển thị thông tin bằng cách quan sát các thay đổi trạng thái, và chuyển tiếp ý định của người dùng bằng cách truyền các thay đổi đó tới ViewModel. Nói cách khác, UDF cho phép nội dung sau:
Hiển thị trạng thái giao diện người dùngSau khi bạn xác định trạng thái giao diện người dùng và xác định cách bạn sẽ quản lý việc tạo trạng thái, bước tiếp theo là hiển thị trạng thái đã được tạo cho giao diện người dùng. Vì bạn đang sử dụng UDF để quản lý việc tạo ra trạng thái, bạn có thể coi trạng thái đã được tạo là một sự kiện phát trực tiếp—nói cách khác, nhiều phiên bản của trạng thái sẽ được tạo theo thời gian. Do đó, bạn nên hiển thị trạng thái giao diện người dùng trong một chủ thẻ dữ liệu có thể quan sát như
4 hoặc
5. Lý do là vì giao diện người dùng có thể phản ứng với bất kỳ thay đổi nào được thực hiện trong trạng thái không phải lấy dữ liệu trực tiếp từ ViewModel. Các loại này có lợi ích luôn lưu vào bộ nhớ đệm phiên bản mới nhất của trạng thái giao diện người dùng. Hoạt động này rất hữu ích trong việc khôi phục trạng thái nhanh chóng sau khi thay đổi cấu hình. Số lượt xem
Compose
Để biết thông tin giới thiệu về
4 với tư cách là chủ thể dữ liệu có thể quan sát, hãy xem codelab này. Để có phần giới thiệu tương tự về luồng Kotlin, hãy xem các luồng Kotlin trên Android. Trong các trường hợp dữ liệu được hiển thị với giao diện người dùng tương đối đơn giản, thì thường nên gói dữ liệu trong một loại trạng thái giao diện người dùng vì loại trạng thái này truyền tải mối quan hệ giữa sự phát xạ của chủ thể trạng thái và màn hình hoặc thành phần trên giao diện người dùng được liên kết. Hơn nữa, khi thành phần trên giao diện người dùng trở nên dùng phức tạp hơn, sẽ dễ dàng hơn khi thêm định nghĩa về trạng thái giao diện người dùng nhằm cung cấp thêm thông tin cần thiết để hiển thị thành phần trên giao diện người dùng. Một cách phổ biến để tạo một trình phát trực tiếp
7 là bằng cách hiển thị một trình phát trực tiếp có thể thay đổi hỗ trợ dưới dạng trình phát bất biến từ ViewModel—ví dụ: hiển thị
8 dưới dạng
9. Số lượt xem
Compose
Khi đó, ViewModel có thể hiển thị các phương thức gây đột biến bên trong trạng thái, mà các đột biến này sẽ phát hành thông tin cập nhật để giao diện người dùng sử dụng. Ví dụ: trong trường hợp một hành động không đồng bộ cần được thực hiện; một coroutine có thể khởi chạy bằng cách sử dụng và trạng thái bất biến có khả năng được cập nhật sau khi hoàn thành. Số lượt xem
Compose
Trong ví dụ trên, lớp
0 thử tìm nạp các bài viết cho một danh mục nhất định, rồi phản ánh kết quả của lần thử—cho dù thành công hay không thành công—ở trạng thái giao diện người dùng trong đó giao diện người dùng có thể phản ứng với lớp một cách phù hợp. Hãy xem mục để tìm hiểu thêm về cách xử lý lỗi. Những điểm cần lưu ý khácNgoài hướng dẫn trước, hãy cân nhắc các điều sau đây khi hiển thị trạng thái giao diện người dùng:
Sử dụng trạng thái giao diện người dùngĐể sử dụng trình phát trực tiếp của đối tượng
7 trong giao diện người dùng, bạn sử dụng toán tử đầu cuối cho loại dữ liệu có khả năng ghi nhận mà bạn đang sử dụng. Ví dụ: đối với
4, bạn sử dụng phương thức
0 và đối với các luồng Kotlin, bạn sử dụng phương thức
1 hoặc các biến thể của phương thức này. Khi sử dụng chủ thể sở hữu dữ liệu có thể quan sát trong giao diện người dùng, hãy đảm bảo rằng bạn xem xét vòng đời của giao diện người dùng. Việc này rất quan trọng vì giao diện người dùng sẽ không quan sát trạng thái giao diện người dùng khi chế độ xem không hiển thị cho người dùng. Để tìm hiểu thêm về chủ đề này, hãy xem bài đăng trên blog này. Khi sử dụng
4,
3 ngầm quan tâm tới các vấn đề liên quan vòng đời. Khi sử dụng luồng, cách tốt nhất là xử lý luồng với phạm vi coroutine thích hợp và API
4: Số lượt xem
Compose
Hiển thị thao tác đang diễn raMột cách đơn giản để biểu thị các trạng thái đang tải trong một lớp
7 là có trường boolean:
0 Giá trị của cờ này đại diện cho việc có hoặc không có thanh tiến trình trong giao diện người dùng. Số lượt xem
1 Compose
2 Hiển thị lỗi trên màn hìnhViệc hiển thị lỗi trong giao diện người dùng tương tự như hiển thị thao tác đang diễn ra vì chúng đều dễ dàng biểu thị bằng các giá trị boolean chỉ rõ sự hiện diện hoặc không hiện diện của chúng. Tuy nhiên, lỗi cũng có thể bao gồm thông báo liên quan để chuyển tiếp trở lại tới người dùng hoặc một hành động được liên kết với chúng để thử lại thao tác không thành công. Do đó, mặc dù thao tác đang diễn ra là thực hiện tải hoặc không tải dữ liệu, nhưng các trạng thái lỗi có thể cần được mô hình hoá với các lớp dữ liệu lưu trữ siêu dữ liệu phù hợp với ngữ cảnh của lỗi. Ví dụ: hãy xem xét ví dụ từ phần trước, hiển thị một thanh tiến trình trong khi tìm nạp bài viết. Nếu thao tác này dẫn đến lỗi, bạn có thể muốn hiển thị một hoặc nhiều thông báo cho người dùng biết chi tiết về sự cố.
3 Sau đó, thông báo lỗi có thể được hiển thị cho người dùng dưới dạng các thành phần giao diện người dùng như thanh thông báo nhanh. Điều này liên quan đến cách các sự kiện giao diện người dùng được tạo và sử dụng, hãy xem trang các sự kiện giao diện người dùng để biết thêm thông tin. Chuỗi lệnh và tính đồng thờiMọi tác vụ được thực hiện trong ViewModel nên là luồng chính–an toàn để gọi lệnh từ chuỗi lệnh chính. Lý do là do dữ liệu và các lớp miền chịu trách nhiệm về việc chuyển tác vụ sang một chuỗi lệnh khác. Nếu một ViewModel thực hiện các tác vụ lâu dài, sao đó nó cũng chịu trách nhiệm di chuyển logic đó sang một chuỗi lệnh ở chế độ nền. Các coroutine Kotlin là một cách tuyệt vời để quản lý các thao tác đồng thời và chúng được thành phần cấu trúc Jetpack cung cấp hỗ trợ tích hợp. Để tìm hiểu thêm về cách sử dụng coroutine trong các ứng dụng Android, hãy xem Coroutine Kotlin trên Android. Tính năng điều hướngCác thay đổi trong điều hướng ứng dụng thường được các sự kiện tương tự trình phát tạo ra. Ví dụ: sau khi một lớp
6 thực hiện đăng nhập,
7 có thể có một trường
8 đặt với
9. Những trình kích hoạt như vậy nên được sử dụng giống như các trình kích hoạt được đề cập trong mục bên trên, ngoại trừ hoạt động triển khai cần phải tuân theo Thành phần điều hướng Đánh số trangThư viện phân trang được sử dụng trong giao diện người dùng với một loại lệnh gọi
0. Vì
0 đại diện và chứa các mục có thể thay đổi theo thời gian. Nói cách khác, thuộc tính này không phải là một loại bất biến nên không nên được biểu thị ở trạng thái giao diện người dùng bất biến. Thay vào đó, bạn nên hiển thị giao diện từ ViewModel một cách độc lập trong trình phát trực tiếp riêng của nó. Hãy xem lớp học lập trình Đánh số trang Android để biết ví dụ cụ thể về nội dung này. Ảnh độngĐể cung cấp chuyển đổi điều hướng ở cấp cao nhất một cách linh hoạt và nhịp nhàng, có thể bạn cần đợi màn hình thứ hai tải dữ liệu trước khi bắt đầu ảnh động. Khung chế độ xem Android cung cấp các giao diện kết nối để trì hoãn việc chuyển đổi giữa các đích đến của mảnh với các API và . Các API này cung cấp phương thức để đảm bảo rằng các thành phần giao diện người dùng trên màn hình thứ hai (thường là một hình ảnh tìm nạp từ mạng) sẵn sàng hiển thị trước khi giao diện người dùng tạo ảnh động cho quá trình chuyển đổi sang màn hình đó. Để biết thêm thông tin chi tiết và các thông số kỹ thuật triển khai cụ thể, hãy xem Mẫu chuyển động Android. |