Поиск в телевизионных приложениях

Пользователи часто ищут конкретный контент, используя медиаприложение на телевизоре. Если ваше приложение содержит большой каталог контента, поиск конкретного названия может быть не самым эффективным способом для пользователей найти то, что им нужно. Интерфейс поиска может помочь вашим пользователям быстрее найти нужный контент, чем поиск.

Библиотека androidx.leanback предоставляет набор классов для создания стандартного интерфейса поиска в вашем приложении, совместимого с другими функциями поиска на телевизоре, а также обеспечивает такие возможности, как голосовой ввод.

В этом руководстве рассматривается, как реализовать интерфейс поиска в вашем приложении, используя классы библиотеки поддержки Leanback.

Добавить действие поиска

При использовании класса BrowseFragment для интерфейса просмотра медиафайлов можно включить интерфейс поиска в качестве стандартной части пользовательского интерфейса. Интерфейс поиска представляет собой значок, который появляется в макете, когда вы устанавливаете View.OnClickListener для объекта BrowseFragment . Следующий пример кода демонстрирует этот метод.

Котлин

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 , чтобы предоставить интерфейс поиска и результаты:

Котлин

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 , который выполняет поисковый запрос в отдельном потоке. Этот метод предотвращает блокировку основного потока пользовательского интерфейса потенциально медленно выполняющимися запросами.

,

Пользователи часто ищут конкретный контент, используя медиаприложение на телевизоре. Если ваше приложение содержит большой каталог контента, поиск конкретного названия может быть не самым эффективным способом для пользователей найти то, что им нужно. Интерфейс поиска может помочь вашим пользователям быстрее найти нужный контент, чем поиск.

Библиотека androidx.leanback предоставляет набор классов для создания стандартного интерфейса поиска в вашем приложении, совместимого с другими функциями поиска на телевизоре, а также обеспечивает такие возможности, как голосовой ввод.

В этом руководстве рассматривается, как реализовать интерфейс поиска в вашем приложении, используя классы библиотеки поддержки Leanback.

Добавить действие поиска

При использовании класса BrowseFragment для интерфейса просмотра медиафайлов можно включить интерфейс поиска в качестве стандартной части пользовательского интерфейса. Интерфейс поиска представляет собой значок, который появляется в макете, когда вы устанавливаете View.OnClickListener для объекта BrowseFragment . Следующий пример кода демонстрирует этот метод.

Котлин

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 , чтобы предоставить интерфейс поиска и результаты:

Котлин

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 , который выполняет поисковый запрос в отдельном потоке. Этот метод предотвращает блокировку основного потока пользовательского интерфейса потенциально медленно выполняющимися запросами.