Pesquisar em apps para TV

Muitas vezes, os usuários têm um conteúdo específico em mente ao usar um app de mídia na TV. Se o app tem um grande catálogo de conteúdo, a navegação por um título específico pode não ser a maneira mais eficiente de os usuários encontrarem o que procuram. Uma interface de pesquisa pode ajudar os usuários a encontrar o conteúdo que procuram com mais rapidez do que a navegação.

A biblioteca Leanback androidx oferece um conjunto de classes para ativar uma interface de pesquisa padrão no seu app, que é consistente com outras funções de pesquisa na TV e oferece recursos como entrada de texto por voz.

Este guia discute como disponibilizar uma interface de pesquisa no app usando classes da Biblioteca de Suporte Leanback.

Adicionar uma ação de pesquisa

Ao usar a classe BrowseFragment para uma interface de navegação de mídia, você pode ativar uma interface de pesquisa como parte padrão da interface do usuário. A interface de pesquisa é um ícone que aparece no layout quando você define View.OnClickListener no objeto BrowseFragment. O código de amostra a seguir ilustra essa técnica.

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());
}

Observação:é possível definir a cor do ícone de pesquisa usando o método setSearchAffordanceColor(int).

Adicionar uma entrada de pesquisa e resultados

Quando o usuário seleciona o ícone de pesquisa, o sistema invoca uma atividade de pesquisa usando a intent definida. Para sua atividade de pesquisa, use um layout linear que contenha um SearchFragment. Esse fragmento também precisa implementar a interface SearchFragment.SearchResultProvider para exibir os resultados de uma pesquisa.

O exemplo de código a seguir mostra como estender a classe SearchFragment para fornecer uma interface de pesquisa e resultados:

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;
    }
}

O código de exemplo anterior precisa ser usado com uma classe SearchRunnable que executa a consulta de pesquisa em uma linha de execução separada. Essa técnica impede que consultas possivelmente lentas bloqueiem a linha de execução principal da interface do usuário.