Cómo realizar búsquedas en apps para TV

Los usuarios suelen tener contenido específico en la mente cuando usan una app de música en la TV. Si la app contiene un extenso catálogo de contenido, buscar un título específico podría no ser la forma más eficaz para que los usuarios encuentren lo que buscan. Una interfaz de búsqueda puede ayudar a tus usuarios a llegar el contenido que desean más rápido que navegar.

La biblioteca de androidx.Lean proporciona un conjunto de clases para habilitar una interfaz de búsqueda estándar en la app que sea coherente con otras funciones de búsqueda en TV y ofrece funciones como entrada de voz.

En esta guía, se explica cómo proporcionar una interfaz de búsqueda en tu app mediante la compatibilidad con Leanback las clases de biblioteca.

Cómo agregar una acción de búsqueda

Cuando usas la clase BrowseFragment para un elemento multimedia interfaz de navegación, puedes habilitar una interfaz de búsqueda como parte estándar del usuario interfaz de usuario. La interfaz de búsqueda es un ícono que aparece en el diseño cuando configuras View.OnClickListener en BrowseFragment . En el siguiente código de muestra, se ilustra esta 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());
}

Nota: Puedes configurar el color del ícono de búsqueda con las setSearchAffordanceColor(int) .

Cómo agregar una entrada para búsqueda y resultados

Cuando un usuario selecciona el ícono de búsqueda, el sistema invoca una actividad de búsqueda usando el intent definido. Para tu actividad de búsqueda, usa un diseño lineal que contenga una SearchFragment Este fragmento también debe implementar SearchFragment.SearchResultProvider para mostrar los resultados de una búsqueda.

En la siguiente muestra de código, se indica cómo extender la clase SearchFragment para proporcionar una interfaz de búsqueda y 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;
    }
}

El código de ejemplo anterior está diseñado para usarse con una clase SearchRunnable que ejecute la búsqueda en un subproceso independiente. Esta técnica mantiene un potencial de ejecución lenta que las consultas bloqueen el subproceso principal de la interfaz de usuario.