在HarmonyOS官方文档中,有这样一项功能是只能在智慧屏上使用的,那就是可见即可说。恰好在很久之前参照官方分布式音乐播放器定制了一款自己的播放器,今天将其改造成智慧屏应用,并添加可见即可说功能。待真机演示,有设备的小伙伴可以测试一下!
一、效果演示
- 各设备
- 手机平板,智慧屏
- 真机待测…(to do 7月7号)
二、可见即可说功能
按照官方文档的介绍,可见即可说就是将一些热词与Component关联,达到监听语音热词,来执行一些相应操作。例如,浏览图片的时候,说出图片的名字或者角标序号,从而实现打开图片的效果。
那么相应的,我们就能将分布式音乐播放器改造成语音控制的,比如将”播放”,“暂停”,”上一首”等热词绑定到对应组件上,监测到热词的时候执行功能即可。
三、可见即可说开发
1、热词注册
- 创建Component.VoiceEvent对象,设置相应的热词,英文和中文都是可以的。
可见即可说的功能的核心就是,Component.VoiceEvent对象,一个对象对应一个事件。
// 比如说设置一个播放事件
Component.VoiceEvent eventplay = new Component.VoiceEvent("播放");
- 一个Component.VoiceEvent对象,可以绑定多个热词。
eventplay.addSynonyms("play");
- 绑定完热词后,哪个组件需要这个语音事件,哪个组件就需要进行注册。
//比如分布式音乐播放器里面的播放按钮,对该语音事件进行注册。
musicPlayButton.subscribeVoiceEvents(eventplay);
- 如果组件有多个语音事件要响应,我们就的创建多个Component.VoiceEvent对象,并且都进行注册。一个对象对应一个事件。
2、事件开发
在前面,我们设置了语音事件,并且将一个播放按钮对其进行了注册。但也仅仅是注册,然后呢?然后就没然后了,因为我们还没有进行事件开发,按钮要在事件发生时做出响应。
(1)实现SpeechEventListener接口
private Component.SpeechEventListener speechEventListener = new Component.SpeechEventListener(){
@Override
public boolean onSpeechEvent(Component v, SpeechEvent event) {
if (event.getActionProperty().equals("播放")) {
... // 检测注册的热词,进行相应的处理
playOrpause();
}
return false;
};
}
(2)通过setSpeechEventListener方法实现回调注册
musicplayButton.setSpeechEventListener(speechEventListener);
至此,我们对可见即可说的功能已经了解了,那么下面是对分布式音乐播放器案例的改造,感兴趣的读者往下看。
四、案例编写
1、工程结构
2、UI设计
3、架构简析
这里简单剖析一下架构,详情见附件工程文件。
- PlayerManager.java
封装好的播放器类,设置音乐路径,播放暂停,上一首下一首的功能。
/**
* 首先播放之前要准备好媒体资源
*/
public void prepareMusic(){...}
/**
* 准备好音频路径 准备媒体资源
* @param Uri
*/
public void setResource(String Uri){...}
/**
* 播放
*/
public void play(){...}
/**
* 暂停
*/
public void pause(){...}
/**
* 定时事件通知更新进度条
* DELAY_TIME 延迟1s
* PERIOD 两个事件间隔1s
*/
private void startTimetask(){...}
//.....
- StateListener
播放器状态监听接口,监听播放器状态进而进行一些事件通知。
package com.yzj.musicplayer.Player;
public interface StateListener {
void onPlaySuccess(int totalTime);
void onPauseSuccess();
void onPositionChange(int currentTime);
void onMusicFinished();
void onUriSet(String name);
}
- CommonProvider,ViewProvidor
 用来生成dialog,显示可分布式流转的设备列表, 对此不赘述,用JAVA做UI体验不是很好。
- MainAbilitySlice
主页面
4、绑定可见即可说事件
这里我们有播放,暂停,上一首,下一首,拖动进度条,分布式流转等操作。
我们逐一为其添加语音事件。
//测试
//播放
private Component.VoiceEvent eventplay;
//暂停
private Component.VoiceEvent eventpause;
//下一首
private Component.VoiceEvent eventnext;
//前一首
private Component.VoiceEvent eventpre;
//流转
private Component.VoiceEvent eventremote;
//流转的语音相应事件
private Component.SpeechEventListener speech_mShowDeviceListListener = new Component.SpeechEventListener() {
@Override
public boolean onSpeechEvent(Component component, SpeechEvent speechEvent) {
if(speechEvent.getActionProperty().equals("流转")){
// 显示选择设备列表
continuationRegisterManager.showDeviceList(abilityToken, null, null);
}
return false;
}
};
void initview(){
//绑定热词
eventplay = new Component.VoiceEvent("播放");
eventpause = new Component.VoiceEvent("暂停");
eventnext = new Component.VoiceEvent("下一首");
eventpre = new Component.VoiceEvent("上一首");
eventremote = new Component.VoiceEvent("流转");
//播放按钮注册热词
musicPlayButton.subscribeVoiceEvents(eventplay);
musicPlayButton.subscribeVoiceEvents(eventpause);
//播放按钮设置响应事件
musicPlayButton.setSpeechEventListener(new Component.SpeechEventListener() {
@Override
public boolean onSpeechEvent(Component component, SpeechEvent speechEvent) {
if(speechEvent.getActionProperty().equals("播放")){
if(playerManager.isPlaying()){
Log.info(TAG,"正在播放");
}
else{
playOrPause();
}
return true;
}
else if(speechEvent.getActionProperty().equals("暂停")){
if(!playerManager.isPlaying()){
Log.info(TAG,"已经暂停了");
}
else{
playOrPause();
}
return true;
}
return false;
};
});
//下一首注册热词
playnextButton.subscribeVoiceEvents(eventnext);
//下一首设置响应事件
playnextButton.setSpeechEventListener(new Component.SpeechEventListener() {
@Override
public boolean onSpeechEvent(Component component, SpeechEvent speechEvent) {
if(speechEvent.getActionProperty().equals("下一首")){
nextMusic(component);
return true;
}
return false;
}
});
//上一首注册热词
playpreButton.subscribeVoiceEvents(eventpre);
//上一首设置响应事件
playpreButton.setSpeechEventListener(new Component.SpeechEventListener() {
@Override
public boolean onSpeechEvent(Component component, SpeechEvent speechEvent) {
if(speechEvent.getActionProperty().equals("上一首")){
prevMusic(component);
return true;
}
return false;
}
});
remotePlay.setClickedListener(mShowDeviceListListener);
//流转按钮注册热词
remotePlay.subscribeVoiceEvents(eventremote);
//流转按钮设置流转弹窗事件
remotePlay.setSpeechEventListener(speech_mShowDeviceListListener);
}
这里只展示了核心部分的代码,具体含义看名称即可知,详情参见附件。
五、关于分布式流转
关于流转的部分,这里简单复习一下。
在本案例里,任何动态变化的数据都是迁移和恢复的内容。
六、关于旋转动画
- 创建一个属性动画
/* 属性动画 */
private AnimatorProperty animatorProperty;
- 初始化一个属性对象
//初始化属性动画对象 musicPosters是一个Image组件
animatorProperty = musicPosters.createAnimatorProperty();
animatorProperty.setCurveType(Animator.CurveType.LINEAR);
- 启动
//让他一直循环转下去
animatorProperty.rotate(360+musicPosters.getRotation()).setDuration(100000).setLoopedCount(-1).start();
- 暂停,重置
animatorProperty.stop();
animatorProperty.reset();
各种操作放在合适的位置执行就可以了。
七、结语
本次主要在分布式音乐播放器案例中加入了智慧屏特有的可见即可说的功能,和一些简单的优化和动画。在手机,平板上也能有类似的操作,可参考分布式语音照相机,但相比之下还是觉得可见即可说的功能更加清楚和好用。