在 TV 应用内进行搜索

在电视上使用媒体应用时,用户通常会想到一些具体内容。如果您的应用包含一个较大的内容目录,那么浏览具体的标题可能不是用户查找所需内容的最高效方式。相比浏览来说,搜索界面可以帮助用户更快地找到需要的内容。

Leanback androidx 库提供了一组类,可让您在应用中实现与电视上其他搜索功能一致的标准搜索界面;另外,这个库还提供了语音输入等功能。

本节课探讨如何在您的应用中利用 Leanback 支持库类提供搜索界面。

添加搜索操作

当您将 BrowseFragment 类用于媒体浏览界面时,您可以启用搜索界面,作为用户界面中的一个标准组成部分。搜索界面是当您在 BrowseFragment 对象上设置 View.OnClickListener 时,布局中显示的一个图标。以下示例代码演示了这一方法。

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.browse_activity)
        browseFragment = fragmentManager.findFragmentById(R.id.browse_fragment) as BrowseFragment
        browseFragment.setOnSearchClickedListener { view ->
            val intent = Intent(this@BrowseActivity, SearchActivity::class.java)
            startActivity(intent)
        }

        browseFragment.setAdapter(buildAdapter())
    }
    

Java

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.browse_activity);

        browseFragment = (BrowseFragment)
                getFragmentManager().findFragmentById(R.id.browse_fragment);

        ...

        browseFragment.setOnSearchClickedListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(BrowseActivity.this, SearchActivity.class);
                startActivity(intent);
            }
        });

        browseFragment.setAdapter(buildAdapter());
    }
    

注意:您可以使用 setSearchAffordanceColor(int) 设置搜索图标的颜色。

添加搜索输入和结果

当用户选择搜索图标时,系统会通过定义的 intent 调用搜索 Activity。 搜索 Activity 应使用包含 SearchFragment 的线性布局。此 Fragment 还必须实现 SearchFragment.SearchResultProvider 界面,以便显示搜索结果。

以代码示例展示了如何扩展 SearchFragment 类来提供搜索界面和结果:

Kotlin

    class MySearchFragment : SearchFragment(), SearchFragment.SearchResultProvider {
        private val rowsAdapter = ArrayObjectAdapter(ListRowPresenter())
        private val handler = Handler()
        private val delayedLoad = SearchRunnable()

        val resultsAdapter: ObjectAdapter
        get() {
            return rowsAdapter
        }

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setSearchResultProvider(this)
            setOnItemClickedListener(getDefaultItemClickedListener())
        }

        fun onQueryTextChange(newQuery: String): Boolean {
            rowsAdapter.clear()
            if (!TextUtils.isEmpty(newQuery)) {
                delayedLoad.setSearchQuery(newQuery)
                handler.removeCallbacks(delayedLoad)
                handler.postDelayed(delayedLoad, SEARCH_DELAY_MS)
            }
            return true
        }

        fun onQueryTextSubmit(query: String): Boolean {
            rowsAdapter.clear()
            if (!TextUtils.isEmpty(query)) {
                delayedLoad.setSearchQuery(query)
                handler.removeCallbacks(delayedLoad)
                handler.postDelayed(delayedLoad, SEARCH_DELAY_MS)
            }
            return true
        }

        companion object {
            private val SEARCH_DELAY_MS = 300
        }
    }
    

Java

    public class MySearchFragment extends SearchFragment
            implements SearchFragment.SearchResultProvider {

        private static final int SEARCH_DELAY_MS = 300;
        private ArrayObjectAdapter rowsAdapter;
        private Handler handler = new Handler();
        private SearchRunnable delayedLoad;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
            setSearchResultProvider(this);
            setOnItemClickedListener(getDefaultItemClickedListener());
            delayedLoad = new SearchRunnable();
        }

        @Override
        public ObjectAdapter getResultsAdapter() {
            return rowsAdapter;
        }

        @Override
        public boolean onQueryTextChange(String newQuery) {
            rowsAdapter.clear();
            if (!TextUtils.isEmpty(newQuery)) {
                delayedLoad.setSearchQuery(newQuery);
                handler.removeCallbacks(delayedLoad);
                handler.postDelayed(delayedLoad, SEARCH_DELAY_MS);
            }
            return true;
        }

        @Override
        public boolean onQueryTextSubmit(String query) {
            rowsAdapter.clear();
            if (!TextUtils.isEmpty(query)) {
                delayedLoad.setSearchQuery(query);
                handler.removeCallbacks(delayedLoad);
                handler.postDelayed(delayedLoad, SEARCH_DELAY_MS);
            }
            return true;
        }
    }
    

上面所示的代码示例应当与单独的 SearchRunnable 类搭配使用,该类将在单独的线程中运行搜索查询。这一方法可以防止运行速度可能较慢的查询阻塞主要的用户界面线程。