1.引言
在短视频火爆的今天,“刷”成为了老少皆宜的一项消遣娱乐的活动。那音乐能不能刷呢,答案当然是可以的。汽水音乐就实现了这个想法。汽水音乐也是抖音推出的一款音乐软件,因为我平时比较喜欢使用这款软件,这也是我做这个项目主要灵感来源。
2.项目背景
自从短视频平台成为音乐作品宣发的重要阵地,便时常有字节跳动进军音乐圈的声音出现。 2021年12月,据tech星球报道,字节正在内部测试国内首款音乐App汽水音乐,而产品主体研发在当年9月已经完成。
2022年2月,汽水音乐在毫无预兆的情况下闪电上线小米应用市场,一周后又出现在苹果商店。 而此时,字节内部员工都还有大量没收到内测码,更遑论外部人员。“措手不及”的突发上线,直接导致汽水音乐在苹果应用商店的评分跌至2.3。 这对于新上线的APP来说,简直是无妄之灾。当时也有说法,这可能与其内部绩效考核时间有关。 无论如何,在几经波折后的2022年6月,汽水音乐在更新到1.6版本时,终于开启公测阶段。 黑色为底,荧光绿的音符动感跳跃,由抖音延续而来的气质,从其logo就展露无遗。 整体而言,汽水音乐的架构与Resso相似。不过,汽水音乐从正式上线到如今的5.6版本,虽然外部声量上有所回落,但确实也有不少值得细品的内容。 一方面,我们诧异于汽水音乐版本迭代之快。要知道大部分产品大版本一年才会更新一次,汽水音乐短短一年已经更新到5.6版本,这是其他产品至少三五年的工作量。 另一方面,我们也发现汽水音乐还是有一些明显的改动的。其中,最重要的,当属音乐视觉化的弱化。 一般认为,汽水音乐会将抖音“看音乐”的优势延续下来。 确实,在汽水音乐刚上线时,对音乐视觉化呈现是产品的主打特色之一,开屏即可见竖版的音乐视频,如抖音一般上下滑动切歌。
总的来说,汽水音乐是在我看来比较不错的软件,也许是受抖音的影响,一段剪辑的短视频,配上一段动感的BGM,给歌曲的本身加了分。对于我而言,刷音乐可能更加适合我这种平常不喜欢搜索新的音乐去听的人。抖音的推荐算法是非常成熟的,应用到音乐软件上也是如此。
3.项目框架
本项目实现的主要功能有
- 登陆
- 首页
- 每日推荐
- 歌单漫游
- 视频MV
- 我的收藏
- 创建歌单
- 本地音乐
- 最近播放
(1)HomePage
主要代码:
import router from '@ohos.router';
import { CommonConstants } from '../common/constants/CommonConstants';
import { DetailListComponent } from '../view/DetailListComponent';
@Component
struct HomePage {
private titleParam: Resource = $r('app.string.detail_default_title');
aboutToAppear() {
if (router.getParams()) {
this.titleParam = router.getParams()[CommonConstants.KEY_PARAM_DATA] as Resource;
}
}
build() {
Column() {
Image($r('app.media.logo'))
.width('62')
.height('62')
.borderRadius('32')
.margin({top:134})
Column(){
Button('抖音登录')
.width(282)
.height(38)
.borderRadius(50)
.fontColor('#778899')
.backgroundColor('#fff')
Button('立即体验')
.width(282)
.height(38)
.borderRadius(50)
.fontColor('#fff')
.backgroundColor('transparent')
.border({
width:1,
color:"#fff"
})
.margin({
top:25
})
.onClick(()=>{
router.pushUrl({
url: CommonConstants.INDEX_PAGE
});
console.log('ok')
})
}
.justifyContent(FlexAlign.Center)
.width('100%')
.position({
x:0,
y:'80%'
})
}
.width('100%')
.height(CommonConstants.DETAIL_COLUMN_HEIGHT_PERCENT)
.backgroundColor($r('app.color.theme_background'))
}
}
(2)IndexPage
主要代码:
import router from '@ohos.router';
import { CommonConstants } from '../common/constants/CommonConstants';
import { DetailListComponent } from '../view/DetailListComponent';
@Entry
@Component
struct IndexPage {
private titleParam: Resource = $r('app.string.detail_default_title');
private controller: TabsController = new TabsController()
private swiperController: SwiperController = new SwiperController()
@State currentIndex: number = 0
aboutToAppear() {
if (router.getParams()) {
this.titleParam = router.getParams()[CommonConstants.KEY_PARAM_DATA] as Resource;
}
}
@Builder TabTextBuilder(text: string,index:number) {
Text(text)
.fontSize(this.currentIndex===index?18:16)
.fontColor(this.currentIndex===index?'#F8F8FF':'#888')
.fontWeight(this.currentIndex===index?800:400)
}
@Builder TabMyContent(){
Scroll(){
Column(){
Column() {
Image($r('app.media.logo'))
.width(50)
.height(50)
.margin({ top: 100 })
Text('JW').fontSize(14).fontSize('#F8F8FF')
}
Column(){
Row(){
Image($r('app.media.music_icon'))
.width(21)
.height(25)
Text('本地音乐')
.fontSize(16)
.fontColor('#F8F8FF')
.margin({left:8})
}
.width('100%')
.height(47)
.alignItems(VerticalAlign.Center)
.border({width:{bottom:1},color:'#dcdcdc',})
.padding({left:13})
Row(){
Image($r('app.media.music_icon'))
.width(21)
.height(25)
Text('最近播放')
.fontSize(16)
.fontColor('#F8F8FF')
.margin({left:8})
}
.width('100%')
.height(47)
.alignItems(VerticalAlign.Center)
.border({width:{bottom:1},color:'#dcdcdc',})
.padding({left:13})
Row(){
Image($r('app.media.music_icon'))
.width(21)
.height(25)
Text('下载管理')
.fontSize(16)
.fontColor('#F8F8FF')
.margin({left:8})
}
.width('100%')
.height(47)
.alignItems(VerticalAlign.Center)
.border({width:{bottom:1},color:'#dcdcdc',})
.padding({left:13})
Row(){
Image($r('app.media.music_icon'))
.width(21)
.height(25)
Text('我的收藏')
.fontSize(16)
.fontColor('#F8F8FF')
.margin({left:8})
}
.width('100%')
.height(47)
.alignItems(VerticalAlign.Center)
.border({width:{bottom:1},color:'#FF182431',})
.padding({left:13})
}
Divider()
.strokeWidth(9)
.color('#FF182431')
Column(){
Flex({ direction: FlexDirection.Row,justifyContent:FlexAlign.SpaceBetween,alignItems:ItemAlign.Center }){
Text('创建的歌单')
.fontSize(16)
.fontColor('#F8F8FF')
.fontWeight(800)
Text('+创建')
.padding({top:2,right:6,bottom:2,left:2})
.border({width:1,color:'#ccc'})
.borderRadius(18)
.fontSize(10)
.fontColor('#F8F8FF')
}
Row(){
Image('http://p1.music.126.net/mENtBPo_dYhMgsv0_3ae9A==/109951165448009459.jpg')
.width(56)
.height(56)
.margin({right:12})
Column(){
Text('JW000喜欢的音乐')
.fontSize(14)
.fontColor('#F8F8FF')
Text('14首')
.fontSize(12)
.fontColor('#666')
.margin({top:4})
}
.alignItems(HorizontalAlign.Start)
}
Row(){
Image($r('app.media.douyin'))
.width(56)
.height(56)
.margin({right:12})
Column(){
Text('抖音收藏的音乐')
.fontSize(14)
.fontColor('#F8F8FF')
Text('14首')
.fontSize(12)
.fontColor('#666')
.margin({top:4})
}
.alignItems(HorizontalAlign.Start)
}
}
.width('100%')
.padding({left:13,top:6})
.alignItems(HorizontalAlign.Start)
.justifyContent(FlexAlign.Start)
}
}
}
build() {
Column() {
Tabs({barPosition: BarPosition.End,index:this.currentIndex,controller: this.controller }) {
TabContent() {
Scroll(){
Column(){
Swiper(this.swiperController) {
Image($r('app.media.8'))
.width(349)
.height(145)
Image($r('app.media.9'))
.width(349)
.height(145)
Image($r('app.media.9'))
.width(349)
.height(145)
}
.width(349)
.height(145)
.loop(true)
.autoPlay(true)
Column(){
Flex({ direction: FlexDirection.Row,justifyContent:FlexAlign.SpaceBetween,alignItems:ItemAlign.Center }){
Text('推荐歌单')
.fontSize(16)
.fontColor('#F8F8FF')
.fontWeight(800)
Text('歌单广场')
.padding({top:2,right:6,bottom:2,left:2})
.border({width:1,color:'#ccc'})
.borderRadius(18)
.fontSize(10)
.fontColor('#F8F8FF')
}
.padding({left:9,right:9})
Flex({ direction: FlexDirection.Row,wrap:FlexWrap.Wrap,justifyContent:FlexAlign.SpaceAround }){
Column(){
Image($r('app.media.1'))
.width(107)
.margin({bottom:4})
Text('每日尝鲜三十首').fontSize(12).fontColor('#F8F8FF')
}
.width(107)
.margin({bottom:13})
Column(){
Image($r('app.media.2'))
.width(107)
.margin({bottom:4})
Text('欧美励志燃曲:与宿命的顶级拉扯').fontSize(12).fontColor('#F8F8FF')
}
.width(107)
.margin({bottom:13})
Column(){
Image($r('app.media.3'))
.width(107)
.margin({bottom:4})
Text('国产高质量hiphop,听就完事了').fontSize(12).fontColor('#F8F8FF')
}
.width(107)
.margin({bottom:13})
Column(){
Image($r('app.media.1'))
.width(107)
.margin({bottom:4})
Text('每日尝鲜三十首').fontSize(12).fontColor('#F8F8FF')
}
.width(107)
.margin({bottom:13})
Column(){
Image($r('app.media.2'))
.width(107)
.margin({bottom:4})
Text('欧美励志燃曲:与宿命的顶级拉扯').fontSize(12).fontColor('#F8F8FF')
}
.width(107)
.margin({bottom:13})
Column(){
Image($r('app.media.3'))
.width(107)
.margin({bottom:4})
Text('国产高质量hiphop,听就完事了').fontSize(12).fontColor('#F8F8FF')
}
.width(107)
.margin({bottom:13})
}
.margin({top:13})
}
Column(){
Flex({ direction: FlexDirection.Row,justifyContent:FlexAlign.SpaceBetween,alignItems:ItemAlign.Center }){
Text('歌单漫游')
.fontSize(16)
.fontColor('#F8F8FF')
.fontWeight(800)
Text('更多歌单')
.padding({top:2,right:6,bottom:2,left:2})
.border({width:1,color:'#ccc'})
.borderRadius(18)
.fontSize(10)
.fontColor('#F8F8FF')
}
.padding({left:9,right:9})
Flex({ direction: FlexDirection.Row,wrap:FlexWrap.Wrap,justifyContent:FlexAlign.SpaceAround }){
Column(){
Image($r('app.media.4'))
.width(107)
.margin({bottom:4})
Text('欢迎来到0.8倍宿的世界').fontSize(12).fontColor('#F8F8FF')
}
.width(107)
.margin({bottom:13})
Column(){
Image($r('app.media.6'))
.width(107)
.margin({bottom:4})
Text('动感英文|提神醒脑就靠它').fontSize(12).fontColor('#F8F8FF')
}
.width(107)
.margin({bottom:13})
Column(){
Image($r('app.media.5'))
.width(107)
.margin({bottom:4})
Text('欧美说唱|醒脑提神,单曲循环就兴奋').fontSize(12).fontColor('#F8F8FF')
}
.width(107)
.margin({bottom:13})
}
.margin({top:13})
}
Column(){
Flex({ direction: FlexDirection.Row,justifyContent:FlexAlign.SpaceBetween,alignItems:ItemAlign.Center }){
Text('最爱情歌')
.fontSize(16)
.fontColor('#F8F8FF')
.fontWeight(800)
Text('查看更多')
.padding({top:2,right:6,bottom:2,left:2})
.border({width:1,color:'#ccc'})
.borderRadius(18)
.fontSize(10)
.fontColor('#F8F8FF')
}
.padding({left:9,right:9})
Column(){
Image($r('app.media.7'))
.width(337)
.height(213)
.margin({top:9,bottom:4})
.borderRadius(12)
Text('HIPHOP')
.fontSize(16)
.fontColor('#F8F8FF')
.fontWeight(800)
.height(42)
.margin({left:12})
}
.borderRadius({topLeft:12,topRight:12})
.backgroundColor('#efefef')
.alignItems(HorizontalAlign.Start)
.width(337)
.margin({bottom:13})
Flex({ direction: FlexDirection.Row,justifyContent:FlexAlign.SpaceBetween,alignItems:ItemAlign.Center }){
Row(){
Image($r('app.media.like'))
.width(17)
.height(20)
Text('521')
.fontSize(16)
.fontColor('#F8F8FF')
.margin({left:2,right:4})
Image($r('app.media.comment'))
.width(17)
.height(20)
Text('520')
.fontSize(16)
.fontColor('#F8F8FF')
.margin({left:2})
}
.alignItems(VerticalAlign.Center)
}
.padding({left:20,right:20})
}
}
}
}
.height('100%')
.tabBar(this.TabTextBuilder('发现',0))
TabContent() {
Text('音乐').fontSize(35).fontColor('#888')
} .backgroundImage($r('app.media.yinyue'))
.padding(10)
.tabBar(this.TabTextBuilder('音乐',1))
TabContent() {
this.TabMyContent()
}.backgroundColor($r('app.color.theme_background'))
.tabBar(this.TabTextBuilder('我的',2))
.align(Alignment.TopStart)
}
.onChange((index:number)=>{
this.currentIndex=index
})
.barWidth(286)
.barHeight(45)
// .padding({left:32,right:32})
.width('100%')
.height('100%')
.scrollable(true)
.backgroundColor($r('app.color.theme_background'))
}
.justifyContent(FlexAlign.Start)
}
}
4.项目总结
本项目还只是处于开发的初级阶段,主要还是用前端实现的,还有很大的不足和需要改进的地方。鸿蒙系统作为咱们第一个国产操作系统,其发展的艰辛可想而知,总的来说,国产操作系统这条路任重而道远,希望有更多的同学能投入到这里面来,为国产操作系统贡献自己的一份力量。