Trang chủ > Phát triển di động > Nội dung chính

Quản lý số lượng và thông báo đỏ trên ứng dụng bằng mô hình cây


Hầu hết các ứng dụng mà chúng ta thường sử dụng hàng ngày đều có cách thông báo khác nhau khi nhận được tin nhắn mới. Ví dụcá cược bóng đá, trong ứng dụng WeChat, khi một người bạn gửi tin nhắn trò chuyện mới cho bạn, biểu tượng cuộc trò chuyện tương ứng sẽ hiển thị số lượng tin nhắn chưa đọc. Tương tự, nếu có người đăng bài mới trên trang (Moment), biểu tượng vào mục sẽ xuất hiện dấu chấm màu đỏ để thu hút sự chú ý. Đặc biệt hơn, nếu có ai đó thích bài viết của bạn hoặc bình luận dưới bài đăng của bạn, biểu tượng này sẽ không chỉ xuất hiện một dấu chấm đỏ mà còn kèm theo số lượng cụ thể để cho bạn biết chính xác có bao nhiêu lượt thích hoặc bình luận. Ngoài ra, một số ứng dụng khác cũng áp dụng cách thức tương tự để giúp người dùng dễ dàng nhận diện những thông báo quan trọng. Điều này không chỉ tạo điều kiện thuận lợi cho việc quản lý thông tin mà còn giúp người dùng không bỏ lỡ bất kỳ cập nhật nào từ bạn bè hay đồng nghiệp. Chúng ta có thể thấy rằng, dù là một tính năng đơn giản nhưng nó đóng vai trò vô cùng quan trọng trong việc cải thiện trải nghiệm người dùng và tăng cường sự kết nối giữa mọi người.

Tuy nhiênmua thẻ trực tuyến, khi thử nghiệm các sản phẩm ứng dụng (App) mới, chúng ta thường nhận thấy nhiều vấn đề liên quan đến việc hiển thị số và chấm đỏ. Chẳng hạn, có những trường hợp chấm đỏ cứ hiện mãi dù đã nhấn đi nhấn lại nhiều lần mà không thể xóa được; hoặc khi nhìn thấy số xuất hiện bên ngoài, nhưng khi nhấn vào thì chẳng có gì cả; thậm chí, đôi khi số lượng được hiển thị bên trong lại hoàn toàn khác với số lượng được thể hiện ở ngoài. Những điều này không chỉ gây phiền phức mà còn làm giảm trải nghiệm người dùng một cách đáng kể.

Vậy những vấn đề này thực chất được tạo ra như thế nào?

Tôi cho rằngmua thẻ trực tuyến, nguyên nhân cốt lõi của vấn đề nằm ở chỗ chưa có một cách tiếp cận thống nhất để khái quát và quản lý logic hiển thị số và chấm đỏ. Điều này dẫn đến việc mối quan hệ giữa các số và chấm đỏ trở nên phức tạp, mỗi thay đổi dù nhỏ đều có thể gây ra tác động dây chuyền. Kết quả là, trong quá trình bảo trì ứng dụng, chỉ cần có một sự điều chỉnh nhỏ (như thêm một vài loại số hoặc chấm đỏ), khả năng xảy ra lỗi sẽ tăng lên đáng kể.

Bài viết này sẽ giới thiệu một mô hình cấu trúc cây để quản lý thống nhất thứ tự ưu tiên giữa số và chấm đỏmua thẻ trực tuyến, đồng thời ở phần cuối cùng, chúng tôi sẽ cung cấp một ứng dụng demo chạy được trên nền tảng Android để bạn tham khảo thêm.

Nếu bạn hiện đang có trong tay một chiếc điện thoại Androidbầu cua, bạn có thể quét mã QR bên dưới (hoặc nhấp vào đường dẫn tải xuống dưới mã QR) để tải về và cài đặt phiên bản thử nghiệm này. Chỉ cần dành ra vài phút để khám phá xem nó có phù hợp với nhu cầu của bạn hay không.

Demo APK Download

Hoặc nhấp vào Liên kết tải xuống

Cách quản lý đơn giản số và thông báo đỏ

Để thuận tiện cho việc thảo luậnmua thẻ trực tuyến, chúng ta hãy bắt đầu bằng cách sắp xếp một cách đơn giản các yêu cầu về việc hiển thị số và chấm đỏ trong trường hợp chung. Sau đó, chúng ta sẽ xem xét cách thức thực hiện trực quan nhất có thể dựa trên những yêu cầu này. Việc hiểu rõ các yêu cầu cơ bản là rất quan trọng, bởi vì nó giúp chúng ta xác định chính xác cách nào để tạo ra một giao diện vừa dễ sử dụng vừa hiệu quả. Hãy tưởng tượng rằng bạn đang thiết kế một hệ thống quản lý thông tin mà ở đó, số và dấu chấm đỏ đóng vai trò như những tín hiệu cảnh báo hoặc chỉ báo quan trọng. Việc sắp xếp các thành phần này một cách logic sẽ đảm bảo rằng người dùng có thể nắm bắt mọi thông tin cần thiết một cách nhanh chóng và chính xác. Vì vậy, trước tiên, hãy cùng liệt kê và phân tích các yếu tố cốt lõi liên quan đến việc hiển thị số và chấm đỏ. Điều này không chỉ giúp chúng ta hiểu rõ hơn về nhu cầu thực tế mà còn tạo nền tảng cho việc phát triển giải pháp tối ưu nhất.

  • Một số tin tức mới mang tính quan trọng cao nên được thể hiện dưới dạng con số; trong khi đócá cược bóng đá, những tin tức không quá quan trọng có thể được biểu thị bằng chấm đỏ. Ví dụ, khi tôi nhận được một bình luận mới hoặc lượt thích mới, việc thể hiện điều đó dưới dạng con số sẽ hợp lý hơn vì nó dễ hiểu và trực tiếp. Ngược lại, với những thông báo từ hệ thống mà tôi không muốn làm phân tâm quá nhiều, một dấu chấm đỏ nhẹ nhàng sẽ là lựa chọn phù hợp, giúp giữ cho giao diện gọn gàng mà vẫn cung cấp thông tin cần thiết.
  • Các con số và chấm đỏ đóng vai trò như những dấu hiệu để phân cấp thông tin hiển thị. Khi có tin nhắn mới đếncá cược bóng đá, người dùng có thể bắt đầu từ trang chủ chính của ứng dụng (gọi là màn hình cấp 1) và lần lượt đi sâu vào các màn hình cấp cao hơn dựa trên các gợi ý từ số lượng và chấm đỏ, cuối cùng đến trang đích để xem tin nhắn mới. Ví dụ trong ảnh chụp màn hình ứng dụng dưới đây, khi người dùng nhận được bình luận mới, trước tiên sẽ xuất hiện một biểu thị số ở tab thứ hai (tab “Tin nhắn”), dẫn dắt người dùng chuyển sang tab này. Sau đó, ở phần “Bình luận nhận được” trên màn hình này, sẽ tiếp tục xuất hiện dấu hiệu số, giúp người dùng nhấp chuột để tiến vào trang bình luận sâu hơn.

Badge Count Demo

  • Nếu một cấp độ nào đó có một số gợi ý số hiển thịcá cược bóng đá, và ở cấp độ sâu hơn chứa nhiều hơn một gợi ý số, thì số ở cấp độ hiện tại nên là tổng của các số từ cấp độ sâu hơn. Ví dụ như trong hình trên, số tin nhắn 5 được tạo thành từ tổng của 4 và 1 ở cấp độ sâu hơn.
  • Nếu một cấp độ nào đó có số (biểu tượng điểm đỏ) thông báomua thẻ trực tuyến, và ở cấp độ sâu hơn của trang web có cả số lẫn biểu tượng điểm đỏ, thì ở cấp độ hiện tại sẽ ưu tiên hiển thị theo số. Tuy nhiên, nếu ở cấp độ sâu hơn, tất cả các số đã được xóa sạch và chỉ còn lại biểu tượng điểm đỏ, thì lúc đó cấp độ hiện tại mới sẽ hiển thị theo biểu tượng điểm đỏ. Ví dụ như trong hình chụp màn hình ứng dụng dưới đây, trên trang chỉ còn lại thông báo tin nhắn hệ thống, và vì thông báo này là biểu tượng điểm đỏ, nên tab thứ hai cũng thay đổi thành biểu tượng điểm đỏ để phản ánh điều đó. Trong trường hợp khác, giả sử bạn đang lướt qua một danh sách và thấy rằng ở cấp độ trước đã không còn bất kỳ số nào nhưng vẫn tồn tại biểu tượng điểm đỏ, điều này cho phép người dùng biết rằng có một thông báo chưa được giải quyết ở cấp độ sâu hơn. Điều này giúp tăng cường khả năng nhận diện thông tin nhanh chóng và dễ dàng, đặc biệt trong các giao diện phức tạp với nhiều tab hoặc mục lựa chọn khác nhau.

Badge Dot Demo

Bạn có thể thấy rằng những điểm tóm tắt trên đây khá tương đồng với logic hiển thị của hầu hết các ứng dụng. Dù có một số khác biệt nhỏmua thẻ trực tuyến, điều đó chắc chắn sẽ không làm ảnh hưởng đến phần thảo luận tiếp theo của chúng ta. Thêm vào đó, dù mỗi ứng dụng có cách trình bày riêng, nhìn chung chúng đều hướng tới mục tiêu tạo ra trải nghiệm người dùng tốt nhất. Điều này cho phép các nhà phát triển linh hoạt trong việc thiết kế giao diện và chức năng mà vẫn đảm bảo sự nhất quán trong cách thức hoạt động.

các lượt thích nhận được

Khi chúng ta tập trung vào việc xử lý logic hiển thị chấm đỏ trên tab "Tin nhắn"bầu cua, việc viết một đoạn mã giả (pseudo-code) tương tự như dưới đây không phải là điều quá khó: ```python # Hàm kiểm tra trạng thái tab tin nhắn def kiem_tra_cham_do(tong_so_tin_nhan_moi): neu tong_so_tin_nhan_moi > 0: hien_thi_cham_do = True nguoc_lai: hien_thi_cham_do = False return hien_thi_cham_do # Ví dụ sử dụng hàm so_tin_nhan_moi = lay_so_tin_nhan_moi() # Hàm lấy số lượng tin nhắn chưa đọc neu kiem_tra_cham_do(so_tin_nhan_moi): hien_thi_cham_do_trong_giao_dien() ``` Đoạn mã này đơn giản chỉ là một ví dụ cơ bản về cách xác định khi nào nên hiển thị chấm đỏ để thông báo có tin nhắn mới trong tab "Tin nhắn". Tùy thuộc vào yêu cầu cụ thể của dự án, chúng ta có thể thêm nhiều điều kiện khác hoặc cải thiện cách xử lý.

							
								
1
2
3
4
5
6
7
8
9
10
													
														int
													 count
													 =
													 Số lượng bình luận +
													 Số lượt thích;
													
if
													 (
													count
													 >
													 0
													)
													 {
													
	Hiển thị số đếm
}
													
else
													 if
													 (
													Có tin nhắn hệ thống)
													 {
													
	Hiển thị thông báo đỏ
}
													
else
													 {
													
	Ẩn số và thông báo đỏ
}
													

Mã nguồn này tất nhiên có thể đáp ứng được yêu cầubầu cua, nhưng nhược điểm cũng khá rõ ràng. Điểm quan trọng nhất là nó đòi hỏi phải liệt kê toàn bộ loại tin nhắn con (như bình luận, thích, thông báo hệ thống) trong phần hiển thị của tab "Tin nhắn" và phải biết chính xác từng loại sẽ được biểu thị bằng số hay chấm đỏ. Những gì đã được trình bày ở trên chỉ áp dụng cho trường hợp hai cấp trang, còn nếu xuất hiện ba cấp hoặc nhiều cấp hơn thì sao? Lúc đó, toàn bộ thông tin này cần phải được lặp lại trên từng cấp trang, dẫn đến việc quản lý ngày càng phức tạp và dễ gây lỗi.

Điều này sẽ làm cho việc bảo trì và chỉnh sửa trở nên phức tạp hơn. Hãy tưởng tượngbầu cua, nếu bạn thêm một loại tin nhắn mới dưới phần "tin nhắn", hoặc một loại tin nhắn nào đó chuyển từ cách hiển thị bằng số sang chỉ xuất hiện biểu tượng chấm đỏ, thậm chí là một loại tin nhắn được di chuyển từ ngăn xếp trang này sang ngăn xếp trang khác. Tất cả những tình huống như vậy đều yêu cầu tất cả các trang ở mức cao hơn phải thực hiện thay đổi tương ứng. Khi một ứng dụng có ngày càng nhiều loại tin nhắn, lên đến vài chục loại, có thể hình dung rằng việc sửa đổi như vậy rất dễ xảy ra sai sót. Hơn nữa, mỗi lần cần điều chỉnh, đội ngũ phát triển không chỉ phải kiểm tra lại toàn bộ luồng dữ liệu mà còn phải đảm bảo tính nhất quán giữa các thành phần khác nhau trong hệ thống. Điều này không chỉ mất thời gian mà còn gia tăng rủi ro về mặt chất lượng sản phẩm. Vì vậy, việc xây dựng một hệ thống linh hoạt và dễ mở rộng ngay từ đầu là vô cùng quan trọng để giảm thiểu những vấn đề tiềm ẩn này.

Cách quản lý số và thông báo đỏ dựa trên mô hình cây

Những vấn đề trêncá cược bóng đá, chúng tôi ở MicroLove Trong giai đoạn đầu phát triển ứng dụngbầu cua, chúng tôi cũng đã gặp phải một số khó khăn. Sau đó, chúng tôi quyết định xem xét lại cấu trúc hiển thị các biểu tượng đỏ và số liệu trong ứng dụng, thay vì tiếp cận theo cách thông thường, chúng tôi đã thử sử dụng cấu trúc cây (tree structure) để tổ chức mọi thứ. Điều này không chỉ giúp cải thiện khả năng quản lý mà còn làm cho công tác bảo trì trở nên dễ dàng hơn rất nhiều. Bằng cách sắp xếp logic và có hệ thống, toàn bộ quá trình phát triển đã đạt được hiệu quả tốt hơn đáng kể.

Một trang ứng dụng chính là phân cấpcá cược bóng đá, đường dẫn truy cập của trang web về bản chất là một cấu trúc cây.

Biểu đồ cấu trúc cây Badge Number

Như đã thể hiện trong hình trênmua thẻ trực tuyến, nút 1 đại diện cho trang cấp 1, trang này bao gồm ba lối vào trang cấp 2 sâu hơn, tương ứng với các nút 2, 3 và 4. Khi đi sâu thêm một cấp nữa, chúng ta sẽ đến các trang cuối cùng, được biểu thị bằng các nút hình vuông màu xanh lá cây.

Mô hình cây này có thể được trình bày như sau:

  • Các nút lá (các nút hình vuông màu xanh lá) đại diện cho trang cuối cùng nơi tin nhắn sẽ được hiển thị. Cách thức mà tin nhắn xuất hiện trên các nút lá đã được xác định từ khi thiết kế sản phẩm. Chẳng hạncá cược bóng đá, nó có thể trực tiếp hiển thị nội dung tin nhắn, hoặc trước tiên chỉ hiện một con số, sau đó người dùng nhấn vào để xem chi tiết tin nhắn (giống như thông báo số lượng bình luận trong ảnh chụp màn hình ứng dụng trước đây), hoặc thậm chí bật hộp thoại để thông báo. Tóm lại, cách thức hiển thị này đã được cố định và lưu trữ trong mã nguồn của sản phẩm.
  • Các nút trung gian (những nút hình tròn màu cam) đại diện cho các trang web nằm trên hành trình từ trang cấp 1 đến trang đích của tin nhắn. Thường thì những gì được hiển thị trên các nút này sẽ là số hoặc chấm đỏbầu cua, giúp người dùng dễ dàng theo dõi tiến trình của mình.
  • Mỗi loại tin nhắnmua thẻ trực tuyến, chúng tôi gọi là mộ Nó có ba thuộc tính:
    • Loại: Loại Badge Number.
    • Đếm: Số lượngcá cược bóng đá, đối với mỗi Badge Number, mỗi người dùng có một giá trị đếm.
    • Phong_cách_hiển_thị: Cách mà số badge hiện tại được hiển thị trên nút cha. Giá_trị 0 đại_diện cho một chấm đỏmua thẻ trực tuyến, trong_khi giá_trị 1 thể_hiện bằng số.
  • Số badge được phân loại theo các danh mục lớn (Category) khác nhau tùy thuộc vào loại hình dịch vụ mà nó liên quan. Bên trong mỗi danh mục lớnbầu cua, các loại số badge sẽ được phân bổ trong cùng một phạm vi loại. Ví dụ như trong sơ đồ cây mà bạn đã thấy, các nút 2, 3 và 4 đại diện cho ba loại hình dịch vụ khác nhau, tức là ba danh mục lớn, và các phạm vi loại tương ứng của chúng lần lượt là [A, C], [X, Y], [R, T]. Để đưa ra ví dụ thực tế hơn, hãy lấy mạng xã hội WeChat Moments làm ví dụ. Đây là một danh mục lớn, và các loại số badge bên trong bao gồm: có người bình luận bài viết của tôi (số), có người thích bài viết của tôi (số), bạn bè của tôi có bài đăng mới (chấm đỏ), v.v. Các danh mục lớn không chỉ giúp người dùng dễ dàng nhận biết các loại thông báo mà còn tối ưu hóa cách thức quản lý thông tin. Chúng tạo ra sự rõ ràng và trực quan trong việc xử lý hàng loạt thông tin đa dạng, đồng thời đảm bảo rằng người dùng không bị quá tải bởi những cập nhật không cần thiết. Chính vì vậy, việc tổ chức số badge theo từng danh mục cụ thể đóng vai trò quan trọng trong việc cải thiện trải nghiệm người dùng trên nền tảng kỹ thuật số hiện đại.

Để biểu diễn Badge Number của một nhóm lớn bằng cách sử dụng một khoảng giá trị loạibầu cua, khi chúng ta gán giá trị cho các loại, có thể áp dụng cách làm như sau: Sử dụng một số nguyên (int) để biểu thị loại Badge Number, trong đó 16 bit cao sẽ được dùng để chỉ định nhóm lớn. Ví dụ, nếu nhóm lớn "Tin nhắn" có phần 16 bit cao là 0x2, thì ba loại Badge Number (type) thuộc nhóm này có thể được phân bổ như sau:

  • Nhận bình luận: (0x2 « 16) + 0x1
  • Nhận lượt thích: (0x2 « 16) + 0x2
  • Tin nhắn hệ thống: (0x2 « 16) + 0x3

Bằng cách nàycá cược bóng đá, nhóm "tin nhắn" có thể được biểu thị bằng một khoảng loại dữ liệu [(0x2 << 16) + 0x1, (0x2 << 16) + 0x3]. Khoảng giá trị này cho phép chúng ta dễ dàng xác định và phân loại các loại tin nhắn khác nhau trong hệ thống. Với việc sử dụng toán tử dịch bit (<<), chúng ta có thể tạo ra các giá trị duy nhất để đại diện cho từng loại tin nhắn mà không sợ bị trùng lặp hoặc nhầm lẫn. Điều này làm tăng tính chính xác và hiệu quả trong việc quản lý thông tin trong hệ thống.

Khi đã có các khoảng loạicá cược bóng đá, hãy cùng xem lại các nút trung gian trong mô hình cây. Những nút này đều có thể được biểu diễn bằng một hoặc nhiều khoảng loại. Cách chúng hiển thị (số, chấm đỏ hay ẩn đi) sẽ phụ thuộc vào việc tính tổng tất cả các khoảng loại của cây con liên quan. Quy trình cụ thể để tính tổng như sau: Trước hết, hãy xác định tất cả các cây con trực thuộc từng nú Mỗi cây con sẽ có một tập hợp khoảng loại riêng biệt. Tiếp theo, tiến hành so sánh và cộng dồn các khoảng loại này theo quy tắc: nếu hai khoảng loại trùng nhau hoặc có sự chồng lấn, chúng sẽ được kết hợp thành một khoảng lớn hơn; còn nếu không có sự chồng lấn, chúng vẫn giữ nguyên trạng thái riêng biệt. Sau khi đã hoàn thành việc tổng hợp tất cả các khoảng loại từ các cây con, hãy kiểm tra lại tổng giá trị của nó. Nếu tổng này nằm trong khoảng nào, thì nút trung gian đó sẽ tuân theo logic hiển thị của khoảng đó. Ví dụ, nếu tổng nằm trong khoảng cho phép hiển thị số, thì nút sẽ hiện ra dưới dạng số; nếu nằm trong khoảng yêu cầu hiển thị chấm đỏ, thì sẽ xuất hiện một dấu chấm đỏ; còn nếu nằm ngoài mọi khoảng cho phép hiển thị, nút sẽ bị ẩn đi. Hãy tưởng tượng mỗi khoảng loại như một dải màu trên mộ Khi các cây con kết nối với nhau, các dải màu này sẽ chồng lên nhau hoặc tách rời. Việc tính toán tổng là quá trình giúp xác định chính xác dải màu cuối cùng mà nút trung gian cần áp dụng. Điều này đảm bảo rằng mọi quyết định hiển thị đều dựa trên sự hợp lý và thống nhất.

  • Đầu tiênmua thẻ trực tuyến, tính tổng tất cả các loại trong khoảng số, nếu lớn hơn 0 thì hiển thị số; ngược lại,
  • Tính tổng tất cả các loại trong khoảng thông báo đỏcá cược bóng đá, nếu lớn hơn 0 thì hiển thị thông báo đỏ; ngược lại,
  • Ẩn số và thông báo đỏ.

Thực hiện mã hóa mô hình cây

Việc triển khai mô hình cây có thể được gọi là Bài viết này giới thiệu một phiên bản Demo dành cho Androidbầu cua, mã nguồn có thể được tải xuống từ GitHub: https://github.com/tielei/BadgeNumberTree

Bây giờ chúng ta hãy phân tích phần quan trọng.

Lớp chính thực hiện cho phiên bản Android là BadgeNumberTreeManagermua thẻ trực tuyến, và mã quan trọng của nó như sau (để không làm gián đoạn việc hiểu logic chính, các đoạn mã không quan trọng đã bị lược bỏ, chưa được hiển thị ở đây. Nếu bạn muốn xem chi tiết, vui lòng tải mã nguồn từ GitHub).

							
								
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
													/**
public
													 interface
													 AsyncResult
													<
													ResultType
													>
													 {
													
    void
													 returnResult
													(
													ResultType
													 result
													);
													
}
													

/**
public
													 class
													 BadgeNumberTreeManager
													 {
													
    /** * Cài đặt số lượng badge * @param badgeNumber Số lượng badge cần thiết lập * @param asyncResult Kết quả trả về theo phương thức bất đồng bộbầu cua, sẽ nhận được một tham số kiểu Boolean cho biết việc cài đặt có thành công hay không. */
    public
													 void
													 setBadgeNumber
													(
													final
													 BadgeNumber
													 badgeNumber
													,
													 final
													 AsyncResult
													<
													Boolean
													>
													 asyncResult
													)
													 {
													
        ...
													
    }
													

    /** * Tính tổng số badge number * @param badgeNumber Số badge cần được cộng dồn * @param asyncResult Kết quả trả về từ tác vụ bất đồng bộcá cược bóng đá, chứa một tham số kiểu Boolean để xác định xem thao tác cộng dồn có thành công hay không. */
    public
													 void
													 addBadgeNumber
													(
													final
													 BadgeNumber
													 badgeNumber
													,
													 final
													 AsyncResult
													<
													Boolean
													>
													 asyncResult
													)
													 {
													
        ...
													
    }
													

    /** * Xóa badge number theo loại được chỉ định * @param type Loại badge number mà bạn muốn xóa. * @param asyncResult Kết quả trả về từ tác vụ bất đồng bộ. Kết quả này sẽ là một giá trị Booleancá cược bóng đá, cho biết việc xóa có thành công hay không. */
    public
													 void
													 clearBadgeNumber
													(
													final
													 int
													 type
													,
													 final
													 AsyncResult
													<
													Boolean
													>
													 asyncResult
													)
													 {
													
        ...
													
    }
													

    /** * Lấy số badge theo loại được chỉ định * @param type Loại. Khi muốn lấy số badge từ cuộc trò chuyệnmua thẻ trực tuyến, bạn chỉ cần truyền giá trị 0. * @param asyncResult Kết quả trả về bất đồng bộ, sẽ trả về số lượng count của số badge theo loại đã chỉ định. */
    public
													 void
													 getBadgeNumber
													(
													final
													 int
													 type
													,
													 final
													 AsyncResult
													<
													Integer
													>
													 asyncResult
													)
													 {
													
        ...
													
    }
													

    kết quả trả về bất đồng bộ
    public
													 void
													 getTotalBadgeNumberOnParent
													(
													final
													 List
													<
													BadgeNumberTypeInterval
													>
													 typeIntervalList
													,
													 final
													 AsyncResult
													<
													BadgeNumberCountResult
													>
													 asyncResult
													)
													 {
													
        // Đầu tiên tính toán loại badge number để hiển thị số
        getTotalBadgeNumberOnParent
													(
													typeIntervalList
													,
													 BadgeNumber
													.
													DISPLAY_MODE_ON_PARENT_NUMBER
													,
													 new
													 AsyncResult
													<
													BadgeNumberCountResult
													>()
													 {
													
            @Override
													
            public
													 void
													 returnResult
													(
													BadgeNumberCountResult
													 result
													)
													 {
													
                if
													 (
													result
													.
													getTotalCount
													()
													 >
													 0
													)
													 {
													
                    // Tổng số loại số lớn hơn 0cá cược bóng đá, có thể trả về rồi.
                    if
													 (
													asyncResult
													 !=
													 null
													)
													 {
													
                        asyncResult
													.
													returnResult
													(
													result
													);
													
                    }
													
                }
													
                else
													 {
													
                    // Tổng số loại số không lớn hơn 0cá cược bóng đá, tiếp tục tính toán loại thông báo đỏ
                    getTotalBadgeNumberOnParent
													(
													typeIntervalList
													,
													 BadgeNumber
													.
													DISPLAY_MODE_ON_PARENT_DOT
													,
													 new
													 AsyncResult
													<
													BadgeNumberCountResult
													>()
													 {
													
                        @Override
													
                        public
													 void
													 returnResult
													(
													BadgeNumberCountResult
													 result
													)
													 {
													
                            if
													 (
													asyncResult
													 !=
													 null
													)
													 {
													
                                asyncResult
													.
													returnResult
													(
													result
													);
													
                            }
													
                        }
													
                    });
													
                }
													
            }
													
        });
													
    }
													


    private
													 void
													 getTotalBadgeNumberOnParent
													(
													final
													 List
													<
													BadgeNumberTypeInterval
													>
													 typeIntervalList
													,
													 final
													 int
													 displayMode
													,
													 final
													 AsyncResult
													<
													BadgeNumberCountResult
													>
													 asyncResult
													)
													 {
													
        final
													 List
													<
													Integer
													>
													 countsList
													 =
													 new
													 ArrayList
													<
													Integer
													>(
													typeIntervalList
													.
													size
													());
													
        for
													 (
													BadgeNumberTypeInterval
													 typeInterval
													 :
													 typeIntervalList
													)
													 {
													
            getBadgeNumber
													(
													typeInterval
													.
													getTypeMin
													(),
													 typeInterval
													.
													getTypeMax
													(),
													 displayMode
													,
													 new
													 AsyncResult
													<
													Integer
													>()
													 {
													
                @Override
													
                public
													 void
													 returnResult
													(
													Integer
													 result
													)
													 {
													
                    countsList
													.
													add
													(
													result
													);
													
                    if
													 (
													countsList
													.
													size
													()
													 ==
													 typeIntervalList
													.
													size
													())
													 {
													
                        // Đã có tất cả các giá trị đếm trong khoảng loại
                        int
													 totalCount
													 =
													 0
													;
													
                        for
													 (
													Integer
													 count
													 :
													 countsList
													)
													 {
													
                            if
													 (
													count
													 !=
													 null
													)
													 {
													
                                totalCount
													 +=
													 count
													;
													
                            }
													
                        }
													

                        // Trả về tổng
                        if
													 (
													asyncResult
													 !=
													 null
													)
													 {
													
                            BadgeNumberCountResult
													 badgeNumberCountResult
													 =
													 new
													 BadgeNumberCountResult
													();
													
                            badgeNumberCountResult
													.
													setDisplayMode
													(
													displayMode
													);
													
                            badgeNumberCountResult
													.
													setTotalCount
													(
													totalCount
													);
													
                            asyncResult
													.
													returnResult
													(
													badgeNumberCountResult
													);
													
                        }
													
                    }
													
                }
													
            });
													
        }
													
    }
													

    private
													 void
													 getBadgeNumber
													(
													final
													 int
													 typeMin
													,
													 final
													 int
													 typeMax
													,
													 final
													 int
													 displayMode
													,
													 final
													 AsyncResult
													<
													Integer
													>
													 asyncResult
													)
													 {
													
         ...
													
   }
													


    /**
    public
													 static
													 class
													 BadgeNumberTypeInterval
													 {
													
        private
													 int
													 typeMin
													;
													
        private
													 int
													 typeMax
													;
													

        public
													 int
													 getTypeMin
													()
													 {
													
            return
													 typeMin
													;
													
        }
													

        public
													 void
													 setTypeMin
													(
													int
													 typeMin
													)
													 {
													
            this
													.
													typeMin
													 =
													 typeMin
													;
													
        }
													

        public
													 int
													 getTypeMax
													()
													 {
													
            return
													 typeMax
													;
													
        }
													

        public
													 void
													 setTypeMax
													(
													int
													 typeMax
													)
													 {
													
            this
													.
													typeMax
													 =
													 typeMax
													;
													
        }
													
    }
													

    /** * Số hiệu huy hiệu được đếm dựa trên một khoảng loại hình cụ thể và cho ra kết quả như sau. */
    public
													 static
													 class
													 BadgeNumberCountResult
													 {
													
        private
													 int
													 displayMode
													;
													
        private
													 int
													 totalCount
													;
													

        public
													 int
													 getDisplayMode
													()
													 {
													
            return
													 displayMode
													;
													
        }
													

        public
													 void
													 setDisplayMode
													(
													int
													 displayMode
													)
													 {
													
            this
													.
													displayMode
													 =
													 displayMode
													;
													
        }
													

        public
													 int
													 getTotalCount
													()
													 {
													
            return
													 totalCount
													;
													
        }
													

        public
													 void
													 setTotalCount
													(
													int
													 totalCount
													)
													 {
													
            this
													.
													totalCount
													 =
													 totalCount
													;
													
        }
													
    }
													
    
}
													

Trong đoạn mã nàycá cược bóng đá, các điểm cần chú ý bao gồm:

  • Trước đâycá cược bóng đá, đã đề cập đến bốn thao tác cơ bản đối với Badge Number: setBadgeNumber, addBadgeNumber, clearBadgeNumber và Những thao tác này đều khá đơn giản và không cần liệt kê mã nguồn trong bài viết này. Thực tế, trong demo này, chúng được triển khai dựa trên SQLite để lưu trữ dữ liệu cục bộ. Điều quan trọng cần lưu ý là các thao tác này nên được áp dụng trong những ngữ cảnh phù hợp: - **setBadgeNumber**: Thích hợp khi bạn muốn thiết lập một giá trị cụ thể cho badge number, chẳng hạn như khi người dùng đăng nhập hoặc hoàn thành một nhiệm vụ quan trọng. - **addBadgeNumber**: Tính năng này sẽ hữu ích hơn khi bạn cần cộng thêm một số điểm hoặc số lượng badge dựa trên hành động của người dùng, ví dụ như khi họ hoàn thành một thử thách mới. - **clearBadgeNumber**: Khi người dùng yêu cầu xóa badge number hoặc khi có sự kiện đặc biệt nào đó xảy ra (như reset tài khoản), thao tác này sẽ giúp xóa sạch tất cả dữ liệu liên quan. - **getBadgeNumber**: Đây là tính năng cần thiết khi bạn muốn hiển thị hoặc kiểm tra số lượng badge hiện tại mà người dùng đang sở hữu. Việc hiểu rõ từng ứng dụng của chúng sẽ giúp cải thiện hiệu quả hoạt động của ứng dụng và mang lại trải nghiệm tốt hơn cho người dùng.
    • Hàm setBadgeNumber được sử dụng để thông báo chung về tin nhắn mớimua thẻ trực tuyến, được kích hoạt khi có thông báo tin nhắn mới xuất hiện và lưu số lượng badge (huy hiệu) xuống bộ nhớ cục bộ. Trong đó, giá trị count trong các huy hiệu này sẽ do máy chủ quản lý và cập nhật, vì vậy cần lấy dữ liệu từ máy chủ làm chuẩn. Mỗi khi nhận được dữ liệu từ máy chủ, hàm setBadgeNumber sẽ được gọi để ghi đè giá trị hiện tại trong bộ nhớ thiết bị. Ví dụ: Khi người dùng nhận được nhiều tin nhắn mới cùng lúc, hệ thống sẽ tự động cập nhật số lượng badge tương ứng dựa trên thông tin mà máy chủ cung cấp. Điều này giúp đảm bảo rằng người dùng luôn nhận được thông tin chính xác nhất về số lượng tin nhắn chưa đọc mà không gặp phải bất kỳ sai sót nào liên quan đến bộ đếm trong ứng dụng của họ.
    • Hàm addBadgeNumber được sử dụng để tăng dần số lượng thông báo trên biểu tượng badgecá cược bóng đá, chẳng hạn như các tin nhắn trò chuyện. Khi một người dùng nhận được tin nhắn mới trong cuộc trò chuyện, số lượng thông báo sẽ được tính toán dựa trên giá trị đếm tại chỗ. Do đó, việc sử dụng addBadgeNumber giúp tích lũy và cập nhật số lượng tin nhắn chưa đọc một cách hiệu quả.
    • Hàm clearBadgeNumber được sử dụng để xóa số lượng thông báo (Badge Number) của một loại cụ thể. Thông thườngcá cược bóng đá, khi người dùng đã đọc hết tin nhắn tại trang tổng hợp tin nhắn (các nút cuối cùng trong cấu trúc cây), cần phải xóa số lượng thông báo đi để cập nhật trạng thái mới nhất. Điều này không chỉ giúp cải thiện trải nghiệm người dùng mà còn đảm bảo giao diện luôn phản ánh chính xác tình trạng hiện tại.
    • hàm getBadgeNumber được sử dụng để lấy giá trị số hiệu badge theo loại được chỉ địnhbầu cua, thường được gọi khi hiển thị thông báo trên trang terminal (các nút lá trong cấu trúc cây). Hàm này đóng vai trò quan trọng trong việc quản lý và phân loại thông tin một cách trực quan cho người dùng.
  • Cuối cùngmua thẻ trực tuyến, có một phương thức được đánh dấu là private có tê Khác với các phương thức public đã được giới thiệu trước đó, thay vì chỉ lấy số Badge của một loại cụ thể nào đó, phương thức này sẽ tính tổng số Badge có displayMode (cách hiển thị) được chỉ định nằm trong khoảng kiểu dữ liệu từ typeMin đến typeMax. Đây là nền tảng quan trọng cho việc quản lý cách hiển thị số Badge tại các nút giữa. Tuy nhiên, mã thực tế của phương thức này không được trình bày ở đây. Nó cũng khá đơn giản và trong ví dụ demo, phần này được thực hiện bằng cách sử dụng SQLite để thực hiện phép tính tổng (sum).
  • Phương thức `getTotalBadgeNumberOnParent` được đánh dấu là `public` đóng vai trò quan trọng trong việc quản lý logic hiển thị số lượng huy hiệu (`Badge Number`) ở các nú Tham số đầu vào của phương thức này là một danh sách các khoảng cách kiểu dữ liệu (`typeIntervalList`)mua thẻ trực tuyến, đại diện cho các khoảng liên kết với một nút trung gian cụ thể. Kết quả trả về của phương thức này là một đối tượng `BadgeNumberCountResult`, có thể biểu diễn ba trạng thái hiển thị khác nhau: số, chấm đỏ (`red dot`), hoặc ẩn (không hiển thị). Thực thi của phương thức này dựa trên việc gọi đến một phương thức riêng (`private`) khác, có cùng tên nhưng với tham số khác nhau. Trong phương thức này, các giá trị trong danh sách khoảng cách kiểu dữ liệu sẽ được xử lý theo từng loại cụ thể: trước tiên là tổng hợp các giá trị số (`numeric types`), sau đó tính tổng các loại biểu tượng chấm đỏ (`red dot types`). Đây chính là cách mà hệ thống thực hiện việc tính toán tổng hợp toàn bộ khoảng cách kiểu dữ liệu từ tất cả các nhánh con của một nú Thông qua cách tiếp cận này, phương thức đảm bảo rằng mọi thông tin về số lượng huy hiệu và trạng thái hiển thị đều được xử lý một cách chính xác và hiệu quả.

Ví dụ mã gọi getTotalBadgeNumberOnParent như sau:

							
								
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    BadgeNumberTypeInterval
													 typeInterval
													 =
													 new
													 BadgeNumberTypeInterval
													();
													
    typeInterval
													.
													setTypeMin
													(
													BadgeNumber
													.
													CATEGORY_NEWS_MIN
													);
													
    typeInterval
													.
													setTypeMax
													(
													BadgeNumber
													.
													CATEGORY_NEWS_MAX
													);
													

    List
													<
													BadgeNumberTypeInterval
													>
													 typeIntervalList
													 =
													 new
													 ArrayList
													<
													BadgeNumberTypeInterval
													>(
													1
													);
													
    typeIntervalList
													.
													add
													(
													typeInterval
													);
													

    BadgeNumberTreeManager
													.
													getInstance
													().
													getTotalBadgeNumberOnParent
													(
													typeIntervalList
													,
													 new
													 AsyncResult
													<
													BadgeNumberCountResult
													>()
													 {
													
        @Override
													
        public
													 void
													 returnResult
													(
													BadgeNumberCountResult
													 result
													)
													 {
													
            if
													 (
													result
													.
													getDisplayMode
													()
													 ==
													 BadgeNumber
													.
													DISPLAY_MODE_ON_PARENT_NUMBER
													 &&
													 result
													.
													getTotalCount
													()
													 >
													 0
													)
													 {
													
                // Hiển thị số
                showTabBadgeCount
													(
													tabIndex
													,
													 result
													.
													getTotalCount
													());
													
            }
													 else
													 if
													 (
													result
													.
													getDisplayMode
													()
													 ==
													 BadgeNumber
													.
													DISPLAY_MODE_ON_PARENT_DOT
													 &&
													 result
													.
													getTotalCount
													()
													 >
													 0
													)
													 {
													
                // Hiển thị thông báo đỏ
                showTabBadgeDot
													(
													tabIndex
													);
													
            }
													 else
													 {
													
                // Ẩn số và thông báo đỏ
                hideTabBadgeNumber
													(
													tabIndex
													);
													
            }
													
        }
													
    });
													

Một số giải thích bổ sung về việc thực hiện

  • Trong chương trình demomua thẻ trực tuyến, BadgeNumberTreeManager sử dụng SQLite làm cơ sở lưu trữ phía dưới. Tuy nhiên, vì BadgeNumberTreeManager được gọi giao diện rất thường xuyên, nên trong quá trình triển khai đã thêm vào một lớp cache bộ nhớ tạm thời ở giữa (chi tiết có thể tham khảo trên mã nguồn GitHub). Điều này giúp tăng tốc đáng kể hiệu suất và giảm tải cho cơ sở dữ liệu SQLite khi có nhiều yêu cầu truy cập liên tục. Với cách thiết kế này, hệ thống không chỉ tối ưu hóa được việc xử lý dữ liệu mà còn đảm bảo tính ổn định và hiệu quả trong hoạt động của ứng dụng.
  • Sau khi nhận được số Badge mới theo một cách nào đóbầu cua, ứng dụng sẽ lưu trữ nó vào bộ nhớ cục bộ thông qua các phương thức setBadgeNumber và addBadgeNumber củ Có nhiều cách để ứng dụng có thể nhận được số Badge này. Một trong những cách phổ biến là thông qua kết nối dài (long connection) mà ứng dụng tự phát triển hoặc sử dụng nền tảng kết nối dài từ bên thứ ba. Ngoài ra, số Badge cũng có thể được tải về thông qua dịch vụ HTTP. Phương thức này thường được áp dụng trong trường hợp yêu cầu tính thời gian thực không quá cao, chẳng hạn như các thông báo nhắc nhở không cần cập nhật ngay lập tức.
  • Logic hiện thị và làm mới số lượng huy hiệu (Badge Number) tại các nút trung gian (middleware node)mua thẻ trực tuyến, cụ thể là việc gọi phương thức getTotalBadgeNumberOnParent của lớp BadgeNumberTreeManager, cần được thực thi ở mọi thời điểm bắt buộc. Dựa trên ví dụ ứng dụng demo phiên bản Android được đề cập trong bài viết này, các thời điểm quan trọng để thực hiện logic này bao gồm: khi màn hình được kích hoạt trở lại (onResume), khi chuyển đổi giữa các tab con (sub-tab), hoặc khi nhận được số lượng huy hiệu mới. Nếu logic hiện thị không được thực hiện chính xác hoặc có sự bỏ sót ở những thời điểm cần thiết, đây cũng là một trong những nguyên nhân phổ biến gây ra vấn đề liên quan đến việc hiển thị biểu tượng số đỏ (red-dot) trên ứng dụng. Để tránh những sai sót này, việc thiết kế logic phải đảm bảo rằng mỗi sự kiện có tác động đến trạng thái Badge Number đều được xử lý kịp thời và đầy đủ. Điều này không chỉ giúp cải thiện trải nghiệm người dùng mà còn duy trì tính ổn định cho toàn bộ hệ thống.
  • Việc xóa giá trị của nút trung gian có badge number thường có hai trường hợp phổ biến: (1) Chỉ khi tất cả các nút con đều bị xóa thì nút trung gian mới bị xóa; (2) Ngược lạibầu cua, chỉ cần nhấn vào là xóa mà không quan tâm đến việc các nút con đã được xóa hay chưa. Trong ví dụ demo của bài viết này, chúng tôi thực hiện theo cách đầu tiên. Nếu muốn áp dụng trường hợp thứ hai, bạn sẽ cần lưu thêm một cờ đánh dấu riêng biệt cho từng nút trung gian, nhưng điều chỉnh này không quá phức tạp. Trong trường hợp áp dụng phương án thứ hai, bạn nên sử dụng một mảng hoặc cấu trúc dữ liệu khác để lưu trữ trạng thái của từng nú Điều này giúp đảm bảo rằng chương trình vẫn hoạt động ổn định và dễ kiểm soát hơn trong mọi tình huống. Hãy nhớ rằng bất kỳ thay đổi nào cũng cần được kiểm thử kỹ lưỡng trước khi triển khai vào hệ thống chính thức để tránh những lỗi không mong muốn.
  • Mặc dù các ví dụ mã nguồn trong bài viết này được xây dựng dựa trên Android Javacá cược bóng đá, nhưng mô hình cây mà bài viết cung cấp hoàn toàn có thể áp dụng cho việc triển khai ứng dụng ở các phiên bản không phả Điều này mở ra khả năng linh hoạt để phát triển và tích hợp mô hình này vào nhiều nền tảng khác nhau, giúp người dùng dễ dàng tùy chỉnh theo nhu cầu riêng của từng môi trường phát triển.

Bài viết gốccá cược bóng đá, xin vui lòng trích dẫn nguồn và bao gồm mã QR bên dưới! Nếu không, từ chối tái bản!
Liên kết bài viết: /v8vru2kt.html
Hãy theo dõi tài khoản Weibo cá nhân của tôi: Tìm kiếm tên tôi "Trương Thiết Lệ" trên Weibo.
Tài khoản WeChat của tôi: tielei-blog (Trương Thiết Lệ)
Bài trước: Con gái và những cuốn truyện tranh của nó
Bài sau: Phân tích sâu cấu trúc dữ liệu bên trong Redis (4) —— ziplist