本示例介绍使用ArkUI中的容器组件FolderStack在折叠屏设备中实现音乐播放器场景,展示当前播放歌曲信息,支持播控中心控制播放和后台播放能力。
使用说明
采用MVVM模式进行架构设计,目录结构中区分展示层、模型层、控制层,展示层通过控制层与模型层沟通,展示层的状态数据与控制层进行双向绑定,模型层的变更通过回调形式通知给控制层,并最终作用于展示层。
在可折叠设备上使用FolderStack组件作为容器组件,承载播放器的所有功能组件,在半折叠态上,使需要移动到上屏的子组件产生相应的动态效果。
// TODO:知识点:FolderStack继承于Stack控件,通过upperItems字段识别指定id的组件,自动避让折叠屏折痕区后移到上半屏
FolderStack({ upperItems: [CommonConstants.FOLDER_STACK_UP_COMP_ID] }) {
MusicPlayerInfoComp({ musicModel: this.musicModel, curFoldStatus: this.curFoldStatus })
.id(CommonConstants.FOLDER_STACK_UP_COMP_ID)
MusicPlayerCtrlComp({ musicModel: this.musicModel })
}
源码请参考MusicPlayerPage.ets
Image(this.musicModel.coverRes)
.width(this.curImgSize)
.height(this.curImgSize)
.margin(20)
.animation(this.attrAniCfg)
.interpolation(ImageInterpolation.High)
.draggable(false)
display.on('foldStatusChange', (curFoldStatus: display.FoldStatus) => {
this.curFoldStatus = curFoldStatus;
this.windowModel.updateMainWinPreferredOrientation(curFoldStatus);
})
源码请参考MusicPlayerPage.ets
// TODO:知识点:创建AVSession实例
this.session = await AVSessionManager.createAVSession(this.bindContext!, this.avSessionTag, this.avSessionType);
// TODO:知识点:注册AVSession事件
await this.registerSessionListener(eventListener);
// TODO:知识点:激活AVSession实例
await this.session.activate();
源码请参考AVSessionModel.ets
// 设置必要的媒体信息
let metadata: AVSessionManager.AVMetadata = {
assetId: '0', // 由应用指定,用于标识应用媒体库里的媒体
title: musicModel?.title,
mediaImage: imagePixel,
artist: musicModel?.singer,
duration: musicModel?.totalTime,
lyric: lrcStr
}
// TODO:知识点:设置AVSession元信息
this.session?.setAVMetadata(metadata).then(() => {
logger.info(`SetAVMetadata successfully`);
}).catch((err: BusinessError) => {
logger.error(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
});
// TODO:知识点:设置AVSession当前状态
this.session?.setAVPlaybackState(this.curState, (err) => {
if (err) {
console.error(`Failed to set AVPlaybackState. Code: ${err.code}, message: ${err.message}`);
} else {
console.info(`SetAVPlaybackState successfully`);
}
});
源码请参考AVSessionModel.ets
let wantAgentInfo: wantAgent.WantAgentInfo = {
// 点击通知后,将要执行的动作列表
// 添加需要被拉起应用的bundleName和abilityName
wants: [
{
bundleName: "com.north.cases",
abilityName: "com.north.cases.EntryAbility"
}
],
// 指定点击通知栏消息后的动作是拉起ability
actionType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
// 通过wantAgent模块下getWantAgent方法获取WantAgent对象
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
// TODO:知识点:设置后台任务类型,启动后台任务
backgroundTaskManager.startBackgroundRunning(this.bindContext!,
backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, wantAgentObj).then(() => {
// 此处执行具体的长时任务逻辑,如放音等。
console.info(`Succeeded in operationing startBackgroundRunning.`);
this.isBackgroundTaskRunning = true;
}).catch((err: BusinessError) => {
console.error(`Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
});
});
源码请参考AVSessionModel.ets
// 播放
this.session?.on('play', () => {
logger.info('avsession on play');
eventListener.onPlay();
});
// 暂停
this.session?.on('pause', () => {
logger.info('avsession on pause');
eventListener.onPause();
});
// 停止
this.session?.on('stop', () => {
logger.info('avsession on stop');
eventListener.onStop();
});
// 下一首
this.session?.on('playNext', async () => {
logger.info('avsession on playNext');
eventListener.onPlayNext();
});
// 上一首
this.session?.on('playPrevious', async () => {
logger.info('avsession on playPrevious');
eventListener.onPlayPrevious();
});
// 拖进度
this.session?.on('seek', (position) => {
logger.info('avsession on seek', position.toString());
eventListener.onSeek(position);
});
// 标记喜好
this.session?.on('toggleFavorite', (assetId) => {
logger.info('avsession on toggleFavorite', assetId);
});
// 播放循环模式切换
this.session?.on('setLoopMode', (mode) => {
logger.info('avsession on setLoopMode', mode.toString());
eventListener.onSetLoopMode();
});
源码请参考AVSessionModel.ets
onViewModelChanged() {
const curMusiclyricsLine = this.viewModel.curMusiclyricsLine;
this.lyricsScrollerCtrl.scrollToIndex(curMusiclyricsLine, true, ScrollAlign.CENTER);
}
switch (this.curLoopMode) {
case AVSessionManager.LoopMode.LOOP_MODE_SINGLE: {
this.seek(0);
break;
}
case AVSessionManager.LoopMode.LOOP_MODE_LIST: {
this.curMusicModelIndex = (this.curMusicModelIndex + 1) % this.musicModelArr.length;
this.curMusicModelRaw = this.musicModelArr[this.curMusicModelIndex];
await this.reset();
await this.prepare(() => {
this.play();
});
this.avsessionModel?.setSessionInfo(this.curMusicModelRaw);
break;
}
case AVSessionManager.LoopMode.LOOP_MODE_SHUFFLE: {
const randomVal: number = Decimal.random(1).e;
let dieta: number = 1;
while (dieta < this.musicModelArr.length - 1) {
if (randomVal >= dieta - 1 / this.musicModelArr.length - 1 && randomVal < dieta / this.musicModelArr.length - 1) {
break;
}
dieta++;
}
this.curMusicModelIndex = (this.curMusicModelIndex + dieta) % this.musicModelArr.length;
this.curMusicModelRaw = this.musicModelArr[this.curMusicModelIndex];
await this.reset();
await this.prepare(() => {
this.play();
});
this.avsessionModel?.setSessionInfo(this.curMusicModelRaw);
break;
}
}
暂无
foldablescreencases // har类型
|---common
| |---constants
| | |---CommonConstants.ets // 通用常量
|---components
| |---MusicPlayerCtrlComp.ets // 自定义组件-音乐播放器控制栏
| |---MusicPlayerInfoComp.ets // 自定义组件-音乐播放器歌曲详情展示
|---model
| |---AVPlayerModel.ets // 模型层-音频播放管理器
| |---AVSessionModel.ets // 模型层-音频会话管理器
| |---MusicModel.ets // 模型层-音乐歌曲数据模型
| |---WindowModel.ets // 模型层-窗口管理器
|---pages
| |---MusicPlayerPage.ets // 展示层-音乐播放器
|---viewmodel
| |---MusicPlayerViewModel.ets // 控制层-音乐播放器控制器
依赖本地的utils模块
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )