回复
音乐流媒体应用开发:HarmonyOS Next实战教程 原创
wuyanghcoa
发布于 2024-11-30 13:24
浏览
0收藏
前言
在数字化时代,音乐已经成为我们生活中不可或缺的一部分。本文将带领大家探讨如何基于HarmonyOS Next开发一个仿网易云音乐APP,从音频播放的核心技术到自定义播放组件的开发,我们将重点讨论音频播放的基本界面和功能、播放原理,以及如何进行鸿蒙网络访问和歌曲列表的构建。
本文主要介绍歌曲列表的获取和歌单、排行榜的展示。
实现效果
具体实现
主页面
流程图如下:
flowchart TD
Start[初始化] --> LoadSelfInfo[加载用户信息]
LoadSelfInfo --> LoadRecommendSongs[加载推荐歌曲]
LoadRecommendSongs --> LoadArrayImage[加载推荐歌曲图片]
LoadArrayImage --> LoadTopSongs[加载热门歌曲]
LoadTopSongs --> LoadRankBlock[加载排行榜]
LoadRankBlock --> PageReady[页面准备就绪]
subgraph LoadSelfInfo
SetImageOn[设置图片开关] --> LoadUserInfo[加载用户信息]
LoadUserInfo --> LoadLikeList[加载喜欢的歌曲列表]
end
subgraph LoadRecommendSongs
RequestRecommendSongs[请求推荐歌曲] --> ParseRecommendSongs[解析推荐歌曲数据]
ParseRecommendSongs --> AddRecommendSongs[添加推荐歌曲]
end
subgraph LoadTopSongs
RequestTopSongs[请求热门歌曲] --> ParseTopSongs[解析热门歌曲数据]
ParseTopSongs --> AddTopSongs[添加热门歌曲]
end
subgraph LoadRankBlock
RequestRankList[请求排行榜列表] --> ParseRankList[解析排行榜数据]
ParseRankList --> LoadRankSongs[加载排行榜歌曲]
LoadRankSongs --> AddRankSongs[添加排行榜歌曲]
end
subgraph PageReady
ShowPage[显示页面] --> HandleTabChange[处理标签页切换]
HandleTabChange --> UpdateLikeList[更新喜欢的歌曲列表]
end
受篇幅限制,只展示包含歌曲榜单和推荐的页面部分实现
...
Navigation(this.page_info){
Tabs({barPosition:BarPosition.End,controller:this.tabs_controller}){
TabContent(){
Column(){
//搜索栏
TextInput({placeholder:'Search'}).height('7%')
.onChange((value:string)=>{
this.Search_cont = value
})
.onSubmit(()=>{
//跳转到搜索页面
const param:os = new os(this.Search_cont,this.page_info)
param.calling_back = ()=>{this.current_song = Static_Config.current_song}
const aim_info:NavPathInfo = new NavPathInfo('Search_page',param)
this.page_info.pushDestination(aim_info)
})
Column(){ //main content here
List(){
ListItem(){
Text("Hello,"+this.current_user.user_name).maxFontSize(40).minFontSize(15).width('100%').height('7%').padding(10)
.onClick(async ()=>{
let result:string[] = fileIo.listFileSync(this.fileDir)
for(let i=0;i!=result.length;i++){
console.info('delete: '+result[i])
fileIo.unlink(this.fileDir+'/'+result[i])
}
})
}.margin({top:'1.5%'})
//轮播图
ListItem() {
banner_swiper()
}
//推荐榜单
ListItem(){
List(){
ForEach(this.Rank_block_list,(item:rank_block)=>{
ListItem(){
suggest_block(item)
}.padding(5).onClick(()=>{
let dialog_controller:CustomDialogController|null = new CustomDialogController({
builder: list_dialog({
value:item,
calling_back:()=>{this.current_song = Static_Config.current_song},
}),
alignment:DialogAlignment.Bottom,
offset:{dx:0,dy:-100},
shadow:{radius:50,color:Color.Red,offsetX:30,offsetY:-30}
})
dialog_controller.open()
dialog_controller = null
})
},(item:rank_block)=>item.rank_id.toString())
}.listDirection(Axis.Horizontal).width('100%').height($r('app.string.suggest_module_height'))
.scrollBar(BarState.Off)
}
//每日推荐模块
ListItem(){
Text(this.title_daily_suggest)
.maxFontSize(30)
.minFontSize(15)
.width('100%')
.height('6%')
.padding(5)
.onClick(()=>{
for(let i=0;i!=this.recommend_songs.length;i++){
const now:Song = this.recommend_songs[i]
fileIo.unlink(this.fileDir+'/'+now.id.toString()+'.jpg')
}
})
}
.margin({top:'2'})
ListItem(){
Text()
.width('100%')
.height(1)
.borderWidth(1)
.borderColor(Configuration.text_underline_color)
}
if(this.recommend_finish){
ListItem(){
List(){
ForEach(this.recommend_songs,(item:Song)=>{
ListItem(){
song_item({song:item}).padding(2)
.onClick(()=>{
this.switch_and_play(item)
})
}.width('80%')
},(item:Song)=>item.id.toString())
}.listDirection(Axis.Horizontal).lanes(3).height('20%').scrollBar(BarState.Off)
}
}
//英文热门歌曲
ListItem(){
Text(this.title_new_E_song).maxFontSize(30).minFontSize(15).width('100%').height('6%').padding(5)
.onClick(()=>{
player.Start_play('https://music.163.com/song/media/outer/url?id=2046938156.mp3')
})
}.margin({top:'2'})
ListItem(){
Text()
.width('100%')
.height(1)
.borderWidth(1)
.borderColor(Configuration.text_underline_color)
}
ListItem(){
List(){
ForEach(this.Top_english_songs,(item:Song)=>{
ListItem(){
song_item({song:item}).padding(2)
.onClick(()=>{
this.switch_and_play(item)
})
}.width('80%')
},(item:Song)=>item.id.toString())
}.listDirection(Axis.Horizontal).lanes(3).height('20%').scrollBar(BarState.Off)
}
//New Chinese song
ListItem(){
Text(this.title_new_C_song)
.maxFontSize(30)
.minFontSize(15)
.width('100%')
.height('6%')
.padding(5)
}.margin({top:'2'})
ListItem(){
Text()
.width('100%')
.height(1)
.borderWidth(1)
.borderColor(Configuration.text_underline_color)
}
ListItem(){
List(){
ForEach(this.Top_Chinese_songs,(item:Song)=>{
ListItem(){
song_item({song:item}).padding(2)
.onClick(()=>{
this.switch_and_play(item)
})
}.width('80%')
},(item:Song)=>item.id.toString())
}.listDirection(Axis.Horizontal).lanes(3).height('20%').scrollBar(BarState.Off)
}
//最近常听:Not finish yet
ListItem(){
Text(this.Usually_module).maxFontSize(30).minFontSize(15).width('100%').height('6%').padding(5)
}.margin({top:'8'})
/*ListItem(){
List(){
ForEach(this.Suggest_song_list,(id:string)=>{
ListItem(){
song_item({shape:1})
.padding(2)
}.width($r('app.string.song_block_square_size')).height($r('app.string.song_block_square_size'))
},(id:string)=>id)
}.listDirection(Axis.Horizontal).scrollBar(BarState.Off)
}*/
}
}.height('86%').width('100%')
//Play column
play_column({song_item:this.current_song}).height(Size_Data.play_column_size).width('100%').padding(2)
.onClick(()=>{
const param = new os(this.Search_cont,this.page_info) as object
const aim_info = new NavPathInfo('Song_page',param)
this.page_info.pushDestination(aim_info)
// this.page_info.pushPath({name:'Search_page',param:new os(this.Search_cont)})
})
}.width('100%')
}.tabBar(this.tab_bar_builder('Main',0,$r('app.media.Main'),$r('app.media.Main_Chosen')))
...
排行榜组件
实现效果:
具体实现:
import {song_block,song_item} from '../Build/song_build'
import {suggest_block} from '../Build/Suggest_build'
import {play_column} from '../Build/Play_column'
import {Introduction_block as Ib,user_info} from '../Build/Self_page'
import {Song,Static_Config,Page_transmission as os} from '../Class_def/song_def'
import {Size_Data} from '../Common/Constant/Size_data'
import {Order} from '../Common/Constant/Order'
import http from "@ohos.net.http"
//存储排行榜数据,每个block对应一个排行榜
@Observed
export class rank_block{
rank_id:number|string
rank_name:string
rank_des:string
rank_songs:Array<Song> = []
constructor(_id?:number|string,_name?:string,_des?:string) {
this.rank_id = _id==undefined?-1:_id
this.rank_name = _name==undefined?"":_name
this.rank_des = _des==undefined?"":_des
}
get_rank_songs(){
let http_request = http.createHttp()
let response = http_request.request(
Order.url + Order.play_list_all + this.rank_id.toString()
);
response.then((data)=> {
if (data.responseCode == 200) {
let res = data.result as string
let json_res = JSON.parse(res) as object
let songs = (json_res as object)?.["songs"] as Array<object>
for(let i=0;i!=(songs as Array<object>).length && i!=10;i++){
let song_info = (songs as Array<object>)[i] as object
let song_id:string = (song_info as object)?.["id"]
let song_name = (song_info as object)?.["name"] as string
let ar = (song_info as object)?.["ar"] as Array<object>
let aim_ar = (ar as Array<object>)[0] as object
let singer_id:string = (aim_ar as object)?.["id"]
let singer_name = (aim_ar as object)?.["name"] as string
let image = ((song_info as object)?.["al"] as object)?.["picUrl"] as string
this.rank_songs.push(new Song(song_id,song_name,singer_id,singer_name,image))
console.info("debug",song_name,this.rank_songs.length,this.rank_songs[i].image)
}
}
})
}
load_songs(data:object){
this.rank_id = (data as object)?.["id"] as string
this.rank_name = (data as object)?.["name"] as string
this.rank_des = (data as object)?.["description"] as string
console.info("debug init finished: ",this.rank_id.toString())
}
push_new_song(new_song:Song){
this.rank_songs.push(new_song)
}
song_num():number {
return this.rank_songs.length
}
}
推荐歌单组件
实现效果:
具体实现:
import {song_block,song_item} from '../Build/song_build'
import {suggest_block} from '../Build/Suggest_build'
import {play_column} from '../Build/Play_column'
import {Introduction_block as Ib,user_info} from '../Build/Self_page'
import {Song,Static_Config,Page_transmission as os} from '../Class_def/song_def'
import {Size_Data} from '../Common/Constant/Size_data'
import {Order} from '../Common/Constant/Order'
import {rank_block} from '../Class_def/Ranking_list'
import { webview } from '@kit.ArkWeb'
import player from '../Api/AVPlayer'
@CustomDialog
@Component
export struct web_dialog{
dialog_controller?:CustomDialogController
web_controller:webview.WebviewController = new webview.WebviewController()
singer_id:string = ""
build() {
Column(){
Web({ src:Order.Get_singer_web(this.singer_id),
controller:this.web_controller,
renderMode:RenderMode.ASYNC_RENDER})
.width('100%')
.height('100%')
.onlineImageAccess(true)
}
.width('100%')
.height(Size_Data.dialog_height)
}
}
@CustomDialog
@Component
export struct list_dialog{
dialog_controller?:CustomDialogController
@Prop value:rank_block
Position:string = '/data/storage/el2/base/haps/entry/files/'
calling_back = ()=>{}
build() {
Column(){
Stack(){
//Image(fileUri.getUriFromPath(this.Position+this.value.rank_songs[0].id.toString()+'.jpg'))
Image(this.value.rank_songs[0].image)
.position({top:15,left:10})
.clip(true)
.width(Size_Data.dialog_cover_size)
.height(Size_Data.dialog_cover_size)
.autoResize(true)
Column(){
//排行榜单名称
Text(this.value.rank_name)
.width('100%')
.height('50%')
.textAlign(TextAlign.Start)
.maxLines(1)
.maxFontSize(25)
.minFontSize(15)
.margin({left:5})
//排行榜单介绍
Text(this.value.rank_des)
.width('100%')
.height('50%')
.textAlign(TextAlign.Start)
.fontSize(15)
.fontColor(Color.Gray)
.maxLines(3)
.textOverflow({overflow:TextOverflow.Ellipsis})
.margin({left:5})
}
.width('70%')
.height(Size_Data.dialog_title_height)
.position({top:5,right:5})
}
.width('100%')
Blank().width('100%').height(30)
List(){
ForEach(this.value.rank_songs,(songs:Song)=>{
ListItem(){
song_item({song:songs,shape:0})
.onClick(()=>{
Static_Config.current_song = songs
player.player_reset(Order.Get_song_url(songs.id.toString()))
this.calling_back()
})
}
.width('100%')
.height($r('app.string.song_block_height'))
},(songs:Song)=>songs.id.toString())
}
.width('100%')
.height('60%')
.scrollBar(BarState.Off)
}
.width('100%')
.height(Size_Data.dialog_height)
.linearGradient({direction:GradientDirection.Top,colors:[[Color.Red,0.0],[Color.White,0.5]]})
.renderGroup(true)
}
}
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
已于2024-12-3 15:44:06修改
赞
收藏
回复
相关推荐