Android TV sử dụng giao diện tìm kiếm của Android để truy xuất dữ liệu nội dung từ các ứng dụng đã cài đặt và cung cấp kết quả tìm kiếm cho người dùng. dữ liệu nội dung nào có thể được đưa vào các kết quả này để giúp người dùng truy cập ngay vào nội dung trong ứng dụng của bạn.
Ứng dụng của bạn phải cung cấp cho Android TV các trường dữ liệu mà từ đó Android TV có thể tạo ra cụm từ tìm kiếm đề xuất
kết quả tìm kiếm khi người dùng nhập các ký tự vào hộp thoại tìm kiếm. Để làm được điều đó, ứng dụng của bạn phải triển khai một
Trình cung cấp nội dung phân phát
đưa ra các đề xuất cùng với
Tệp cấu hình searchable.xml
mô tả nội dung
và các thông tin quan trọng khác cho Android TV. Bạn cũng cần có một hoạt động xử lý
ý định kích hoạt khi người dùng chọn một kết quả tìm kiếm đề xuất. Cho
để biết thêm chi tiết, hãy xem phần Thêm
cụm từ tìm kiếm tuỳ chỉnh được đề xuất. Hướng dẫn này trình bày các điểm chính dành riêng cho ứng dụng Android TV.
Trước khi đọc hướng dẫn này, hãy đảm bảo rằng bạn làm quen với các khái niệm được giải thích trong Hướng dẫn về API Tìm kiếm. Ngoài ra, hãy xem bài viết Thêm chức năng tìm kiếm.
Mã mẫu trong hướng dẫn này lấy từ Ứng dụng mẫu Leanback của Google.
Xác định các cột
SearchManager
mô tả các trường dữ liệu cần có bằng cách biểu thị các trường đó dưới dạng
cột của cơ sở dữ liệu cục bộ. Bất kể định dạng dữ liệu của bạn là gì, bạn đều phải liên kết các trường dữ liệu đến
các cột này, thường là trong lớp truy cập dữ liệu nội dung của bạn. Để biết thông tin về việc xây dựng
một lớp liên kết dữ liệu hiện có với các trường bắt buộc, hãy xem
Xây dựng bảng đề xuất.
Lớp SearchManager
bao gồm một số cột cho Android TV. Một số
các cột quan trọng hơn được mô tả trong bảng sau.
Giá trị | Mô tả |
---|---|
SUGGEST_COLUMN_TEXT_1 |
Tên nội dung của bạn (bắt buộc) |
SUGGEST_COLUMN_TEXT_2 |
Phần mô tả dạng văn bản về nội dung của bạn |
SUGGEST_COLUMN_RESULT_CARD_IMAGE |
Hình ảnh, áp phích hoặc bìa cho nội dung của bạn |
SUGGEST_COLUMN_CONTENT_TYPE |
Loại MIME của nội dung nghe nhìn của bạn |
SUGGEST_COLUMN_VIDEO_WIDTH |
Chiều rộng độ phân giải của nội dung nghe nhìn |
SUGGEST_COLUMN_VIDEO_HEIGHT |
Chiều cao độ phân giải của nội dung nghe nhìn |
SUGGEST_COLUMN_PRODUCTION_YEAR |
Năm sản xuất nội dung (bắt buộc) |
SUGGEST_COLUMN_DURATION |
Thời lượng tính bằng mili giây của nội dung nghe nhìn (bắt buộc) |
Khung tìm kiếm yêu cầu các cột sau:
Khi giá trị của những cột này cho nội dung của bạn khớp với giá trị cho cùng nội dung từ các nhà cung cấp do các máy chủ của Google tìm thấy, thì hệ thống sẽ cung cấp đường liên kết sâu đến ứng dụng của bạn một cách chi tiết chế độ xem nội dung, cùng với đường liên kết đến ứng dụng của các nhà cung cấp khác. Vấn đề này sẽ được thảo luận thêm trong phần Đường liên kết sâu đến ứng dụng của bạn trong màn hình chi tiết.
Lớp cơ sở dữ liệu của ứng dụng có thể xác định các cột như sau:
Kotlin
class VideoDatabase { companion object { // The columns we'll include in the video database table val KEY_NAME = SearchManager.SUGGEST_COLUMN_TEXT_1 val KEY_DESCRIPTION = SearchManager.SUGGEST_COLUMN_TEXT_2 val KEY_ICON = SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE val KEY_DATA_TYPE = SearchManager.SUGGEST_COLUMN_CONTENT_TYPE val KEY_IS_LIVE = SearchManager.SUGGEST_COLUMN_IS_LIVE val KEY_VIDEO_WIDTH = SearchManager.SUGGEST_COLUMN_VIDEO_WIDTH val KEY_VIDEO_HEIGHT = SearchManager.SUGGEST_COLUMN_VIDEO_HEIGHT val KEY_AUDIO_CHANNEL_CONFIG = SearchManager.SUGGEST_COLUMN_AUDIO_CHANNEL_CONFIG val KEY_PURCHASE_PRICE = SearchManager.SUGGEST_COLUMN_PURCHASE_PRICE val KEY_RENTAL_PRICE = SearchManager.SUGGEST_COLUMN_RENTAL_PRICE val KEY_RATING_STYLE = SearchManager.SUGGEST_COLUMN_RATING_STYLE val KEY_RATING_SCORE = SearchManager.SUGGEST_COLUMN_RATING_SCORE val KEY_PRODUCTION_YEAR = SearchManager.SUGGEST_COLUMN_PRODUCTION_YEAR val KEY_COLUMN_DURATION = SearchManager.SUGGEST_COLUMN_DURATION val KEY_ACTION = SearchManager.SUGGEST_COLUMN_INTENT_ACTION ... } ... }
Java
public class VideoDatabase { // The columns we'll include in the video database table public static final String KEY_NAME = SearchManager.SUGGEST_COLUMN_TEXT_1; public static final String KEY_DESCRIPTION = SearchManager.SUGGEST_COLUMN_TEXT_2; public static final String KEY_ICON = SearchManager.SUGGEST_COLUMN_RESULT_CARD_IMAGE; public static final String KEY_DATA_TYPE = SearchManager.SUGGEST_COLUMN_CONTENT_TYPE; public static final String KEY_IS_LIVE = SearchManager.SUGGEST_COLUMN_IS_LIVE; public static final String KEY_VIDEO_WIDTH = SearchManager.SUGGEST_COLUMN_VIDEO_WIDTH; public static final String KEY_VIDEO_HEIGHT = SearchManager.SUGGEST_COLUMN_VIDEO_HEIGHT; public static final String KEY_AUDIO_CHANNEL_CONFIG = SearchManager.SUGGEST_COLUMN_AUDIO_CHANNEL_CONFIG; public static final String KEY_PURCHASE_PRICE = SearchManager.SUGGEST_COLUMN_PURCHASE_PRICE; public static final String KEY_RENTAL_PRICE = SearchManager.SUGGEST_COLUMN_RENTAL_PRICE; public static final String KEY_RATING_STYLE = SearchManager.SUGGEST_COLUMN_RATING_STYLE; public static final String KEY_RATING_SCORE = SearchManager.SUGGEST_COLUMN_RATING_SCORE; public static final String KEY_PRODUCTION_YEAR = SearchManager.SUGGEST_COLUMN_PRODUCTION_YEAR; public static final String KEY_COLUMN_DURATION = SearchManager.SUGGEST_COLUMN_DURATION; public static final String KEY_ACTION = SearchManager.SUGGEST_COLUMN_INTENT_ACTION; ...
Khi tạo bản đồ từ cột SearchManager
đến các trường dữ liệu, bạn
bạn cũng phải chỉ định _ID
để cấp cho mỗi hàng một mã nhận dạng duy nhất.
Kotlin
companion object { .... private fun buildColumnMap(): MapS<tring, String> { return mapOf( KEY_NAME to KEY_NAME, KEY_DESCRIPTION to KEY_DESCRIPTION, KEY_ICON to KEY_ICON, KEY_DATA_TYPE to KEY_DATA_TYPE, KEY_IS_LIVE to KEY_IS_LIVE, KEY_VIDEO_WIDTH to KEY_VIDEO_WIDTH, KEY_VIDEO_HEIGHT to KEY_VIDEO_HEIGHT, KEY_AUDIO_CHANNEL_CONFIG to KEY_AUDIO_CHANNEL_CONFIG, KEY_PURCHASE_PRICE to KEY_PURCHASE_PRICE, KEY_RENTAL_PRICE to KEY_RENTAL_PRICE, KEY_RATING_STYLE to KEY_RATING_STYLE, KEY_RATING_SCORE to KEY_RATING_SCORE, KEY_PRODUCTION_YEAR to KEY_PRODUCTION_YEAR, KEY_COLUMN_DURATION to KEY_COLUMN_DURATION, KEY_ACTION to KEY_ACTION, BaseColumns._ID to ("rowid AS " + BaseColumns._ID), SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID to ("rowid AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID), SearchManager.SUGGEST_COLUMN_SHORTCUT_ID to ("rowid AS " + SearchManager.SUGGEST_COLUMN_SHORTCUT_ID) ) } }
Java
... private static HashMap<String, String> buildColumnMap() { HashMap<String, String> map = new HashMap<String, String>(); map.put(KEY_NAME, KEY_NAME); map.put(KEY_DESCRIPTION, KEY_DESCRIPTION); map.put(KEY_ICON, KEY_ICON); map.put(KEY_DATA_TYPE, KEY_DATA_TYPE); map.put(KEY_IS_LIVE, KEY_IS_LIVE); map.put(KEY_VIDEO_WIDTH, KEY_VIDEO_WIDTH); map.put(KEY_VIDEO_HEIGHT, KEY_VIDEO_HEIGHT); map.put(KEY_AUDIO_CHANNEL_CONFIG, KEY_AUDIO_CHANNEL_CONFIG); map.put(KEY_PURCHASE_PRICE, KEY_PURCHASE_PRICE); map.put(KEY_RENTAL_PRICE, KEY_RENTAL_PRICE); map.put(KEY_RATING_STYLE, KEY_RATING_STYLE); map.put(KEY_RATING_SCORE, KEY_RATING_SCORE); map.put(KEY_PRODUCTION_YEAR, KEY_PRODUCTION_YEAR); map.put(KEY_COLUMN_DURATION, KEY_COLUMN_DURATION); map.put(KEY_ACTION, KEY_ACTION); map.put(BaseColumns._ID, "rowid AS " + BaseColumns._ID); map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); map.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, "rowid AS " + SearchManager.SUGGEST_COLUMN_SHORTCUT_ID); return map; } ...
Trong ví dụ trước, hãy lưu ý ánh xạ đến SUGGEST_COLUMN_INTENT_DATA_ID
. Đây là phần của URI trỏ đến nội dung dành riêng cho dữ liệu trong
hàng—phần cuối cùng của URI, mô tả nơi lưu trữ nội dung. Phần đầu tiên của URI,
khi giá trị chung cho tất cả các hàng trong bảng, được thiết lập trong
searchable.xml
dưới dạng tệp
android:searchSuggestIntentData
, như được mô tả trong
Phần Xử lý cụm từ tìm kiếm được đề xuất.
Nếu phần đầu tiên của URI khác nhau đối với mỗi hàng trong
bảng, liên kết giá trị đó với trường SUGGEST_COLUMN_INTENT_DATA
.
Khi người dùng chọn nội dung này, ý định kích hoạt sẽ cung cấp dữ liệu ý định từ
tổ hợp SUGGEST_COLUMN_INTENT_DATA_ID
và thuộc tính android:searchSuggestIntentData
hoặc thuộc tính
Giá trị trường SUGGEST_COLUMN_INTENT_DATA
.
Cung cấp dữ liệu cụm từ tìm kiếm được đề xuất
Triển khai Trình cung cấp nội dung
để trả về các đề xuất cụm từ tìm kiếm cho hộp thoại tìm kiếm của Android TV. Hệ thống truy vấn nội dung của bạn
để nhận đề xuất bằng cách gọi phương thức query()
mỗi lần
một chữ cái được nhập. Trong quá trình triển khai query()
, nội dung của bạn
tìm kiếm dữ liệu đề xuất của bạn và trả về một Cursor
trỏ đến
các hàng bạn đã chỉ định cho các đề xuất.
Kotlin
fun query(uri: Uri, projection: Array<String>, selection: String, selectionArgs: Array<String>, sortOrder: String): Cursor { // Use the UriMatcher to see what kind of query we have and format the db query accordingly when (URI_MATCHER.match(uri)) { SEARCH_SUGGEST -> { Log.d(TAG, "search suggest: ${selectionArgs[0]} URI: $uri") if (selectionArgs == null) { throw IllegalArgumentException( "selectionArgs must be provided for the Uri: $uri") } return getSuggestions(selectionArgs[0]) } else -> throw IllegalArgumentException("Unknown Uri: $uri") } } private fun getSuggestions(query: String): Cursor { val columns = arrayOf<String>( BaseColumns._ID, VideoDatabase.KEY_NAME, VideoDatabase.KEY_DESCRIPTION, VideoDatabase.KEY_ICON, VideoDatabase.KEY_DATA_TYPE, VideoDatabase.KEY_IS_LIVE, VideoDatabase.KEY_VIDEO_WIDTH, VideoDatabase.KEY_VIDEO_HEIGHT, VideoDatabase.KEY_AUDIO_CHANNEL_CONFIG, VideoDatabase.KEY_PURCHASE_PRICE, VideoDatabase.KEY_RENTAL_PRICE, VideoDatabase.KEY_RATING_STYLE, VideoDatabase.KEY_RATING_SCORE, VideoDatabase.KEY_PRODUCTION_YEAR, VideoDatabase.KEY_COLUMN_DURATION, VideoDatabase.KEY_ACTION, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID ) return videoDatabase.getWordMatch(query.toLowerCase(), columns) }
Java
@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // Use the UriMatcher to see what kind of query we have and format the db query accordingly switch (URI_MATCHER.match(uri)) { case SEARCH_SUGGEST: Log.d(TAG, "search suggest: " + selectionArgs[0] + " URI: " + uri); if (selectionArgs == null) { throw new IllegalArgumentException( "selectionArgs must be provided for the Uri: " + uri); } return getSuggestions(selectionArgs[0]); default: throw new IllegalArgumentException("Unknown Uri: " + uri); } } private Cursor getSuggestions(String query) { query = query.toLowerCase(); String[] columns = new String[]{ BaseColumns._ID, VideoDatabase.KEY_NAME, VideoDatabase.KEY_DESCRIPTION, VideoDatabase.KEY_ICON, VideoDatabase.KEY_DATA_TYPE, VideoDatabase.KEY_IS_LIVE, VideoDatabase.KEY_VIDEO_WIDTH, VideoDatabase.KEY_VIDEO_HEIGHT, VideoDatabase.KEY_AUDIO_CHANNEL_CONFIG, VideoDatabase.KEY_PURCHASE_PRICE, VideoDatabase.KEY_RENTAL_PRICE, VideoDatabase.KEY_RATING_STYLE, VideoDatabase.KEY_RATING_SCORE, VideoDatabase.KEY_PRODUCTION_YEAR, VideoDatabase.KEY_COLUMN_DURATION, VideoDatabase.KEY_ACTION, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID }; return videoDatabase.getWordMatch(query, columns); } ...
Trong tệp kê khai, trình cung cấp nội dung được xử lý đặc biệt. Thay vì
được gắn thẻ là một hoạt động, thì hoạt động đó được mô tả là một
<provider>
. Chiến lược phát hành đĩa đơn
nhà cung cấp bao gồm thuộc tính android:authorities
để cho hệ thống biết
của nhà cung cấp nội dung của bạn. Ngoài ra, bạn phải đặt thuộc tính android:exported
thành
"true"
để tính năng tìm kiếm chung trên Android có thể sử dụng các kết quả được trả về.
<provider android:name="com.example.android.tvleanback.VideoContentProvider" android:authorities="com.example.android.tvleanback" android:exported="true" />
Xử lý cụm từ tìm kiếm được đề xuất
Ứng dụng của bạn phải bao gồm
res/xml/searchable.xml
để định cấu hình chế độ cài đặt cụm từ tìm kiếm được đề xuất.
Trong tệp res/xml/searchable.xml
, hãy đưa vào
android:searchSuggestAuthority
để cho hệ thống biết không gian tên của
nhà cung cấp nội dung của Google. Giá trị này phải khớp với giá trị chuỗi mà bạn chỉ định trong
android:authorities
của <provider>
trong tệp AndroidManifest.xml
của bạn.
Đồng thời hãy bao gồm nhãn, chính là tên của ứng dụng. Các chế độ cài đặt tìm kiếm hệ thống sử dụng nhãn này khi liệt kê ứng dụng có thể tìm kiếm.
Tệp searchable.xml
cũng phải bao gồm
android:searchSuggestIntentAction
có giá trị "android.intent.action.VIEW"
để xác định thao tác theo ý định để cung cấp nội dung đề xuất tuỳ chỉnh. Ý định này khác với ý định
để cung cấp cụm từ tìm kiếm, như mô tả trong phần sau.
Để biết những cách khác để khai báo thao tác theo ý định cho nội dung đề xuất,
hãy xem Khai báo
thao tác theo ý định.
Cùng với thao tác theo ý định, ứng dụng của bạn phải cung cấp dữ liệu ý định mà bạn chỉ định bằng
Thuộc tính android:searchSuggestIntentData
. Đây là phần đầu tiên của URI trỏ đến
đối với nội dung, trong đó mô tả một phần URI chung cho tất cả các hàng trong bảng liên kết cho
nội dung. Phần URI duy nhất cho mỗi hàng được thiết lập bằng trường SUGGEST_COLUMN_INTENT_DATA_ID
,
như mô tả trong phần Xác định cột.
Để biết các cách khác nhằm khai báo dữ liệu ý định cho nội dung đề xuất, hãy xem
Khai báo
dữ liệu ý định.
Thuộc tính android:searchSuggestSelection=" ?"
chỉ định giá trị được truyền
dưới dạng tham số selection
của query()
. Giá trị dấu chấm hỏi (?
) được thay thế bằng văn bản truy vấn.
Cuối cùng, bạn cũng phải đưa
Thuộc tính android:includeInGlobalSearch
có giá trị "true"
. Sau đây là ví dụ
Tệp searchable.xml
:
<searchable xmlns:android="http://schemas.android.com/apk/res/android" android:label="@string/search_label" android:hint="@string/search_hint" android:searchSettingsDescription="@string/settings_description" android:searchSuggestAuthority="com.example.android.tvleanback" android:searchSuggestIntentAction="android.intent.action.VIEW" android:searchSuggestIntentData="content://com.example.android.tvleanback/video_database_leanback" android:searchSuggestSelection=" ?" android:searchSuggestThreshold="1" android:includeInGlobalSearch="true"> </searchable>
Xử lý cụm từ tìm kiếm
Ngay khi hộp thoại tìm kiếm có một từ khớp với giá trị ở một trong các cột của ứng dụng, như
được mô tả trong phần Xác định cột, hệ thống sẽ kích hoạt
Ý định ACTION_SEARCH
.
Hoạt động trong ứng dụng của bạn xử lý
ý định sẽ tìm kiếm trong kho lưu trữ các cột có chứa từ đã cho trong giá trị và trả về một danh sách
mục nội dung có các cột đó. Trong tệp AndroidManifest.xml
, bạn chỉ định
hoạt động xử lý ACTION_SEARCH
ý định như trong ví dụ sau:
... <activity android:name="com.example.android.tvleanback.DetailsActivity" android:exported="true"> <!-- Receives the search request. --> <intent-filter> <action android:name="android.intent.action.SEARCH" /> <!-- No category needed, because the Intent will specify this class component --> </intent-filter> <!-- Points to searchable meta data. --> <meta-data android:name="android.app.searchable" android:resource="@xml/searchable" /> </activity> ... <!-- Provides search suggestions for keywords against video meta data. --> <provider android:name="com.example.android.tvleanback.VideoContentProvider" android:authorities="com.example.android.tvleanback" android:exported="true" /> ...
Hoạt động cũng phải mô tả cấu hình có thể tìm kiếm được kèm theo thông tin tham chiếu đến
searchable.xml
.
Để sử dụng hộp thoại tìm kiếm chung,
tệp kê khai phải mô tả hoạt động nào sẽ nhận được truy vấn tìm kiếm. Tệp kê khai cũng phải
mô tả <provider>
, chính xác như mô tả trong tệp searchable.xml
.
Liên kết sâu đến ứng dụng của bạn trong màn hình chi tiết
Nếu bạn đã thiết lập cấu hình tìm kiếm như được mô tả trong phần Xử lý tìm kiếm
đề xuất và liên kết SUGGEST_COLUMN_TEXT_1
,
SUGGEST_COLUMN_PRODUCTION_YEAR
và
SUGGEST_COLUMN_DURATION
trường như được mô tả trong
phần Xác định cột,
liên kết sâu đến một thao tác xem đối với nội dung của bạn sẽ xuất hiện trong màn hình chi tiết sẽ khởi chạy khi
người dùng chọn một kết quả tìm kiếm:
Khi người dùng chọn đường liên kết cho ứng dụng của bạn, được xác định bằng nút **Đang bật** trong
màn hình chi tiết, hệ thống sẽ chạy hoạt động xử lý ACTION_VIEW
đặt thành
android:searchSuggestIntentAction
có giá trị "android.intent.action.VIEW"
trong
tệp searchable.xml
.
Bạn cũng có thể thiết lập một ý định tuỳ chỉnh để chạy hoạt động của mình. Điều này được thể hiện trong
Ứng dụng mẫu Leanback
của Google. Lưu ý ứng dụng mẫu khởi chạy LeanbackDetailsFragment
riêng để
hiển thị thông tin chi tiết về nội dung nghe nhìn đã chọn; trong ứng dụng của bạn, hãy chạy hoạt động phát nội dung nghe nhìn
ngay lập tức để lưu lại cho người dùng một hoặc hai nhấp chuột khác.
Hành vi tìm kiếm
Tính năng tìm kiếm hoạt động trên Android TV từ màn hình chính và từ bên trong ứng dụng. Kết quả tìm kiếm khác nhau cho hai trường hợp này.
Tìm kiếm từ màn hình chính
Khi người dùng tìm kiếm từ màn hình chính, kết quả đầu tiên sẽ xuất hiện trong một thẻ thực thể. Nếu có những ứng dụng có thể phát nội dung, một đường liên kết đến từng ứng dụng sẽ xuất hiện ở cuối thẻ:
Bạn không thể lập trình để đặt ứng dụng vào thẻ thực thể. Để được đưa vào dưới dạng chế độ phát lại, kết quả tìm kiếm của một ứng dụng phải khớp với tiêu đề, năm và thời lượng của nội dung tìm kiếm.
Có thể bạn sẽ thấy thêm kết quả tìm kiếm bên dưới thẻ này. Để thấy các chế độ xem này, người dùng phải nhấn nút từ xa và cuộn xuống. Kết quả cho mỗi ứng dụng xuất hiện trong một hàng riêng. Bạn không thể kiểm soát thứ tự hàng. Ứng dụng hỗ trợ hành động xem được liệt kê đầu tiên.
Tìm kiếm trong ứng dụng của bạn
Người dùng cũng có thể bắt đầu tìm kiếm từ bên trong ứng dụng của bạn bằng cách khởi động micrô từ điều khiển từ xa hoặc tay điều khiển trò chơi trên tay điều khiển trò chơi. Kết quả tìm kiếm được hiển thị trong một hàng duy nhất ở đầu nội dung của ứng dụng. Ứng dụng của bạn tạo kết quả tìm kiếm bằng cách sử dụng nhà cung cấp dịch vụ tìm kiếm toàn cầu của riêng ứng dụng đó.
Tìm hiểu thêm
Để tìm hiểu thêm về cách tìm kiếm một ứng dụng TV, hãy đọc Tích hợp các tính năng tìm kiếm của Android vào ứng dụng của bạn và Thêm chức năng tìm kiếm.
Để biết thêm thông tin về cách tuỳ chỉnh trải nghiệm tìm kiếm trong ứng dụng bằng SearchFragment
, hãy đọc
Tìm kiếm trong ứng dụng dành cho TV.