Khi tương tác với TV, người dùng thường muốn nhập ít thông tin đầu vào trước khi xem nội dung. Tình huống lý tưởng đối với nhiều người dùng TV là: ngồi xuống, bật TV và xem. Ít bước nhất để đưa người dùng đến với nội dung họ yêu thích thường là con đường mà họ thích.
Lưu ý: Hãy sử dụng các API được mô tả ở đây để đưa ra đề xuất chỉ trong ứng dụng chạy trong các phiên bản Android lên đến và bao gồm Android 7.1 (API cấp 25). Để cung cấp đề xuất cho ứng dụng chạy trong Android 8.0 (API cấp 26) trở lên, ứng dụng của bạn phải sử dụng kênh đề xuất.
Khung Android hỗ trợ hoạt động tương tác đầu vào tối thiểu bằng cách cung cấp một hàng đề xuất trên màn hình chính. Nội dung đề xuất xuất hiện dưới dạng hàng đầu tiên trên màn hình chính của TV lần đầu tiên sử dụng thiết bị. Việc đóng góp các đề xuất từ danh mục nội dung của ứng dụng có thể giúp ích cho bạn đưa người dùng quay lại ứng dụng của bạn.
![](https://developer.android.google.cn/static/images/tv/home-recommendations.png?authuser=19&hl=vi)
Hình 1. Ví dụ về hàng đề xuất.
Hướng dẫn này chỉ cho bạn cách tạo và cung cấp các đề xuất cho khung Android để người dùng có thể dễ dàng khám phá và thưởng thức nội dung ứng dụng của bạn. Xem thêm cách triển khai mẫu trong thời gian Ứng dụng mẫu Leanback của Google.
Các phương pháp hay nhất cho các đề xuất
Hệ thống đề xuất giúp người dùng nhanh chóng tìm thấy nội dung và ứng dụng mà họ yêu thích. Đang tạo những đề xuất có chất lượng cao và phù hợp với người dùng là một yếu tố quan trọng trong việc tạo trải nghiệm người dùng tuyệt vời hơn với ứng dụng dành cho TV. Vì lý do này, bạn nên xem xét cẩn thận những gì đề xuất mà bạn hiển thị cho người dùng và quản lý chúng một cách chặt chẽ.
Các loại đề xuất
Khi tạo nội dung đề xuất, bạn nên liên kết người dùng quay lại các hoạt động xem chưa hoàn chỉnh hoặc gợi ý các hoạt động mở rộng sang nội dung có liên quan. Sau đây là một số loại các đề xuất bạn nên cân nhắc:
- Những đề xuất nội dung tiếp nối cho tập tiếp theo để người dùng xem tiếp xem một chương trình dài tập. Ngoài ra, bạn có thể dùng các đề xuất về việc tiếp tục phát cho những bộ phim, chương trình truyền hình hoặc podcast đã tạm dừng để người dùng có thể quay lại xem nội dung bị tạm dừng chỉ bằng vài cú nhấp chuột.
- Đề xuất Nội dung mới, chẳng hạn như cho một tập mới phát hành lần đầu, nếu người dùng đã xem xong một chuỗi video khác. Ngoài ra, nếu ứng dụng của bạn cho phép người dùng đăng ký, theo dõi hoặc theo dõi thêm nội dung mới, đề xuất nội dung mới cho các mục chưa xem trong danh sách nội dung bạn theo dõi.
- Đề xuất Nội dung có liên quan dựa trên ý kiến của người dùng hành vi xem trước đây.
Để biết thêm thông tin về cách thiết kế thẻ đề xuất nhằm mang lại trải nghiệm tốt nhất cho người dùng, hãy xem Hàng Đề xuất trong Thông số thiết kế của Android TV.
Làm mới đề xuất
Khi làm mới nội dung đề xuất, đừng chỉ xoá rồi đăng lại vì làm như vậy sẽ gây ra lỗi các đề xuất sẽ xuất hiện ở cuối hàng đề xuất. Khi một mục nội dung, chẳng hạn như phim, đã được phát, xoá địa điểm đó khỏi danh sách đề xuất.
Tuỳ chỉnh nội dung đề xuất
Bạn có thể tuỳ chỉnh các thẻ đề xuất để truyền tải thông tin xây dựng thương hiệu bằng cách thiết lập giao diện người dùng các phần tử như nền trước và nền, màu sắc, biểu tượng ứng dụng, tiêu đề và phụ đề của thẻ. Để tìm hiểu thêm, hãy xem Hàng Đề xuất trong Thông số thiết kế của Android TV.
Đề xuất theo nhóm
Bạn có thể tuỳ ý nhóm các đề xuất dựa trên nguồn đề xuất. Ví dụ: ứng dụng của bạn có thể cung cấp hai nhóm đề xuất: đề xuất về nội dung mà người dùng đăng ký theo dõi, và gợi ý các nội dung thịnh hành mới mà người dùng có thể chưa biết đến.
Khi tạo hoặc cập nhật, hệ thống sẽ xếp hạng và sắp xếp thứ tự đề xuất cho từng nhóm riêng biệt hàng đề xuất. Bằng việc cung cấp thông tin nhóm cho các đề xuất, bạn có thể đảm bảo để đề xuất của bạn không bị xếp dưới những đề xuất không liên quan.
Sử dụng
NotificationCompat.Builder.setGroup()
để đặt chuỗi khoá nhóm của một đề xuất. Cho
ví dụ: để đánh dấu một nội dung đề xuất là thuộc về một nhóm chứa nội dung thịnh hành mới,
bạn có thể gọi setGroup("trending")
.
Tạo dịch vụ đề xuất
Hệ thống đề xuất nội dung được tạo bằng quy trình xử lý ở chế độ nền. Để đơn đăng ký của bạn đóng góp vào đề xuất, tạo ra một dịch vụ bổ sung danh sách định kỳ từ danh mục của ứng dụng vào danh sách các đề xuất của hệ thống.
Ví dụ về mã sau minh hoạ cách mở rộng IntentService
để
tạo dịch vụ đề xuất cho ứng dụng của bạn:
Kotlin
class UpdateRecommendationsService : IntentService("RecommendationService") { override protected fun onHandleIntent(intent: Intent) { Log.d(TAG, "Updating recommendation cards") val recommendations = VideoProvider.getMovieList() if (recommendations == null) return var count = 0 try { val builder = RecommendationBuilder() .setContext(applicationContext) .setSmallIcon(R.drawable.videos_by_google_icon) for (entry in recommendations.entrySet()) { for (movie in entry.getValue()) { Log.d(TAG, "Recommendation - " + movie.getTitle()) builder.setBackground(movie.getCardImageUrl()) .setId(count + 1) .setPriority(MAX_RECOMMENDATIONS - count) .setTitle(movie.getTitle()) .setDescription(getString(R.string.popular_header)) .setImage(movie.getCardImageUrl()) .setIntent(buildPendingIntent(movie)) .build() if (++count >= MAX_RECOMMENDATIONS) { break } } if (++count >= MAX_RECOMMENDATIONS) { break } } } catch (e: IOException) { Log.e(TAG, "Unable to update recommendation", e) } } private fun buildPendingIntent(movie: Movie): PendingIntent { val detailsIntent = Intent(this, DetailsActivity::class.java) detailsIntent.putExtra("Movie", movie) val stackBuilder = TaskStackBuilder.create(this) stackBuilder.addParentStack(DetailsActivity::class.java) stackBuilder.addNextIntent(detailsIntent) // Ensure a unique PendingIntents, otherwise all // recommendations end up with the same PendingIntent detailsIntent.setAction(movie.getId().toString()) val intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) return intent } companion object { private val TAG = "UpdateRecommendationsService" private val MAX_RECOMMENDATIONS = 3 } }
Java
public class UpdateRecommendationsService extends IntentService { private static final String TAG = "UpdateRecommendationsService"; private static final int MAX_RECOMMENDATIONS = 3; public UpdateRecommendationsService() { super("RecommendationService"); } @Override protected void onHandleIntent(Intent intent) { Log.d(TAG, "Updating recommendation cards"); HashMap<String, List<Movie>> recommendations = VideoProvider.getMovieList(); if (recommendations == null) return; int count = 0; try { RecommendationBuilder builder = new RecommendationBuilder() .setContext(getApplicationContext()) .setSmallIcon(R.drawable.videos_by_google_icon); for (Map.Entry<String, List<Movie>> entry : recommendations.entrySet()) { for (Movie movie : entry.getValue()) { Log.d(TAG, "Recommendation - " + movie.getTitle()); builder.setBackground(movie.getCardImageUrl()) .setId(count + 1) .setPriority(MAX_RECOMMENDATIONS - count) .setTitle(movie.getTitle()) .setDescription(getString(R.string.popular_header)) .setImage(movie.getCardImageUrl()) .setIntent(buildPendingIntent(movie)) .build(); if (++count >= MAX_RECOMMENDATIONS) { break; } } if (++count >= MAX_RECOMMENDATIONS) { break; } } } catch (IOException e) { Log.e(TAG, "Unable to update recommendation", e); } } private PendingIntent buildPendingIntent(Movie movie) { Intent detailsIntent = new Intent(this, DetailsActivity.class); detailsIntent.putExtra("Movie", movie); TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); stackBuilder.addParentStack(DetailsActivity.class); stackBuilder.addNextIntent(detailsIntent); // Ensure a unique PendingIntents, otherwise all // recommendations end up with the same PendingIntent detailsIntent.setAction(Long.toString(movie.getId())); PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); return intent; } }
Để dịch vụ này được hệ thống nhận dạng và chạy, hãy đăng ký dịch vụ bằng tệp kê khai ứng dụng. Đoạn mã sau đây minh hoạ cách khai báo lớp này là một dịch vụ:
<manifest ... > <application ... > ... <service android:name="com.example.android.tvleanback.UpdateRecommendationsService" android:enabled="true" /> </application> </manifest>
Đề xuất về bản dựng
Khi bắt đầu chạy, dịch vụ đề xuất của bạn phải tạo các đề xuất và chuyển các đề xuất đó đến
khung Android. Khung này sẽ nhận được các đề xuất dưới dạng đối tượng Notification
sử dụng một mẫu cụ thể và được đánh dấu bằng một
danh mục.
Đặt các giá trị
Để đặt giá trị thành phần trên giao diện người dùng cho thẻ đề xuất, bạn cần tạo một lớp trình tạo như sau mẫu trình tạo được mô tả như sau. Trước tiên, bạn đặt giá trị cho thẻ đề xuất phần tử.
Kotlin
class RecommendationBuilder { ... fun setTitle(title: String): RecommendationBuilder { this.title = title return this } fun setDescription(description: String): RecommendationBuilder { this.description = description return this } fun setImage(uri: String): RecommendationBuilder { imageUri = uri return this } fun setBackground(uri: String): RecommendationBuilder { backgroundUri = uri return this } ...
Java
public class RecommendationBuilder { ... public RecommendationBuilder setTitle(String title) { this.title = title; return this; } public RecommendationBuilder setDescription(String description) { this.description = description; return this; } public RecommendationBuilder setImage(String uri) { imageUri = uri; return this; } public RecommendationBuilder setBackground(String uri) { backgroundUri = uri; return this; } ...
Tạo thông báo
Sau khi đặt giá trị, bạn sẽ tạo thông báo, chỉ định các giá trị từ trình tạo
lớp vào thông báo và gọi NotificationCompat.Builder.build()
.
Ngoài ra, hãy nhớ gọi
setLocalOnly()
để thông báo NotificationCompat.BigPictureStyle
không hiện lên
trên các thiết bị khác.
Ví dụ về mã sau đây minh hoạ cách tạo một đề xuất.
Kotlin
class RecommendationBuilder { ... @Throws(IOException::class) fun build(): Notification { ... val notification = NotificationCompat.BigPictureStyle( NotificationCompat.Builder(context) .setContentTitle(title) .setContentText(description) .setPriority(priority) .setLocalOnly(true) .setOngoing(true) .setColor(context.resources.getColor(R.color.fastlane_background)) .setCategory(Notification.CATEGORY_RECOMMENDATION) .setLargeIcon(image) .setSmallIcon(smallIcon) .setContentIntent(intent) .setExtras(extras)) .build() return notification } }
Java
public class RecommendationBuilder { ... public Notification build() throws IOException { ... Notification notification = new NotificationCompat.BigPictureStyle( new NotificationCompat.Builder(context) .setContentTitle(title) .setContentText(description) .setPriority(priority) .setLocalOnly(true) .setOngoing(true) .setColor(context.getResources().getColor(R.color.fastlane_background)) .setCategory(Notification.CATEGORY_RECOMMENDATION) .setLargeIcon(image) .setSmallIcon(smallIcon) .setContentIntent(intent) .setExtras(extras)) .build(); return notification; } }
Chạy dịch vụ đề xuất
Dịch vụ đề xuất của ứng dụng phải chạy định kỳ để tạo những
nội dung đề xuất. Để chạy dịch vụ, hãy tạo một lớp để chạy bộ tính giờ và gọi
theo định kỳ. Ví dụ về mã sau đây mở rộng lớp BroadcastReceiver
để bắt đầu thực thi định kỳ một dịch vụ đề xuất
mỗi nửa giờ:
Kotlin
class BootupActivity : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Log.d(TAG, "BootupActivity initiated") if (intent.action.endsWith(Intent.ACTION_BOOT_COMPLETED)) { scheduleRecommendationUpdate(context) } } private fun scheduleRecommendationUpdate(context: Context) { Log.d(TAG, "Scheduling recommendations update") val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val recommendationIntent = Intent(context, UpdateRecommendationsService::class.java) val alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0) alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, INITIAL_DELAY, AlarmManager.INTERVAL_HALF_HOUR, alarmIntent ) } companion object { private val TAG = "BootupActivity" private val INITIAL_DELAY:Long = 5000 } }
Java
public class BootupActivity extends BroadcastReceiver { private static final String TAG = "BootupActivity"; private static final long INITIAL_DELAY = 5000; @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "BootupActivity initiated"); if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) { scheduleRecommendationUpdate(context); } } private void scheduleRecommendationUpdate(Context context) { Log.d(TAG, "Scheduling recommendations update"); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class); PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0); alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, INITIAL_DELAY, AlarmManager.INTERVAL_HALF_HOUR, alarmIntent); } }
Phương thức triển khai lớp BroadcastReceiver
này phải chạy sau khi bắt đầu
thiết bị TV được lắp đặt. Để thực hiện việc này, hãy đăng ký lớp học này trong ứng dụng của bạn
tệp kê khai có bộ lọc ý định giúp theo dõi việc hoàn tất quá trình khởi động thiết bị. Chiến lược phát hành đĩa đơn
mã mẫu sau đây minh hoạ cách thêm cấu hình này vào tệp kê khai:
<manifest ... > <application ... > <receiver android:name="com.example.android.tvleanback.BootupActivity" android:enabled="true" android:exported="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver> </application> </manifest>
Lưu ý quan trọng: Việc nhận thông báo khởi động xong yêu cầu ứng dụng của bạn
yêu cầu quyền RECEIVE_BOOT_COMPLETED
.
Để biết thêm thông tin, hãy xem ACTION_BOOT_COMPLETED
.
Trong hạng dịch vụ đề xuất dành cho bạn onHandleIntent()
hãy đăng đề xuất cho người quản lý như sau:
Kotlin
val notification = notificationBuilder.build() notificationManager.notify(id, notification)
Java
Notification notification = notificationBuilder.build(); notificationManager.notify(id, notification);