TV アプリ内で検索する

ユーザーがテレビでメディアアプリを使用する際、特定のコンテンツを念頭に置いていることがよくあります。アプリ内のコンテンツ カタログが大きい場合、ブラウジングしながら特定のタイトルを探そうとしてもあまり効率的ではなく、探しているコンテンツがなかなか見つからないことがあります。検索インターフェースがあれば、ユーザーは、ブラウジングしながらの場合よりも早く目的のコンテンツにたどり着くことができます。

Leanback AndroidX ライブラリは、アプリ内で標準の検索インターフェースを使用できるようにするクラスのセットを提供します。この検索インターフェースは、テレビの他の検索機能と整合性があり、音声入力などの機能も備えています。

このレッスンでは、Leanback サポート ライブラリ クラスを使用して、アプリ内で検索インターフェースを提供する方法について説明します。

検索アクションを追加する

メディア ブラウジング インターフェースに対して BrowseFragment クラスを使用すると、ユーザー インターフェースの標準部分として検索インターフェースを有効にすることができます。検索インターフェースは、レイアウト内に表示されるアイコンで、View.OnClickListenerBrowseFragment オブジェクトに対して設定したときにレイアウト内に表示されます。この方法を次のサンプルコードに示します。

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) を使用すると、検索アイコンの色を設定できます。

検索入力と検索結果を追加する

ユーザーが検索アイコンを選択すると、システムは、定義済みインテントを通じて検索アクティビティを呼び出します。検索アクティビティは、SearchFragment を含む線形レイアウトを使用する必要があります。また、このフラグメントは、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 クラスと一緒に使用することを想定しています。この方法により、低速のクエリがメイン ユーザー インターフェース スレッドをブロックするのを防ぐことができます。