Chuyển đến nội dung chính

Using Jetpack libraries in Compose | Session

 


Xin chào tất cả mọi người, tôi ở đây để nói về Jetpack Compose, bộ công cụ giao diện

người dùng mới của chúng tôi, hoạt động tốt như thế nào với các thư viện Jetpack hiện có

mà bạn có thể đã sử dụng trong ứng dụng của mình.

Một trong những phần quan trọng nhất khi thiết kế Compose là bạn có thể dần dần áp dụng

nó với các ứng dụng hiện có của mình mà không cần viết lại mọi thứ từ đầu. Điều đó có nghĩa

là doanh nghiệp của bạn và các lớp dữ liệu bên dưới lớp Giao diện người dùng có thể được

sử dụng với Giao diện người dùng Compose mới sáng bóng của chúng tôi. Ví dụ, hãy lấy

ứng dụng này, Bloom.



Bloom là một ứng dụng mua sắm trong nhà và vườn cung cấp cho người dùng khả năng

tìm kiếm thông qua một bộ thực vật khổng lồ và xem qua các bộ sưu tập cây có liên quan

được tạo sẵn. Hãy đi sâu vào cách chúng ta có thể viết màn hình này trong Compose thay vì

sử dụng Views và XML. Tất cả điều đó với sức mạnh của Jetpack.

Trong trường hợp của Bloom, chúng tôi đã tận dụng rất nhiều thư viện Jetpack khác. Nếu

chúng ta xem xét kiến trúc của ứng dụng, Bloom sử dụng Room để lưu trữ các loại cây trong

cơ sở dữ liệu. Phân trang để tải chúng trong các phần hiệu quả của bộ nhớ, ViewModels để

xử lý logic giao diện người dùng, Kotlin coroutines Luồng để hiển thị trạng thái và dữ liệu

giữa mỗi lớp và Hilt là giải pháp tiêm phụ thuộc.



Bloom’s HomeViewModel phục vụ hai mục đích chính.



Đầu tiên, nó cung cấp sự tách biệt giữa trạng thái được hiển thị với giao diện người dùng

và cách trạng thái đó được tạo ra. Chính ViewModel chịu trách nhiệm xử lý logic nghiệp vụ

của Home UI.


Thứ hai, vì nó mở rộng lớp Jetpack ViewModel, nó vẫn tồn tại các thay đổi cấu hình, đảm

bảo dữ liệu mới nhất có sẵn ngay lập tức. Danh sách các loài thực vật được phân trang

được hiển thị từ lớp repo và phần còn lại của trạng thái giao diện người dùng cho màn hình

này bao gồm bộ sưu tập các loài thực vật và tín hiệu liên quan trong trường hợp trang đang

tải hoặc cần hiển thị lỗi.



Nhìn vào HomeScreen, chúng ta có thể chia nó thành một số phần tổng hợp riêng biệt cho

từng phần của giao diện người dùng của chúng tôi, mỗi phần chỉ lấy dữ liệu mà nó cần.



Bắt đầu triển khai HomeScreen, chúng ta có thể lấy một phiên bản của HomeViewModel ở

dạng có thể kết hợp bằng cách sử dụng phương thức ViewModel(). Phương pháp này

đang làm hai điều cho bạn.


Đầu tiên, nó tự động xác định phạm vi ViewModel đến ViewModelStoreOwner gần nhất.

Nếu chúng tôi đặt HomeScreen của mình trực tiếp vào một hoạt động, bạn có thể sử dụng

điều đó. Nếu nó nằm trong Fragment, nó sẽ sử dụng phạm vi Fragment để thay thế.


Thứ hai, nó đang sử dụng Factory mặc định. Đối với trường hợp Hoạt động có chú

thích AndroidEntryPoint hoặc Phân đoạn, Hilt đã được cài đặt chính nó làm nhà máy mặc

định, vì vậy ViewModel do Hilt hỗ trợ của chúng tôi hoạt động ngay lập tức.


Nếu bạn không sử dụng Hilt hoặc bất kỳ giải pháp chèn phụ thuộc nào khác, bạn sẽ cần

tạo ViewModel Factory bằng tay và chuyển nó vào hàm viewModel.

Trong Compose, bạn nên ưu tiên các bản tổng hợp không giữ trạng thái để có thể tái sử

dụng và kiểm tra tốt hơn. Chúng tôi gọi chúng là vật thể tổng hợp không trạng thái thay vì

những vật thể trạng thái giữ trạng thái bên trong.



Trong có thể kết hợp này, HomeScreen đang truy xuất một thể hiện của HomeViewModel

bên trong chính hàm, điều này làm cho nó trở thành một thể tổng hợp trạng thái. Để làm cho

nó không trạng thái, thay vào đó chúng ta nên cung cấp ViewModel dưới dạng một tham

số. Bằng cách này, chúng tôi ủy thác trách nhiệm lấy ViewModel cho cha mẹ để có thể tổng

hợp này tập trung vào việc tạo ra giao diện người dùng.



Khi chúng ta có một phiên bản của ViewModel, chúng ta có thể thu thập luồng uiState

bằng cách sử dụng hàm collectAsState. Thao tác này sẽ thực thi lại tệp tổng hợp nơi trạng

thái được đọc và sẽ cập nhật giao diện người dùng tương ứng với các giá trị mới.

Nếu bạn sử dụng LiveData hoặc RxJava, đừng lo lắng. Chúng tôi cũng hỗ trợ bạn sử dụng

các chức năng ObserverAsState và subscribeAsState.



Chúng tôi đã nói rằng chúng tôi có rất nhiều loại cây trong Bloom nên việc sử dụng Paging

để tải chúng theo từng phần rất có ý nghĩa. Paging Compose giúp bạn có thể chuyển đổi

Flow of PagingData của chúng ta thành một trạng thái mà chúng ta có thể sử dụng

trong Compose bằng phương thức collectAsLazyPagingItems(). Với điều đó, chúng ta có thể

dễ dàng hiển thị chúng bằng LazyColumn.



Ngoài ra, bằng cách nhìn vào loadState, chúng ta có thể dễ dàng thêm khả năng kết hợp tải

của riêng mình để cho biết khi nào dữ liệu được làm mới và ngay cả khi chúng ta xuống

cuối màn hình trong khi nhiều dữ liệu hơn đang được thêm vào danh sách của chúng ta.



Và bây giờ, trên HomeScreen, chúng ta có thể gọi PlantList với dữ liệu đến từ ViewModel.


Trước khi tiếp tục, hãy tạm dừng để xác nhận rằng chúng tôi đã sử dụng rất nhiều thư

viện Jetpack trong Compose UI của chúng tôi mà không sửa đổi ViewModel hoặc thay đổi

bất kỳ lớp nào khác trong ứng dụng của chúng tôi. Thật tuyệt, phải không?


Nhìn vào thiết kế ứng dụng của chúng tôi, có một phần giao diện người dùng mà chúng tôi

có thể muốn sử dụng lại trong các màn hình khác và đó là Browse CollectionsCarousel có

thể được sử dụng lại cho các đề xuất trên PlantDetailScreen của chúng tôi, như bạn có thể

thấy ở đây. Điều thú vị về băng chuyền này là khi bạn chạm vào một mục, nó sẽ mở rộng

để hiển thị các loại cây có liên quan đến bộ sưu tập đó. Trong quá trình triển khai, băng

chuyền cần logic và trạng thái bên trong để tự mở rộng hoặc thu gọn, cũng như xử lý logic

để chỉ cho phép một bộ sưu tập được mở rộng và chuyển đến PlantDetailScreen khi

người dùng chạm vào cây. Chúng ta đặt logic đó ở đâu? Trong ViewModel của chúng tôi?



Điều đó là không thể đối với các thành phần giao diện người dùng có thể tái sử dụng. Hãy

đi sâu vào vấn đề này.



Để làm cho CollectionsCarousel có thể tái sử dụng, chúng tôi làm theo các phương pháp

hay nhất về Compose bằng cách chuyển trạng thái vào và hiển thị các sự kiện. Đóng gói

logic và kiểm soát trạng thái của tệp có thể tổng hợp trong một lớp là một cách tốt để bảo vệ

tệp có thể kết hợp hiển thị giao diện người dùng không nhất quán và tránh lặp lại cùng một

mã ở những nơi khác nhau.



Đối với trạng thái của CollectionsCarousel, chúng ta có thể tạo một lớp thông thường được

gọi là CollectionsCarouselState lấy các bộ sưu tập làm tham số.



Nó hiển thị thông tin về việc liệu băng chuyền có được mở rộng bộ sưu tập đã chọn hay

không và các loại cây sẽ hiển thị.



Và nó đóng gói logic để cập nhật trạng thái khi một tập hợp được nhấp vào trong một

phương thức điểm nhập duy nhất, onCollectionClick. Trạng thái này hiện được cung cấp cho

tệp tổng hợp dưới dạng tham số mà người gọi CollectionsCarousel cần tạo và quản lý. Đối

với các sự kiện, CollectionsCarousel nhận thêm một tham số, một lambda được gọi bất cứ

khi nào người dùng chạm vào cây.



Nhưng bạn có thể tự hỏi tại sao chúng tôi có CollectionsCarouselState như một lớp

thông thường và các sự kiện để chỉ ra các tương tác của người dùng thay vì có tất cả

những điều đó trong một ViewModel?


Theo quy tắc chung, bạn sẽ tạo một ViewModel để lưu trữ trạng thái, xử lý logic và xử lý các

sự kiện khi có thể tổng hợp gần với cấp gốc của màn hình, ví dụ: có thể tổng hợp

HomeScreen hoặc Màn hình đăng nhập. Trong trường hợp đó, chẳng hạn như đối với các

vật liệu tổng hợp có thể tái sử dụng như Carousel, đừng sử dụng ViewModel. Thay vào đó,

hãy tạo một lớp chủ sở hữu trạng thái thông thường để quản lý trạng thái và hiển thị các sự

kiện cho hàm gọi. Điều này là do ViewModels được xác định phạm vi đến Hoạt động,

Phân đoạn hoặc đích của biểu đồ điều hướng. Do đó, bạn chỉ có thể nhận được một phiên

bản của kiểu ViewModel trong phạm vi đó.


Các lợi ích đối với ViewModels ở cấp độ sàng lọc các vật thể tổng hợp là chúng có thể tồn

tại trước các thay đổi cấu hình, dữ liệu có thể tồn tại trong quá trình chết thông

qua SavedStateHandle, đó là một nơi tốt hơn cho trạng thái có vòng đời khớp với màn hình

và chúng có phạm vi quy trình điều tra tích hợp sẵn mà bạn có thể sử dụng để điều chỉnh

logic kinh doanh của bạn.



Quay lại mã, vì trạng thái của HomeScreen được HomeViewModel quản lý, nên

carouselState phải là một phần của trạng thái giao diện người dùng mà ViewModel hiển thị.

Sau đó, nó được chuyển cho CollectionsCarousel. Nhưng những gì về sự kiện điều

hướng? Chúng ta nên làm gì với điều đó? Và bản thân HomeScreen  thì sao? Chúng ta sẽ

có thể áp dụng cùng một khả năng kiểm tra và khả năng xem trước cho toàn màn hình,

phải không?


Đó là hoàn toàn đúng. Chúng tôi muốn có thể viết các bài kiểm tra trên HomeScreen của

mình và chuyển dữ liệu giả mạo sang bản xem trước mà không cần lo lắng về trạng thái

thu thập và mô hình View của chúng tôi.



Thật dễ dàng để thực hiện những thay đổi đó đối với HomeScreen của chúng tôi, chỉ cần

thêm trạng thái giao diện người dùng, luồng của các cây được phân trang và

lambda onPlantClick làm thông số.



Mặc dù điều này có vẻ không phải là một vấn đề lớn, nhưng chúng tôi vừa đẩy lệnh gọi

đến ViewModel() và collectAsState() lên một cấp độ, nó tạo ra sự khác biệt lớn khi nghĩ về vị

trí của màn hình chính này so với mọi thứ khác trong ứng dụng của bạn.



Nếu Bloom là một ứng dụng chỉ có một màn hình này, chúng tôi có thể sử dụng chức

năng setContent để đặt Composable của chúng tôi trực tiếp trong hoạt động. Nhưng các

bản mô phỏng của chúng tôi cho thấy một ứng dụng phức tạp hơn nhiều, vì vậy việc thêm

trực tiếp có thể tổng hợp của chúng tôi vào hoạt động có thể không phải là cách tiếp cận

phù hợp.



Một con đường di chuyển rất tự nhiên cho một ứng dụng hiện có khi bắt đầu áp dụng tính

năng Compose sẽ là di chuyển từng màn hình một. Ví dụ: nếu bạn đang sử dụng Fragment,

thì HomeScreen này thực sự có thể là nội dung duy nhất trong HomeFragment của bạn,

tạo ComposeView trực tiếp trong onCreateView (). Trong kiểu tiếp cận này, bạn sẽ sử dụng

các API như FragmentScenario để kiểm tra toàn bộ phân đoạn của mình.


Sau khi mọi màn hình trong ứng dụng của bạn chỉ là một lớp bao bọc xung quanh

một Composable có thể sử dụng lại, có thể thử nghiệm, bước tiếp theo sẽ là liên kết tất cả

các màn hình đó lại với nhau bằng tính năng Navigation Compose. Navigation

Compose Jetpack được xây dựng ngay từ đầu như một thời gian chạy chung không biết gì

về bất kỳ loại điểm đến nào. Sau đó, phần trừu tượng này được sử dụng để xây dựng cấu

phần Mảnh ghép điều hướng và gần đây hơn là phần tạo tác phần Navigation Compose.

Thời gian chạy được chia sẻ này có nghĩa là mỗi triển khai này đều được hỗ trợ sẵn sàng

để xây dựng biểu đồ điều hướng của màn hình hoặc điểm đến trong ứng dụng của bạn

thông qua Kotlin DSL, tự động xác định phạm vi vòng đời, ViewModels và Saved State

từng điểm đến, liên kết sâu, trả về kết quả và tích hợp với nút quay lại của hệ thống.



Trong Navigation Compose, có hai phần chính bạn cần: một NavController và

NavHost. NavController tuân theo cùng một mẫu mà chúng tôi đã sử dụng cho trạng

thái CollectionsCarousel, bằng cách cho phép bạn tạo NavController trạng thái như một

đối tượng riêng biệt từ NavHost lấy NavController làm đầu vào.


Điều này cho phép các thành phần khác bên ngoài NavHost phản ứng với việc thay đổi

đích hiện tại bằng cách sử dụng NavController làm nguồn chân lý duy nhất cho trạng thái.

Ví dụ: Điều này hữu ích khi đặt mục đã chọn trong thanh điều hướng dưới cùng.

NavHost Composable chịu trách nhiệm thêm điểm đến Composable của bạn vào hệ thống

phân cấp Composable. Đây là nơi bạn xác định biểu đồ điều hướng của mình thông qua

Kotlin DSL. Đây là bản đồ của tất cả các điểm đến có thể có trong ứng dụng của bạn.



Vì vậy, đối với HomeScreen của chúng tôi, chúng tôi có thể tạo một điểm đến có thể tổng

hợp tại nhà. Bạn sẽ lưu ý rằng chúng tôi khai báo một tuyến đường trên HomeScreen

của chúng tôi. Đây là con đường duy nhất mà bạn sử dụng để điều hướng đến điểm đến đó.

Vì đây là màn hình chính của ứng dụng, chúng tôi đặt nó làm điểm đến bắt đầu của biểu đồ.

Tuy nhiên, chúng tôi cần thực hiện một thay đổi đối với mã của mình. Thay vì sử

dụng ViewModel(), chúng tôi đang sử dụng phương thức hiltNavGraphViewModel() của

Hilt Navigation Compose. Giống như tôi đã đề cập, Điều hướng tự động tìm các

ViewModels của bạn đến đích riêng lẻ, nhưng vì đích đến không phải là Hoạt động hoặc

Phân đoạn @AndroidEntryPoint, nó không có nhà máy của Hilt làm nhà máy mặc định.


Phương thức hiltNavGraphViewModel() là một trình bao bọc thuận tiện xung quanh

viewModel() đảm bảo rằng nhà máy phù hợp luôn được đặt cho bạn. Lưu ý rằng chúng tôi

hoàn toàn không cần thay đổi HomeScreen của mình. Nó hoàn toàn không biết rằng nó

đang được sử dụng trong Navigation Compose và vẫn có thể được kiểm tra một cách độc

lập. Tất nhiên, lợi ích lớn của tính năng Navigation Compose đến khi bạn có nhiều màn hình.

Đó là khi khả năng phân chia trạng thái đã lưu và ViewModels tới đích riêng lẻ đảm bảo

trạng thái chỉ được tạo khi cần thiết và chỉ bị xóa khi bạn bật đích ra khỏi ngăn xếp phía sau.



Trong trường hợp của Bloom, những hình ảnh nhỏ trên PlantList hoặc băng chuyền mở rộng

chỉ nên là bản xem trước cho màn hình chi tiết thực sự cung cấp cho người dùng ý tưởng

về những gì họ có thể nhận được cho khu vườn của mình.


Bước đầu tiên là xây dựng PlantDetailsScreen. Cũng giống như HomeScreen của chúng

ta, chúng ta nên xây dựng PlantDetailsScreen của mình dưới dạng một thành phần không

trạng thái để chúng ta có thể sử dụng các bản xem trước và kiểm tra nó một cách riêng

biệt ngay từ đầu. Bạn sẽ lưu ý rằng CollectionsCarousel của chúng tôi xuất hiện một cách

khác. Việc xây dựng các thành phần có thể tái sử dụng không chỉ có nghĩa là mang lại

trải nghiệm nhất quán hơn cho người dùng trên ứng dụng của bạn mà còn giúp ích khi tạo

ra các màn hình mới.



Tiếp theo, chúng ta cần thêm PlantDetailsScreen vào biểu đồ điều hướng của mình. Đây chỉ

là một điểm đến có thể kết hợp khác, nhưng có một điểm khác biệt ở đây so với

HomeScreen của chúng tôi, chúng tôi cần biết người dùng đã chọn loại cây nào. Các

tuyến đường trong Navigation có rất nhiều điểm chung với các URL web ở chỗ chúng được

thiết kế để phục vụ như địa chỉ của nội dung chúng tôi hiển thị. Điều này có nghĩa là

tuyến đường của chúng tôi không chỉ là ‘plant/’, mà là ‘plant/’ với ID của thực vật mà chúng

tôi muốn hiển thị bằng cách sử dụng cú pháp giữ chỗ này. Đối với lớp Plant của chúng tôi, ID

là một số nguyên, vì vậy chúng tôi khai báo nó với IntType.


Bây giờ, chúng tôi có thể trích xuất ID trực tiếp từ mục nhập ngăn xếp phía sau mà

NavHost chuyển đến đích chi tiết thực vật của chúng tôi, nhưng chúng tôi thực sự muốn ID

đó trong ViewModel thực vật của chúng tôi, không phải ở đây.



Để thực hiện điều này, chúng tôi có thể sử dụng một API khác là một phần của

Jetpack ViewModel, SavedStateHandle. SavedStateHandle thực sự chỉ là một bản đồ giá

trị khóa lạ mắt, nhưng điều quan trọng là nó được tự động điền bởi các đối số mà chúng tôi

đặt trong lộ trình của mình.


Điều này cho phép ViewModel của chúng tôi chuyển trực tiếp ID đó đến kho lưu trữ của

chúng tôi, để truy xuất thông tin chi tiết về nhà máy và cung cấp Luồng có thể quan sát đó

cho giao diện người dùng. Điều này có nghĩa là nếu tải nền cập nhật giá của nhà máy hoặc

thay đổi nguồn gốc duy nhất của sự thật, PlantDetailScreen của chúng tôi sẽ tự động biên

soạn lại với dữ liệu mới.



Nhờ công việc của Manuel trong việc làm cho HomeScreen không ở trạng thái, chúng tôi

đã phát hiện ra một lambda được kích hoạt khi người dùng chạm vào một cái cây. Sau đó,

đích đến HomeScreen của chúng tôi thực hiện lambda đó, gọi navigate() với tuyến đường

của PlantDetailScreen của chúng tôi, điền vào ID. NavController sẽ xử lý phần còn lại của

nó, lưu trạng thái của HomeScreen và hoán đổi trong PlantDetailScreen. Nhấn vào nút quay

lại hệ thống sẽ tự động đưa bạn trở lại HomeScreen ở trạng thái chính xác mà bạn đã

để nguyên.


Chúng tôi chỉ đang sơ lược về những gì bạn có thể làm với Jetpack Compose và tất cả

các điểm tích hợp với các thư viện Jetpack khác.


Nếu bạn muốn tìm hiểu thêm, tôi thực sự khuyên bạn nên đọc qua hướng dẫn của chúng tôi

về khả năng tương tác đã soạn và hướng dẫn cụ thể về Điều hướng bằng Compose trong

tài liệu của chúng tôi. Để biết các ví dụ thực tế, hãy xem tất cả các mẫu Compose của chúng

tôi và ví dụ thực tế về việc chuyển đổi một ứng dụng hiện có thành Compose trong ứng

dụng mẫu, Tivi. Tất cả các liên kết đều có trong phần mô tả.

Cho dù bạn đang thực hiện một dự án hoàn toàn mới trong phần Compose hay điều chỉnh

một dự án hiện có, Jetpack luôn sẵn sàng trợ giúp. Cảm ơn.


Nhận xét

Bài đăng phổ biến từ blog này

Jetpack Compose VS SwiftUI !VS Flutter

  Việc phát triển Android đã trở nên dễ dàng hơn khi các bản cập nhật liên tục đến. Sau bản cập nhật 2020.3.1, rất nhiều thứ đã thay đổi. Nhưng thay đổi chính mà tôi nghĩ hầu hết các nhà phát triển phải chờ đợi là Jetpack Compose cho ứng dụng sản xuất. Và Kotlin là lựa chọn duy nhất cho jetpack Compose, cũng là ngôn ngữ được ưu tiên. Để biết thêm chi tiết hoặc các thay đổi trên Jetpack Compose, bạn có thể truy cập vào https://developer.android.com/jetpack/compose Tương tự, IOS Development cũng cung cấp một tùy chọn để phát triển khai báo, SwiftUI. Trong IDE, không có thay đổi nào do điều này. Nhưng khái niệm gần giống với Jetpack Compose. Thay vì bảng phân cảnh, chúng tôi tạo giao diện người dùng bằng Swift. Để biết thêm chi tiết hoặc các thay đổi trên SwiftUI, hãy truy cập https://developer.apple.com/xcode/swiftui/ Hãy xem cách cả hai hoạt động bằng cách sử dụng một dự án demo. Tôi đã lấy một số ví dụ về số lần chạm tương tự của Flutter. 1. Android Jetpack Compose Chúng tôi có thể tạo

Thiết kế giao diện với DotNetBar (Phần 1)

Đây là phiên bản DotNetBar hỗ trợ C# và Visual Basic https://www.dropbox.com/s/wx80jpvgnlrmtux/DotNetBar.rar  , phiên bản này hỗ trợ giao diện Metro cực kỳ “dễ thương” Các bạn load về và cài đặt, khi cài đặt xong sẽ có source code mẫu của tất cả các control. Để sử dụng được các control của DotNetBar các bạn nhớ add item vào controls box. Thiết kế giao diện với DotNetBar, giao diện sẽ rất đẹp. Link các video hướng dẫn chi tiết cách sử dụng và coding: http://www.devcomponents.com/dotnetbar/movies.aspx Hiện tại DotNetBar có rất nhiều công cụ cực mạnh, trong đó có 3 công cụ dưới đây: DotNetBar for Windows Forms Requires with Visual Studio 2003, 2005, 2008, 2010 or 2012.   DotNetBar for WPF Requires with Visual Studio 2010 or 2012 and Windows Presentation Foundation.   DotNetBar for Silverlight Requires with Visual Studio 2010 or 2012 and Silverlight. Dưới đây là một số hình ảnh về các control trong DotnetBar.   Metro User Interface  controls with Metro Tiles, toolbars, slide panels, forms,

Một số bài tập Winform C#

Một số bài tập: 1. Mô phỏng game đoán số. Luật chơi:         o Đúng số và đúng vị trí   +         o Đúng số mà sai vị trí      ?         o Sai số và sai vị trí          -         . . .         - Kết quả được tạo ngẫu nhiên từ các số có 4 chữ số.         - Các chữ số có giá trị từ 0-6.         - Người chơi có 6 lần đoán. Chương trình tham khảo: 2. In số điện tử Yêu cầu: người dùng nhập vào 1 số ( hoặc 1 chuỗi số) yêu cầu in ra số đó dưới dạng số điện tử. Chương trình tham khảo: 3. Mô phỏng game CARO  (update) 4. Mô phỏng game DÒ MÌN (update)