版权声明:本文为博主原创翻译文章,转载请注明出处。
推荐:
欢迎关注我创建的Android TV 专题,会定期给大家分享一些AndroidTv相关的内容:
https://www.jianshu.com/c/3f0ab61a1322
Android TV 应用内搜寻
Android手机和Android TV之间最大的区别之一就是他们的输入法。 由于电视不支持触摸板,我们不应该期望用户使用电视键盘,为电视输入字词很麻烦。
Google建议在电视应用中使用语音输入进行搜索
BrowseFragment上的应用内搜索图标
BrowseFragment包含搜索功能的设计布局,并且在应用程序上显示应用内图标非常简单。 只需要实现setOnSearchClickedListener就可以了。
// Existence of this method make In-app search icon visible
setOnSearchClickedListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
setSearchAffordanceColor方法可用于指定搜索图标颜色。
// set search icon color
setSearchAffordanceColor(getResources().getColor(R.color.search_opaque));
当调用setOnSearchClickedListener时,应用内搜索图标将显示在BrowseFragment的右上角。
SearchFragment
需要实现搜索功能来回答用户的搜索查询。 为了显示搜索查询输入UI和搜索结果UI,LeanbackLibrary提供了SearchFragment。 这一次,当请求搜索(通过语音搜索按钮或显式按搜索按钮图标)时,我们从SearchActivity调用此SearchFragment。
private void setupEventListeners() {
setOnItemViewSelectedListener(new ItemViewSelectedListener());
setOnItemViewClickedListener(new ItemViewClickedListener());
// Existence of this method make In-app search icon visible
setOnSearchClickedListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getActivity(), SearchActivity.class);
startActivity(intent);
}
});
}
通过右键单击包名称 Create → New Activity → Blank Activity →键入“SearchActivity”,开始创建SearchActivity。 在创建了SearchActivity类和activity_search.xml布局之后,按如下所示修改res / acitivity_search.xml。
它附加SearchFragment。 再次,通过Create → New → Java class → SearchFragment,并使其成为android.support.v17.leanback.app.SearchFragment的子类。
如果您在这里构建和运行应用程序,则应用程序将崩溃,因为SearchFragment会自动从内部语音识别器开始获取搜索查询。
实现语音输入/语音搜索 - setSpeechRecognitionCallback
官方文件说,
如果您不通过setSpeechRecognitionCallback(SpeechRecognitionCallback)提供回调,则将使用内部语音识别器,您的应用程序将需要它来请求android.permission.RECORD_AUDIO。
所以你需要:
- 实现setSpeechRecognitionCallback
- 在AndroidManifest.xml上请求android.permission.RECORD_AUDIO
如下。
public class SearchFragment extends android.support.v17.leanback.app.SearchFragment {
private static final String TAG = SearchFragment.class.getSimpleName();
private static final int REQUEST_SPEECH = 0x00000010;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!Utils.hasPermission(getActivity(), Manifest.permission.RECORD_AUDIO)) {
// SpeechRecognitionCallback is not required and if not provided recognition will be handled
// using internal speech recognizer, in which case you must have RECORD_AUDIO permission
setSpeechRecognitionCallback(new SpeechRecognitionCallback() {
@Override
public void recognizeSpeech() {
Log.v(TAG, "recognizeSpeech");
try {
startActivityForResult(getRecognizerIntent(), REQUEST_SPEECH);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Cannot find activity for speech recognizer", e);
}
}
});
}
}
utils.java
public static boolean hasPermission(final Context context, final String permission) {
return PackageManager.PERMISSION_GRANTED == context.getPackageManager().checkPermission(
permission, context.getPackageName());
}
覆盖onSearchRequeseted以激活应用内搜索
当用户尝试语音输入搜索时,执行onSearchRequested回调,默认情况下将启动Google的全局内容搜索。
语音搜索功能与Google的默认语音搜索结果相关。
如果要激活应用内应用程序搜索,则需要重写此方法。
它是写在startSearch方法的描述中
它通常从onSearchRequested()直接从Activity.onSearchRequested()或任何给定的Activity中的覆盖版本调用。 如果您的目标只是激活搜索,则最好调用onSearchRequested(),这可能已被您的Activity中的其他地方覆盖。 如果您的目标是注入上下文数据等特定数据,则优先覆盖onSearchRequested(),以便任何调用者将从覆盖中获益。
我们覆盖MainActivity和SearchActivity的onSearchRequested方法。
@Override
public boolean onSearchRequested() {
startActivity(new Intent(this, SearchActivity.class));
return true;
}
自定义应用内搜索 - SearchResultProvider
SearchResultProvider接口是Leanback库的接口,用于监听搜索相关事件。 我们需要重写3种方法。
- getResultsAdapter - 返回包含搜索结果的适配器,以显示SearchFragment上的搜索结果。
- onQueryTextChange - 当用户更改搜索查询文本时调用的事件侦听器。
- onQueryTextSubmit - 当用户提交搜索查询文本时调用的事件侦听器。
我们需要使用setSearchResultProvider方法注册这个SearchResultProvider,最小化的实现就是这样,
public class SearchFragment extends android.support.v17.leanback.app.SearchFragment
implements android.support.v17.leanback.app.SearchFragment.SearchResultProvider {
private static final String TAG = SearchFragment.class.getSimpleName();
private static final int REQUEST_SPEECH = 0x00000010;
private ArrayObjectAdapter mRowsAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
setSearchResultProvider(this);
...
}
...
@Override
public ObjectAdapter getResultsAdapter() {
Log.d(TAG, "getResultsAdapter");
Log.d(TAG, mRowsAdapter.toString());
// It should return search result here,
// but static Movie Item list will be returned here now for practice.
ArrayList mItems = MovieProvider.getMovieItems();
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter());
listRowAdapter.addAll(0, mItems);
HeaderItem header = new HeaderItem("Search results");
mRowsAdapter.add(new ListRow(header, listRowAdapter));
return mRowsAdapter;
}
@Override
public boolean onQueryTextChange(String newQuery){
Log.i(TAG, String.format("Search Query Text Change %s", newQuery));
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
Log.i(TAG, String.format("Search Query Text Submit %s", query));
return true;
}
构建并运行
SearchActivity可以通过两种方式启动,
1.单击BrowseFragment的搜索图标。
2.当用户从特定控制器启动语音输入搜索*1(将调用onSearchRequested。)
*1这取决于Android TV设备。 例如,SONY BRAVIA提供触摸板遥控器,可以从该遥控器完成语音搜索。
SearchFragment现在显示模拟搜索结果。
源码在 github上。
我们应该实现OnItemViewClickedListener来定义点击这些搜索结果的动作。 这个OnItemViewClickedListener可以被设置
setOnItemViewClickedListener(new ItemViewClickedListener());
(在这种情况下)在onCreate的BrowseFragment。
关注微信公众号,定期为你推荐移动开发相关文章。