下面我们将通过一个简单的音乐播放器示例,展示鸿蒙音乐应用开发的基本流程。

项目结构

MusicPlayer/
├── entry/
│   └── src/
│       ├── main/
│       │   ├── java/com/example/musicplayer/
│       │   │   ├── MainAbility.java
│       │   │   ├── MusicService.java
│       │   │   └── MusicPlayer.java
│       │   └── resources/
│       │       ├── base/layout/ability_main.xml
│       │       └── base/media/ (存放音乐文件)

布局文件 (ability_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:orientation="vertical"
    ohos:padding="32">

    <Text
        ohos:id="$+id:music_title"
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:text="鸿蒙音乐播放器"
        ohos:text_size="32fp"
        ohos:text_alignment="center"
        ohos:margin="0 0 0 20"/>

    <Text
        ohos:id="$+id:current_song"
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:text="未选择歌曲"
        ohos:text_size="24fp"
        ohos:text_alignment="center"
        ohos:margin="0 0 0 10"/>

    <Slider
        ohos:id="$+id:progress_slider"
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:max_value="100"
        ohos:min_value="0"
        ohos:margin="0 0 0 20"/>

    <Text
        ohos:id="$+id:time_display"
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:text="00:00 / 00:00"
        ohos:text_size="18fp"
        ohos:text_alignment="center"/>

    <DirectionalLayout
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:orientation="horizontal"
        ohos:top_margin="40">

        <Button
            ohos:id="$+id:prev_btn"
            ohos:height="match_content"
            ohos:width="120"
            ohos:text="上一首"
            ohos:background_element="$graphic:button_element"
            ohos:layout_alignment="center"
            ohos:margin="0 10 0 0"/>

        <Button
            ohos:id="$+id:play_btn"
            ohos:height="match_content"
            ohos:width="120"
            ohos:text="播放"
            ohos:background_element="$graphic:button_element"
            ohos:layout_alignment="center"
            ohos:margin="0 10 0 10"/>

        <Button
            ohos:id="$+id:next_btn"
            ohos:height="match_content"
            ohos:width="120"
            ohos:text="下一首"
            ohos:background_element="$graphic:button_element"
            ohos:layout_alignment="center"
            ohos:margin="0 0 0 10"/>
    </DirectionalLayout>

    <ListContainer
        ohos:id="$+id:song_list"
        ohos:height="300"
        ohos:width="match_parent"
        ohos:top_margin="40"
        ohos:bottom_margin="40"/>

</DirectionalLayout>

音乐播放器核心类 (MusicPlayer.java)

package com.example.musicplayer;

import ohos.media.player.Player;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.media.common.Source;
import ohos.app.Context;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class MusicPlayer {
    private Player mediaPlayer;
    private List<Song> songList;
    private int currentSongIndex = 0;
    private boolean isPlaying = false;
    private Timer progressTimer;
    private Slider progressSlider;
    private Text timeDisplay;
    private Text currentSongText;
    private Button playButton;
    
    public MusicPlayer(Context context) {
        mediaPlayer = new Player(context);
        initializeSongList();
        setupMediaPlayerListeners();
    }
    
    private void initializeSongList() {
        songList = new ArrayList<>();
        // 添加示例歌曲
        songList.add(new Song("鸿蒙之歌", "artist1", "resource://rawfile/song1.mp3"));
        songList.add(new Song("分布式交响曲", "artist2", "resource://rawfile/song2.mp3"));
        songList.add(new Song("万物互联旋律", "artist3", "resource://rawfile/song3.mp3"));
    }
    
    private void setupMediaPlayerListeners() {
        mediaPlayer.setPlayerCallback(new Player.IPlayerCallback() {
            @Override
            public void onPrepared() {
                // 准备完成
                if (progressSlider != null) {
                    progressSlider.setMaxValue(mediaPlayer.getDuration());
                }
                updateTimeDisplay();
            }
            
            @Override
            public void onMessage(int i, int i1) {
                // 处理消息
            }
            
            @Override
            public void onError(int i, int i1) {
                // 错误处理
                HiLog.error(LABEL, "播放器错误: %{public}d", i1);
            }
            
            @Override
            public void onResolutionChanged(int i, int i1) {
                // 分辨率改变
            }
            
            @Override
            public void onPlayBackComplete() {
                // 播放完成
                playNext();
            }
            
            @Override
            public void onRewindToComplete() {
                // 重定向完成
            }
            
            @Override
            public void onBufferingChange(int i) {
                // 缓冲进度改变
            }
            
            @Override
            public void onNewTimedMetaData(Player.MediaTimedMetaData mediaTimedMetaData) {
                // 新的定时元数据
            }
            
            @Override
            public void onMediaTimeIncontinuity(Player.MediaTimeInfo mediaTimeInfo) {
                // 媒体时间不连续
            }
        });
    }
    
    public void setUIComponents(Slider slider, Text timeText, Text songText, Button playBtn) {
        this.progressSlider = slider;
        this.timeDisplay = timeText;
        this.currentSongText = songText;
        this.playButton = playButton;
        
        // 设置进度条监听
        progressSlider.setValueChangedListener(new Slider.ValueChangedListener() {
            @Override
            public void onProgressUpdated(Slider slider, int progress, boolean fromUser) {
                if (fromUser && mediaPlayer != null && mediaPlayer.isNowPlaying()) {
                    mediaPlayer.rewindTo(progress);
                }
            }
            
            @Override
            public void onTouchStart(Slider slider) {
                // 触摸开始
            }
            
            @Override
            public void onTouchEnd(Slider slider) {
                // 触摸结束
            }
        });
    }
    
    public void playSong(int index) {
        if (index < 0 || index >= songList.size()) {
            return;
        }
        
        try {
            if (mediaPlayer.isNowPlaying()) {
                mediaPlayer.stop();
            }
            
            mediaPlayer.reset();
            Song song = songList.get(index);
            Source source = new Source(song.getPath());
            mediaPlayer.setSource(source);
            mediaPlayer.prepare();
            mediaPlayer.play();
            
            currentSongIndex = index;
            isPlaying = true;
            
            if (currentSongText != null) {
                currentSongText.setText(song.getTitle() + " - " + song.getArtist());
            }
            
            if (playButton != null) {
                playButton.setText("暂停");
            }
            
            startProgressTimer();
            
        } catch (IOException e) {
            HiLog.error(LABEL, "播放歌曲失败: %{public}s", e.getMessage());
        }
    }
    
    public void play() {
        if (mediaPlayer != null && !mediaPlayer.isNowPlaying()) {
            mediaPlayer.play();
            isPlaying = true;
            if (playButton != null) {
                playButton.setText("暂停");
            }
            startProgressTimer();
        }
    }
    
    public void pause() {
        if (mediaPlayer != null && mediaPlayer.isNowPlaying()) {
            mediaPlayer.pause();
            isPlaying = false;
            if (playButton != null) {
                playButton.setText("播放");
            }
            stopProgressTimer();
        }
    }
    
    public void playNext() {
        int nextIndex = (currentSongIndex + 1) % songList.size();
        playSong(nextIndex);
    }
    
    public void playPrevious() {
        int prevIndex = (currentSongIndex - 1 + songList.size()) % songList.size();
        playSong(prevIndex);
    }
    
    private void startProgressTimer() {
        stopProgressTimer();
        progressTimer = new Timer();
        progressTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                updateProgress();
            }
        }, 0, 1000);
    }
    
    private void stopProgressTimer() {
        if (progressTimer != null) {
            progressTimer.cancel();
            progressTimer = null;
        }
    }
    
    private void updateProgress() {
        if (mediaPlayer != null && mediaPlayer.isNowPlaying()) {
            int currentPosition = mediaPlayer.getCurrentTime();
            int duration = mediaPlayer.getDuration();
            
            if (progressSlider != null) {
                progressSlider.setProgressValue(currentPosition);
            }
            
            updateTimeDisplay();
        }
    }
    
    private void updateTimeDisplay() {
        if (timeDisplay != null && mediaPlayer != null) {
            int current = mediaPlayer.getCurrentTime();
            int total = mediaPlayer.getDuration();
            
            String currentTime = formatTime(current);
            String totalTime = formatTime(total);
            
            timeDisplay.setText(currentTime + " / " + totalTime);
        }
    }
    
    private String formatTime(int milliseconds) {
        int seconds = milliseconds / 1000;
        int minutes = seconds / 60;
        seconds = seconds % 60;
        return String.format("%02d:%02d", minutes, seconds);
    }
    
    public void release() {
        stopProgressTimer();
        if (mediaPlayer != null) {
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }
    
    public List<Song> getSongList() {
        return songList;
    }
    
    public boolean isPlaying() {
        return isPlaying;
    }
    
    // 歌曲数据模型
    public static class Song {
        private String title;
        private String artist;
        private String path;
        
        public Song(String title, String artist, String path) {
            this.title = title;
            this.artist = artist;
            this.path = path;
        }
        
        public String getTitle() { return title; }
        public String getArtist() { return artist; }
        public String getPath() { return path; }
    }
}

主Ability实现 (MainAbility.java)

package com.example.musicplayer;

import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.window.service.WindowManager;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.util.ArrayList;
import java.util.List;

public class MainAbility extends Ability {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MainAbility");
    
    private MusicPlayer musicPlayer;
    private ListContainer songListContainer;
    private ArrayAdapter<MusicPlayer.Song> songAdapter;
    
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        // 保持屏幕常亮
        getWindow().addFlags(WindowManager.LayoutConfig.MARK_KEEP_SCREEN_ON);
        
        super.setUIContent(ResourceTable.Layout_ability_main);
        
        initializeMusicPlayer();
        setupUIComponents();
        setupSongList();
    }
    
    private void initializeMusicPlayer() {
        musicPlayer = new MusicPlayer(this);
    }
    
    private void setupUIComponents() {
        // 获取UI组件
        Slider progressSlider = (Slider) findComponentById(ResourceTable.Id_progress_slider);
        Text timeDisplay = (Text) findComponentById(ResourceTable.Id_time_display);
        Text currentSongText = (Text) findComponentById(ResourceTable.Id_current_song);
        Button playButton = (Button) findComponentById(ResourceTable.Id_play_btn);
        Button prevButton = (Button) findComponentById(ResourceTable.Id_prev_btn);
        Button nextButton = (Button) findComponentById(ResourceTable.Id_next_btn);
        songListContainer = (ListContainer) findComponentById(ResourceTable.Id_song_list);
        
        // 设置音乐播放器UI组件
        musicPlayer.setUIComponents(progressSlider, timeDisplay, currentSongText, playButton);
        
        // 设置按钮点击事件
        playButton.setClickedListener(component -> togglePlayPause());
        prevButton.setClickedListener(component -> musicPlayer.playPrevious());
        nextButton.setClickedListener(component -> musicPlayer.playNext());
    }
    
    private void togglePlayPause() {
        if (musicPlayer.isPlaying()) {
            musicPlayer.pause();
        } else {
            if (musicPlayer.getSongList().isEmpty()) {
                // 如果没有歌曲在播放,播放第一首
                musicPlayer.playSong(0);
            } else {
                musicPlayer.play();
            }
        }
    }
    
    private void setupSongList() {
        List<MusicPlayer.Song> songs = musicPlayer.getSongList();
        songAdapter = new ArrayAdapter<MusicPlayer.Song>(this, 
            ResourceTable.Layout_song_list_item, songs) {
            @Override
            public void onItemChanged(Component component, MusicPlayer.Song song, int position) {
                Text songTitle = (Text) component.findComponentById(ResourceTable.Id_song_title);
                Text songArtist = (Text) component.findComponentById(ResourceTable.Id_song_artist);
                
                if (songTitle != null) {
                    songTitle.setText(song.getTitle());
                }
                if (songArtist != null) {
                    songArtist.setText(song.getArtist());
                }
                
                // 设置列表项点击事件
                component.setClickedListener(comp -> {
                    musicPlayer.playSong(position);
                });
            }
        };
        
        songListContainer.setItemProvider(songAdapter);
    }
    
    @Override
    protected void onBackground() {
        super.onBackground();
        // 应用进入后台时,可以继续播放音乐
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        // 释放资源
        if (musicPlayer != null) {
            musicPlayer.release();
        }
    }
}

歌曲列表项布局 (song_list_item.xml)

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="60vp"
    ohos:width="match_parent"
    ohos:orientation="vertical"
    ohos:padding="16"
    ohos:background_element="$graphic:background_list_item">

    <Text
        ohos:id="$+id:song_title"
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:text_size="20fp"
        ohos:text_color="#000000"
        ohos:text_alignment="center"/>

    <Text
        ohos:id="$+id:song_artist"
        ohos:height="match_content"
        ohos:width="match_parent"
        ohos:text_size="16fp"
        ohos:text_color="#666666"
        ohos:text_alignment="center"
        ohos:top_margin="5"/>

</DirectionalLayout>
功能扩展建议
分布式播放:利用鸿蒙的分布式能力,实现多设备协同播放

歌词显示:解析LRC文件并实现实时歌词显示

音效处理:添加均衡器、音效设置等功能

在线音乐:集成音乐API,实现在线播放和下载

语音控制:结合鸿蒙的语音助手,实现语音控制播放

总结

本文介绍了如何在鸿蒙系统上开发音乐播放器应用,涵盖了基本的播放控制、进度显示、歌曲列表等功能。鸿蒙系统的分布式特性为音乐应用开发带来了新的可能性,开发者可以在此基础上进一步探索更复杂的功能,打造更加出色的音乐体验。

通过这个示例,我们可以看到鸿蒙系统在多媒体应用开发方面的强大能力,以及其简洁高效的开发模式。随着鸿蒙生态的不断完善,音乐类应用将有更多创新空间和发展机遇。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐