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

Quản lý số và dấu đỏ trong ứ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 xuyên sử dụng đều có cách thông báo tin nhắn mới một cách trực quantai ban ca, thường là dưới dạng số hoặc biểu tượng chấm đỏ. Ví dụ như trên ứng dụng WeChat, khi bạn bè gửi tin nhắn mới cho bạn, ở góc trên của cuộc trò chuyện sẽ xuất hiện một con số để báo hiệu số lượng tin chưa đọc. Hay khi có người đăng bài mới trên trang Moments (bạn bè), biểu tượng vào phần Moments sẽ hiện lên một dấu chấm đỏ để 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 nội dung bạn đã đăng tải, thay vì chỉ đơn giản là một dấu chấm đỏ, Moments sẽ hiển thị một con số cụ thể để bạn dễ dàng nhận biết số lượng tương tác mà bài viết của mình nhận được. Điều này không chỉ giúp người dùng nhanh chóng nắm bắt thông tin mà còn tạo ra một trải nghiệm thú vị và tiện lợi trong quá trình sử dụng ứng dụng. Những chi tiết nhỏ này đôi khi lại đóng vai trò quan trọng trong việc giữ chân người dùng và khuyến khích họ tương tác nhiều hơn với nền tảng.

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

Vậy những vấn đề này thực chất xuất phát từ đâu?

Tôi cho rằng nguyên nhân gốc rễ 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ị giữa các con số và chấm đỏ. Kết quả làtai ban ca, mối quan hệ giữa các con số và chấm đỏ trở nên phức tạp, một thay đổi nhỏ cũng có thể ảnh hưởng đến toàn bộ hệ thống. Điều này dẫn đến việc trong quá trình duy trì ứng dụng, chỉ cần thực hiện một thay đổi nhỏ (ví dụ như thêm một số lượng hoặc loại chấm đỏ mới), nguy cơ gặp lỗi sẽ rất cao. Thậm chí, mỗi khi có sự điều chỉnh nhỏ, các lập trình viên phải đối mặt với hàng loạt các vấn đề phát sinh từ sự thiếu nhất quán trong thiết kế ban đầu, khiến công việc trở nên mệt mỏi và tốn thời gian hơn nhiều so với dự kiến. Điều này không chỉ làm chậm tiến độ mà còn tăng nguy cơ tạo ra các lỗ hổng bảo mật hoặc trải nghiệm người dùng kém chất lượng.

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ý đồng nhất cấp bậc của các số và chấm đỏtai ban ca, đồng thời ở phần cuối bài, chúng tôi sẽ trình bày một ứng dụng demo chạy được trên nền tảng Android để bạn tham khảo. Ngoài ra, chúng tôi cũng sẽ thảo luận thêm về cách triển khai và tối ưu hóa hiệu suất của mô hình này trong thực tế.

Nếu bạn hiện đang có một chiếc điện thoại Android trong taytai ban ca, bạn có thể quét mã QR bên dưới (hoặc nhấp vào liên kết tải xuống dưới mã QR) để tải về và cài đặt phiên bản demo này. Chỉ cần dành ra vài phút để xem liệu nó có thực sự 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ý số và điểm đỏ đơn giản

Để thuận tiện cho việc thảo luậnsv 88, chúng ta hãy bắt đầu bằng cách sắp xếp nhanh các yêu cầu chung về việc hiển thị số và chấm đỏ, sau đó cùng tìm hiểu xem phương án thực hiện trực quan nhất có thể là gì dựa trên những yêu cầu này. Trước hết, điều quan trọng là phải xác định rõ vai trò của các con số và chấm đỏ trong giao diện. Chúng thường được sử dụng để thu hút sự chú ý hoặc nhấn mạnh một thông tin cụ thể nào đó. Chẳng hạn, một chấm đỏ có thể tượng trưng cho trạng thái "khẩn cấp" hoặc mức độ ưu tiên cao, trong khi các con số lại giúp người dùng dễ dàng theo dõi hoặc đánh giá tình hình một cách chính xác hơn. Tiếp theo, chúng ta cần suy nghĩ về cách bố trí và thiết kế sao cho người dùng có thể nhận ra ngay lập tức những yếu tố quan trọng này. Một gợi ý đơn giản nhưng hiệu quả là đặt chúng ở vị trí dễ nhìn thấy nhất trên màn hình, chẳng hạn như góc trên bên trái hoặc giữa trung tâm. Điều này sẽ đảm bảo rằng người dùng không bỏ lỡ bất kỳ thông tin quan trọng nào. Cuối cùng, để tăng tính tương tác, có thể bổ sung thêm hiệu ứng động nhẹ khi người dùng di chuột qua hoặc nhấn vào các biểu tượng này. Điều này không chỉ làm cho giao diện trở nên sống động mà còn giúp cải thiện trải nghiệm người dùng tổng thể.

  • Có những tin tức mới quan trọng cần được thể hiện dưới dạng số; cũng có những tin tức không quá quan trọng99win club, nên chỉ cần hiển thị bằng chấm đỏ. Ví dụ, khi tôi nhận được bình luận mới hoặc lượt thích mới, việc thể hiện dưới dạng số sẽ hợp lý hơn. Còn với những tin nhắn hệ thống mà họ gửi cho tôi, tôi không muốn nó làm phân tâm quá nhiều, vì vậy cách tốt nhất là dùng biểu tượng chấm đỏ nhẹ nhàng để báo hiệu một cách tinh tế.
  • Bình luận đã nhận

Badge Count Demo

  • Nếu một số ở cấp độ hiện tại gợi ý rằng các cấp độ sâu hơn chứa nhiều số gợi ý kháctai ban ca, thì số ở cấp độ hiện tại phải bằng tổng của tất cả các số ở cấp độ sâu hơn. Ví dụ như trong hình trên, số thông báo 5 có thể được phân chia thành 4 và 1 ở cấp độ bên dưới.
  • Nếu một cấp độ nào đó có thông báo (biểu tượng chấm đỏ) và ở các cấp sâu hơn có cả số lẫn biểu tượng chấm đỏ99win club, thì ở cấp độ này ưu tiên hiển thị theo số. Tuy nhiên, nếu ở cấp sâu hơn không còn số nào nữa mà chỉ còn biểu tượng chấm đỏ, thì cấp độ này sẽ hiển thị theo chấm đỏ. Ví dụ như trong hình ảnh chụp màn hình của ứng dụng bên dưới, nếu chỉ còn các thông báo hệ thống, và thông báo hệ thống được thiết lập để hiển thị chấm đỏ, thì tab thứ hai cũng sẽ hiển thị thành chấm đỏ. Điều này giúp người dùng dễ dàng nhận biết mức độ quan trọng của từng tab một cách rõ ràng và logic hơn.

Badge Dot Demo

Bạn có thể nhận thấy rằng những điểm tóm tắt trên đây tương đối giống với logic hiển thị của hầu hết các ứng dụng. Ngay cả khi có một số khác biệt99win club, điều đó cũng không ảnh hưởng nhiều đến phần thảo luận tiếp theo của chúng ta. Những khác biệt nhỏ ấy thường chỉ là sự điều chỉnh để phù hợp với mục đích riêng của từng ứng dụng mà thôi.

Bình luận nhận được

Khi chúng ta tập trung vào việc phân tích logic hiển thị chấm đỏ trên tab "Tin nhắn"tai ban ca, việc viết mã giả (pseudo-code) cho chức năng này sẽ không quá phức tạp. Dưới đây là một ví dụ: ``` Hàm capNhatChamDo(tongSoTinNhanMoi): Nếu tongSoTinNhanMoi > 0: Hiển thịChamDo() Nếu tongSoTinNhanMoi >= 99: HienThiText("99+") Else: HienThiText(tongSoTinNhanMoi) Else: AnChamDo() Hàm xuLyTinNhanMoi(): GiaiMaDuLieuTuServer() Cập nhật biến tongSoTinNhanMoi capNhatChamDo(tongSoTinNhanMoi) Khi moTabTinNhan: = "Tin Nhan": Neu tongSoTinNhanMoi > 0: PlayAmThanhCanhBao() XuLyTinNhanMoi() Else: KhongCanXuLyThem() ``` Trong đoạn mã giả trên, chúng ta thấy rằng logic chính xoay quanh việc kiểm tra số lượng tin nhắn mới và cập nhật giao diện tương ứng. Chấm đỏ sẽ xuất hiện khi có tin nhắn chưa đọc và biến đổi dựa trên số lượng cụ thể.

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

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

Điều này sẽ khiến việc bảo trì và chỉnh sửa trở nên phức tạp hơn bao giờ hết. Hãy tưởng tượng99win club, nếu bạn thêm một loại tin nhắn mới dưới mục "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ố thành biểu tượng chấm đỏ, thậm chí là di chuyển một loại tin nhắn từ ngăn chứa của một trang sang một trang khác. Tất cả những tình huống này đều yêu cầu mọi trang ở cấp cao hơn phải thực hiện thay đổi tương ứng. Khi một ứng dụng có số lượng loại tin nhắn ngày càng tăng, lên đến vài chục loại, có thể tưởng tượng rằng việc sửa đổi như vậy rất dễ dẫn đến sai sót. Thêm vào đó, khi số lượng tin nhắn tăng lên, việc quản lý logic liên quan cũng trở nên khó khăn hơn. Bạn không chỉ phải theo dõi cách hiển thị mà còn phải đảm bảo rằng các chức năng khác, chẳng hạn như thông báo đẩy hay cập nhật trạng thái, vẫn hoạt động ổn định. Điều này đặt ra áp lực lớn cho đội ngũ phát triển, đặc biệt là khi thời gian phát hành gần kề. Vì vậy, việc thiết kế hệ thống cần được tối ưu hóa ngay từ đầu để giảm thiểu sự phức tạp khi mở rộng tính năng. Một giải pháp hiệu quả có thể là sử dụng các mô hình thiết kế linh hoạt, chẳng hạn như Pattern Matching hoặc Observer Pattern, giúp ứng dụng có khả năng thích nghi tốt hơn với sự thay đổi mà không cần phải chỉnh sửa quá nhiều phần khác nhau. Điều này không chỉ giúp giảm thiểu rủi ro lỗi mà còn cải thiện chất lượng tổng thể của sản phẩm.

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

Những vấn đề trênsv 88, chúng tôi đã xử lý ở MicroLove Trong giai đoạn đầu phát triển ứng dụng99win club, chúng tôi cũng đã gặp phải một số vấn đề. Sau đó, chúng tôi quyết định xem xét lại cấu trúc của các biểu tượng đỏ và số liệu hiển thị trong ứng dụng, và bắt đầu tiếp cận nó dưới dạng cấu trúc cây. Cách làm này đã giúp công tác bảo trì trở nên dễ dàng hơn rất nhiều. Đồng thời, việc áp dụng phương pháp mới cũng giúp đội ngũ kỹ sư có cái nhìn toàn diện hơn về hệ thống, từ đó tối ưu hóa hiệu suất và cải thiện trải nghiệm người dùng một cách đáng kể.

Một trang của ứng dụng vốn dĩ đã được phân cấptai ban ca, vì vậy đường dẫn truy cập trang 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êntai ban ca, nút 1 đại diện cho trang cấp 1, trang này bao gồm ba cổng truy cập vào các trang cấp 2 sâu hơn, tương ứng với các nút 2, 3 và 4. Tiếp tục đi sâu hơn nữa 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ỗi bước chuyển tiếp không chỉ mở ra thêm nhiều lựa chọn mà còn giúp người dùng dễ dàng định hướng trong cấu trúc hệ thống ngày càng phức tạp này.

Mô hình cây này có thể được diễn đạt 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 thông điệp sẽ được hiển thị cho người dùng. Cách mà thông điệp được trình bày tại các nút lá đã được xác định từ giai đoạn thiết kế sản phẩm. Ví dụsv 88, nó có thể hiển thị ngay lập tức thông điệp hoặc chỉ hiện số lượng trước, sau đó khi người dùng nhấp vào sẽ hiển thị nội dung chi tiết của thông điệp (giống như cách các thông báo số lượng nhận xét trong hình chụp màn hình ứng dụng trước đây). Ngoài ra, nó cũng có thể sử dụng hộp thoại để đưa ra thông báo. Dù là phương án nào, cách thức trình bày này đã được cài đặt sẵn trong mã nguồn liên quan đến nghiệp vụ của sản phẩm.
  • Các nút trung gian (nút tròn màu cam) đại diện cho các trang nằm trên hành trình từ trang cấp 1 đến trang đích của tin nhắn. Thông thường99win club, những gì hiển thị trên các nút trung gian sẽ là số hoặc chấm đỏ để báo hiệu có thông báo mới hoặc số lượng thông báo chưa đọc.
  • Mỗi loại tin nhắn99win club, chúng ta gọi là mộ Nó có ba thuộc tính:
    • type: Loại Badge Number.
    • count: Đếm số lượng99win club, mỗi Badge Number, mỗi người dùng có một giá trị đếm.
    • Cách hiển thị badge number hiện tại trên nút cha: giá trị 0 đại diện cho chấm đỏtai ban ca, trong khi giá trị 1 thể hiện bằng số.
  • Số hiệu huy hiệu (Badge Number) được phân loại thành các hạng mục lớn (Category) khác nhau tùy thuộc vào loại hình dịch vụ mà nó thuộc về. Trong mỗi hạng mục lớnsv 88, các loại số hiệu huy hiệu sẽ được sắp xếp trong cùng một dải giá trị. Ví dụ như trong cấu trúc cây ở trên, 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 hạng mục lớn, và các dải giá trị tương ứng của chúng lần lượt là [A, C], [X, Y], [R, T]. Một ví dụ thực tế khác, ví dụ như vòng tròn bạn bè trên WeChat là một hạng mục lớn, trong đó các loại số hiệu huy hiệu bao gồm: có người bình luận bài viết của mình (số), có người thích bài viết của mình (số), bạn bè có bài viết mới (biểu tượng chấm đỏ), v.v. Dựa trên cấu trúc này, việc quản lý Badge Number trở nên rõ ràng hơn, giúp người dùng dễ dàng nhận biết và phân biệt các loại thông báo khác nhau từ các dịch vụ khác nhau. Điều này không chỉ làm tăng tính chuyên nghiệp mà còn nâng cao trải nghiệm sử dụng cho người cuối cùng.

Để 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ại99win club, khi gán giá trị cho loại, chúng ta có thể áp dụng phương pháp tương tự 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ó giá trị 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 được bình luận: (0x2 « 16) + 0x1
  • Nhận được lượt thích: (0x2 « 16) + 0x2
  • Tin nhắn hệ thống: (0x2 « 16) + 0x3

Bằng cách nàysv 88, nhóm "tin nhắn" có thể được biểu diễn 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 phân loại các loại tin nhắn một cách rõ ràng và hiệu quả trong hệ thống. Với cách thiết lập này, việc quản lý và phân tích các loại dữ liệu sẽ trở nên dễ dàng hơn bao giờ hết, đồng thời giúp tránh được xung đột giữa các loại thông tin khác nhau.

Sau khi có khoảng cách kiểu dữ liệusv 88, chúng ta hãy cùng xem lại các nút trung gian trong mô hình cây. Tất cả chúng đều có thể được biểu diễn bằng một hoặc nhiều khoảng cách kiểu dữ liệu. Cách chúng hiển thị logic (hiển thị dưới dạng số, biểu tượng chấm đỏ hay ẩn đi), sẽ phụ thuộc vào việc tính tổng tất cả các khoảng cách kiểu dữ liệu của cây con tương ứng. Quy trình tính tổng cụ thể như sau:

  • Trước tiênsv 88, tính tổng tất cả các loại số trong khoảng loại, 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 điểm đỏ trong khoảng loạisv 88, nếu lớn hơn 0 thì hiển thị điểm đỏ; ngược lại,
  • Ẩn số và điểm đỏ.

Thực hiện mã nguồn 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 Android99win club, mã nguồn có thể được tải xuống từ kho lưu trữ GitHub: https://github.com/tielei/BadgeNumberTree

Dưới đây chúng ta sẽ phân tích phần quan trọng.

Trong phiên bản Android99win club, lớp chính được sử dụng để triển khai là Dưới đây là đoạn mã quan trọng của nó (một số đoạn code không liên quan đến logic chính đã bị lược bỏ để dễ hiểu hơn. Nếu bạn muốn xem toàn bộ mã nguồn, vui lòng truy cập GitHub để tải về).

							
								
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ố badge * @param badgeNumber Số badge cần được thiết lập * @param asyncResult Kết quả trả về theo phương thức bất đồng bộ99win club, sẽ trả về một giá trị Boolean cho biết liệu 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 * @param badgeNumber Số badge cần được tính toán * @param asyncResult Kết quả trả về từ hoạt động bất đồng bộsv 88, sẽ trả về một giá trị Boolean cho biết liệu 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ừ hoạt động bất đồng bộ. Kết quả này sẽ là một giá trị Boolean99win club, 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ố hiệu huy hiệu theo loại được chỉ định * @param type Loại. Khi muốn lấy số hiệu huy hiệu từ cuộc trò chuyện99win club, 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 của số hiệu huy hiệu theo loại được chỉ định. */
    public
													 void
													 getBadgeNumber
													(
													final
													 int
													 type
													,
													 final
													 AsyncResult
													<
													Integer
													>
													 asyncResult
													)
													 {
													
        ...
													
    }
													

    /** * Dựa trên danh sách khoảng các loại badge number để tính toán tổng số badge number của nút cha trong cấu trúc cây. * Ưu tiên tính toán các con số trướctai ban ca, tiếp đến là biểu tượng chấm đỏ. * Một danh sách khoảng các loại badge number trong thực tế tương ứng với một nút cha trong cấu trúc cây. * @param typeIntervalList Danh sách khoảng các loại badge number được chỉ định, chắc chắn chứa ít nhất một khoảng. * @param asyncResult Kết quả trả về từ quy trình xử lý bất đồng bộ, sẽ cung cấp tình trạng badge number theo loại (bao gồm cách hiển thị và tổng số). */ Mỗi phần tử trong danh sách khoảng các loại badge number sẽ đại diện cho một phạm vi cụ thể mà chúng ta cần tính toán. Trong khi đó, kết quả bất đồng bộ sẽ giúp chúng ta xác định chi tiết từng loại badge number, từ đó có thể dễ dàng tổng hợp lại giá trị cuối cùng cho toàn bộ nút cha trong cấu trúc cây.
    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 099win club, 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 0sv 88, tiếp tục tính toán loại điểm đỏ
                    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
													())
													 {
													
                        // Count của tất cả các loại đã có
                        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ả tương ứng. */
    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
													;
													
        }
													
    }
													
    
}
													

Điểm cần chú ý trong đoạn mã này bao gồm:

  • Trước đó đã đề cập đến bốn thao tác cơ bản đối với Badge Number: setBadgeNumbertai ban ca, addBadgeNumber, clearBadgeNumber và Những thao tác này khá đơn giản và không cần liệt kê mã nguồn cụ thể ở đây. Trên thực tế, trong dự án Demo, chúng được triển khai dựa trên SQLite – một hệ thống quản lý cơ sở dữ liệu bản địa. Điều quan trọng là phải hiểu rõ ngữ cảnh sử dụng của từng thao tác: Ví dụ, khi bạn muốn **thiết lập badge number** (setBadgeNumber), điều này thường được áp dụng khi có một giá trị mới hoàn toàn cần được gán. Trong khi đó, **thêm badge number** (addBadgeNumber) sẽ phù hợp hơn nếu bạn muốn tăng thêm một giá trị hiện tại. Còn **xóa badge number** (clearBadgeNumber) thì rõ ràng là để xóa bỏ thông tin hoặc đưa về trạng thái ban đầu, và **lấy badge number** (getBadgeNumber) sẽ hữu ích khi bạn cần kiểm tra giá trị hiện tại mà không muốn thay đổi nó. Như vậy, mỗi chức năng đều mang ý nghĩa riêng và việc hiểu đúng ngữ cảnh sẽ giúp tối ưu hóa hiệu quả hoạt động của ứng dụng.
    • Hàm setBadgeNumber được sử dụng để thông báo chung về tin nhắn mới và sẽ được kích hoạt khi có thông báo tin nhắn mới xuất hiện. Nó sẽ lưu số lượng biểu tượng Badge (biểu tượng số trên ứng dụng) vào bộ nhớ thiết bị. Trong đó99win club, giá trị đếm (count) của các Badge Number được duy trì bởi máy chủ, do đó giá trị từ máy chủ sẽ là chuẩn. Mỗi khi nhận được dữ liệu từ máy chủ, hàm setBadgeNumber sẽ được gọi để ghi đè lên giá trị hiện tại trong bộ nhớ thiết bị. Điều này giúp đảm bảo rằng người dùng luôn nhận được thông tin cập nhật nhất về số lượng tin nhắn chưa đọc. Đồng thời, việc đồng bộ liên tục với máy chủ cũng tránh được sự khác biệt giữa thông tin hiển thị và thực tế. Một điểm đáng chú ý là khi ứng dụng vừa mở hoặc khởi động lại, hàm này cũng tự động chạy để đảm bảo rằng mọi thông tin biểu tượng số Badge đều chính xác và không bị lỗi thời.
    • Hàm addBadgeNumber được sử dụng để quản lý thông báo với số lượng tin nhắn tích lũy tại chỗtai ban ca, 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 trò chuyện mới, số lượng này sẽ được đếm dựa trên giá trị tại máy khách (local). Do đó, hàm addBadgeNumber đóng vai trò quan trọng trong việc tăng dần số lượng thông báo để người dùng dễ dàng theo dõi số tin nhắn chưa đọc của mình.
    • 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ườngsv 88, khi người dùng đã đọc hết tin nhắn mới trong trang tổng hợp tin nhắn (các nút lá cây), cần phải xóa số lượng thông báo đi để cập nhật trạng thái và tránh gây nhầm lẫn. Điều này không chỉ giúp giao diện trở nên gọn gàng mà còn tạo sự tiện lợi cho người dùng trong việc theo dõi các thay đổi mới nhất.
    • hàm getBadgeNumbertai ban ca, được sử dụng để lấy giá trị số Badge theo loại đã chỉ định. Hàm này thường được gọi khi hiển thị thông báo trên trang terminal (các nút lá của cấu trúc cây) để đảm bảo thông tin được trình bày một cách chính xác và rõ ràng cho người dùng.
  • Cuối cùngtai ban ca, 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 đó, phương thức này không tập trung vào việc lấy số Badge của một loại cụ thể nào đó mà thay vào đó nó sẽ tính tổng số Badge có kiểu nằm trong khoảng [typeMin, typeMax] và hiển thị theo một chế độ nhất định (displayMode). Đây chính là nền tảng để xử lý logic hiển thị số Badge ở các nút giữa. Tuy nhiên, mã nguồn thực tế của phương thức này không được trình bày ở đây, nhưng cách thực hiện khá đơn giản. Trong phiên bản demo, nó được triển khai bằng cách sử dụng SQLite để thực hiện phép tính tổng (sum) cần thiết.
  • Phương thức công khai `getTotalBadgeNumberOnParent` đóng vai trò quan trọng trong việc quản lý logic hiển thị số lượng huy hiệu tại các nú Tham số đầu vào của phương thức này là một danh sách khoảng cách kiểu dữ liệu99win club, đại diện cho một nút trung gian cụ thể. Kết quả trả về từ phương thức này là một đối tượng `BadgeNumberCountResult`, có khả năng biểu diễn ba trạng thái hiển thị khác nhau: số nguyên, biểu tượng đỏ (red dot), hoặc ẩn (không hiển thị gì cả). Để thực hiện chức năng này, phương thức sẽ gọi một phương thức riêng tư được overload khác, tiến hành tính tổng các giá trị số và kiểm tra biểu tượng đỏ từ danh sách khoảng cách kiểu dữ liệu liên quan đến tất cả các cây con của nút trung gian (đây chính là cách mà chúng ta đã thảo luận trước đó để tính tổng cho tất cả các khoảng cách kiểu dữ liệu của nút trung gian).

Ví dụ mã để gọi getTotalBadgeNumberOnParent:

							
								
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ị điểm đỏ
                showTabBadgeDot
													(
													tabIndex
													);
													
            }
													 else
													 {
													
                // Ẩn số và điểm đỏ
                hideTabBadgeNumber
													(
													tabIndex
													);
													
            }
													
        }
													
    });
													

Một số lưu ý bổ sung về việc thực hiện

  • Trong chương trình demotai ban ca, BadgeNumberTreeManager sử dụng SQLite làm phương thức lưu trữ cơ bản. Tuy nhiên, do BadgeNumberTreeManager thường xuyên được gọi giao diện, việc tối ưu hóa hiệu suất là điều cần thiết. Vì vậy, trong quá trình triển khai, đã thêm một lớp đệm bộ nhớ tạm thời ở cấp giữa để cải thiện tốc độ truy xuất dữ liệu (chi tiết xem thêm trên mã nguồn GitHub).
  • Sau khi nhận được số Badge mới theo một cách nào đó99win club, ứng dụng sẽ lưu nó vào bộ nhớ cục bộ bằng cách sử dụng các phương thức setBadgeNumber và addBadgeNumber của lớ Có nhiều cách khác nhau để ứng dụng có thể lấy được số Badge này. Một số trường hợp phổ biến là thông qua kết nối dài hạn (long connection) mà ứng dụng tự xây dựng hoặc từ nền tảng bên thứ ba, trong đó dữ liệu được đẩy trực tiếp đến ứng dụng. Ngoài ra, số Badge cũng có thể được tải về thông qua dịch vụ HTTP, mặc dù cách này thường phù hợp hơn cho những thông báo không yêu cầu độ chính xác thờ
  • Quá trình cập nhật và hiển thị số Badge tại các nút trung gian (cụ thể là việc gọi phương thức getTotalBadgeNumberOnParent của lớp BadgeNumberTreeManager) cần được thực hiện chính xác ở mọi thời điểm bắt buộc. Dựa trên ví dụ Demo phiên bản Android được đề xuất trong bài viết nàytai ban ca, các thời điểm quan trọng bao gồm: khi màn hình được hoạt động trở lại trong trạng thái onResume, khi người dùng chuyển đổi giữa các tab con, và ngay sau khi nhận được số Badge mới. Nếu cơ chế cập nhật này không diễn ra đúng lúc hoặc có sự thiếu sót, thì đây thường là một trong những nguyên nhân phổ biến dẫn đến lỗi trong việc hiển thị biểu tượng số đỏ (red dot) trong ứng dụng.
  • Việc xóa giá trị Badge Number tại các nút trung gian thường có hai trường hợp phổ biến: (1) Chỉ khi tất cả các nút con đều được xóa thì nút trung gian mới xóa; (2) Ngay lập tức xóa ngay cả khi chỉ cần nhấp vào nótai ban ca, bất kể các nút con đã xóa hay chưa. Trong ví dụ minh họa 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 cách thứ hai, bạn cần thêm một cờ đánh dấu riêng biệt cho từng nú Tuy nhiên, sự thay đổi này không quá phức tạp. Trong thực tế, việc quản lý các trạng thái như vậy đòi hỏi phải thiết lập cơ chế kiểm tra chặt chẽ giữa các nút cha và con để đảm bảo tính nhất quán. Điều này cũng giúp tránh những xung đột không đáng có khi người dùng thao tác trên cây cấu trúc dữ liệu. Với phương án thứ hai, việc duy trì trạng thái sẽ phức tạp hơn một chút nhưng lại tạo ra trải nghiệm trực quan và linh hoạt hơn cho người sử dụng.
  • Mặc dù các ví dụ mã trong bài viết này được xây dựng dựa trên Android Javasv 88, nhưng mô hình cây mà bài viết đề xuất hoàn toàn có thể áp dụng cho việc triển khai ứng dụng ở phiên bản không phả Điều này mở ra nhiều khả năng linh hoạt cho các nhà phát triển muốn áp dụng giải pháp này vào môi trường phần mềm đa nền tảng khác nhau.

Bài viết gốctai ban ca, vui lòng ghi rõ 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 "Trương Tiết Lệ" trên Weibo.
Tài khoản WeChat của tôi: tielei-blog (Trương Tiế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 nội bộ của Redis (4) —— ziplist