目的是要制作一个这样的音乐播放控制面板:
水平布局显示:
几个按钮前几篇文章已经绘制过了,直接给出该组件代码:
export component ControlPanel inherits Window {
in-out property <float> progress;
in property <float> duration;
in property <bool> paused;
in property <PlayMode> play_mode;
in-out property <bool> dragging;
in property <SongInfo> current_song;
in property <image> album_image;
callback change-progress(float);
callback toggle-play();
callback play-next();
callback play-prev();
callback switch-mode(PlayMode);
callback double-clicked();
pure callback format-duration(float) -> string;
VerticalLayout {
width: 100%;
height: 100%;
Slider {
minimum: 0;
maximum: root.duration;
// 单向绑定被用户强制改变状态后,绑定失效,不再自动更新, 所以双向绑定
value <=> root.progress;
released(v) => {
root.dragging = false;
change_progress(v);
}
changed(_) => {
root.dragging = true;
}
}
HorizontalLayout {
height: 80%;
alignment: center;
TouchArea {
width: 35%;
height: 100%;
double-clicked => {
root.double-clicked()
};
HorizontalLayout {
alignment: start;
spacing: 3px;
Rectangle {
width: 20%;
height: 100%;
Rectangle {
width: 40px;
height: 40px;
border-radius: 4px;
clip: true;
drop-shadow-blur: 6px;
Image {
y: parent.height / 2 - self.height / 2;
width: 100%;
height: 100%;
source: root.album_image;
image-fit: contain;
}
}
}
Rectangle {
width: 80%;
height: 100%;
Text {
width: 100%;
x: parent.width / 2 - self.width / 2;
text: root.current_song.song_name + " - " + root.current_song.singer;
vertical-alignment: center;
horizontal-alignment: center;
overflow: TextOverflow.elide;
}
}
}
}
Rectangle {
width: 6%;
OverlapButton {
x: parent.width / 2 - self.width / 2;
y: parent.height / 2 - self.height / 2;
width: 20px;
height: 20px;
mode: root.play_mode;
clicked => {
if (root.play_mode == PlayMode.InOrder) {
root.switch_mode(PlayMode.Recursive);
} else {
root.switch_mode(PlayMode.InOrder);
}
}
}
}
Rectangle {
width: 6%;
PrevSongButton {
x: parent.width / 2 - self.width / 2;
y: parent.height / 2 - self.height / 2;
width: 20px;
height: 20px;
clicked => {
root.play_prev();
}
}
}
Rectangle {
width: 6%;
PlayPauseButton {
x: parent.width / 2 - self.width / 2;
y: parent.height / 2 - self.height / 2;
width: 20px;
height: 20px;
// 单向绑定被用户强制改变状态后,绑定失效,不再自动更新, 所以双向绑定
paused: root.paused;
toggled => {
root.toggle_play();
}
}
}
Rectangle {
width: 6%;
NextSongButton {
x: parent.width / 2 - self.width / 2;
y: parent.height / 2 - self.height / 2;
width: 20px;
height: 20px;
clicked => {
root.play_next();
}
}
}
Rectangle {
width: 6%;
border-color: transparent;
RandomButton {
x: parent.width / 2 - self.width / 2;
y: parent.height / 2 - self.height / 2;
selected: root.play_mode == PlayMode.Random;
width: 20px;
height: 20px;
clicked => {
root.switch_mode(PlayMode.Random);
}
}
}
Rectangle {
width: 35%;
TouchArea {
double-clicked => {
root.double-clicked()
}
}
Text {
x: parent.width / 2 - self.width / 2;
text: root.format-duration(root.progress) + " / " + root.format-duration(root.duration);
vertical-alignment: center;
horizontal-alignment: center;
}
}
}
}
}
实现思路很直接,点击每个按钮会触发一个回调函数,对应一个功能。
其中,进度条拖动来改变播放进度的实现比较特殊,因为进度条一方面是根据音乐播放进度从后台用定时器定时更新的,另一方面又要支持用户手动拖动来改变播放进度,为了防止用户拖动时被后台定时更新覆盖,这里使用一个额外变量dragging
来记录用户是否在拖动,设置当后台检测到dragging
为true
时,不再更新进度条。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。