天天看點

基于線程和圖形界面的音樂播放器

音樂播放器

一、所需的外包:

jaudiotagger-2.2.6-SNAPSHOT.jar

jl1.0.1.jar

二、實作的具體功能:

  1. 根據選擇的目标目錄,導入該目錄下的所有.mp3檔案,在控制台會顯示歌曲的集合;
  2. 上方清單會顯示導入的歌曲的一些詳細資訊:歌曲名、歌手、專輯和播放時間,輕按兩下音樂清單中的歌曲,可以播放該歌曲;
  3. 下方左側區域會顯示正在播放歌曲的歌曲名和歌手,最右側區域會顯示播放歌曲的時間;
  4. 清單下方的進度條會根據播放的進度而增加,一首歌曲播放完成,會進行更新;
  5. 下方按鈕分别為,上一首,暫停,下一首和播放模式選擇,點選相應按鈕會實作相應的功能;
  6. 播放模式分為:單曲循環播放,清單循環播放,随機播放;
  7. 最小化時,播放器會最小化到系統托盤區域,輕按兩下或者點選右鍵->顯示首頁面,會顯示播放器,并退出托盤區域。

三、關于一些需要注意的問題

1.關于播放和暫停
  1. 該播放方法會造成線程的阻塞,對播放歌曲的播放,應該重新開一個線程;
  2. 由于不存在暫停功能,我的解決方法是通過設定變量控制線程類的park()方法來阻塞線程,再通過主線程調用unpark()來解除該線程的阻塞,來達到暫停和播放的功能;
  3. 對于停止播放,直接調用該類的close()方法即可。
2.關于進度條
  1. 使用标簽JLabel類的setBounds(),根據計算的播放的時間和總時間之間的得到已播放百分比,根據設定寬度來改變顯示的jlabel的長度達到播放進度的功能;
  2. 使用定時器TimerTask來計算播放的時間以及每秒更新标簽位置,并對全局變量已播放的時間進行指派,可以記錄目前播放的進度,用于播放和暫停,将進度條和時間恢複到已播放的位置。

播放器運作的效果

  1. 初始運作界面
    基于線程和圖形界面的音樂播放器
  2. 導入本地歌曲
    基于線程和圖形界面的音樂播放器
  3. 播放界面
    基于線程和圖形界面的音樂播放器

實作代碼

項目結構:

基于線程和圖形界面的音樂播放器

播放工具類(播放方法,擷取歌曲資訊方法,時間格式轉化方法):

package com.bxwl.player.comm;

import com.bxwl.player.service.MusicService;
import com.bxwl.player.entity.Music;
import javazoom.jl.player.Player;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.audio.mp3.MP3AudioHeader;
import org.jaudiotagger.audio.mp3.MP3File;
import org.jaudiotagger.tag.TagException;
import org.jaudiotagger.tag.id3.AbstractID3v2Frame;
import org.jaudiotagger.tag.id3.AbstractID3v2Tag;
import org.jaudiotagger.tag.id3.framebody.FrameBodyAPIC;

import javax.swing.*;
import java.awt.*;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;

/**
 * 音樂播放的工具類
 *
 * @author bxwl
 * @create 2020-07-26 21:25
 */
public class MP3Utils {

    /**
     * 建立播放器對象
     */
    private static Player player;
    /**
     * 建立歌曲對象
     */
    private static MP3File file;
    /**
     * 目前播放對象
     */
    private static Music music;
    /**
     * 播放狀态
     */
    private volatile static boolean statue = true;
    /**
     * 對線程控制
     */
    private volatile static AtomicBoolean pause = new AtomicBoolean(false);
    /**
     * 标記歌曲是否單曲循環播放
     */
    private static boolean loop = false;
    /**
     * 标記是否随機播放
     */
    private static boolean random = false;
    /**
     * 随機數對象
     */
    private static Random ran = new Random();
    /**
     * 是否停止播放
     */
    private static boolean stop = false;

    /**
     * 設定控制線程的變量
     * @param b
     */
    public static void setPause(boolean b) {
        pause.set(b);
    }

    public static Music getPlaying() {
        return music;
    }

    public static void setPlaying(Music m) {
        music = m;
    }

    /**
     * 傳回播放器對象
     * @return
     */
    public static Player getPlayer() {
        return player;
    }

    public static void SetLoop(boolean loop) {
        MP3Utils.loop = loop;
    }

    public static void setRandom(boolean random) {
        MP3Utils.random = random;
    }

    public static boolean isStatue() {
        return statue;
    }

    public static void setStatue(boolean statue) {
        MP3Utils.statue = statue;
    }

    public static void setStop(boolean stop) {
        MP3Utils.stop = stop;
    }

    /**
     * 開始播放
     */
    public static void play(Music m) {
        setPlaying(m);
        //存儲播放歌曲的集合
        List<Music> lists = new ArrayList<>();
        //标記歌曲清單中目前播放歌曲
        boolean flag = false;
        //清單循環
        for (int i = 0; i < MusicService.list.size(); i++) {
            Music misic = MusicService.list.get(i);
            if (m.getId() == misic.getId()) {
                flag = true;
            }
            //将目前播放的歌曲及後面的放入播放集合
            if (flag) {
                lists.add(misic);
            }
        }

        Runnable r = new Runnable() {
            //播放集合中的索引
            int index = 0;

            @Override
            public void run() {
                try {
                    //位元組輸入流讀取音頻檔案
                    InputStream is = new FileInputStream(lists.get(index).getPath());
                    //位元組流轉字元流
                    BufferedInputStream bis = new BufferedInputStream(is);
                    //建立播放器對象
                    player = new Player(bis);


                    statue = true;
                    stop = false;

                    while (statue) {

                        while (!pause.get()) {
                            //暫停(線程阻塞)
                            if (pause.get()) {
                                LockSupport.park();
                                setPause(false);
                            }

                            if(stop){
                                System.out.println("stop");
                                statue = false;
                                stop = false;
                                player.close();
                                play(m);
                            }
                            synchronized (this){
                                //播放到末尾或者停止播放
                                if (!player.play(1)) {
                                    System.out.println("finsh");
                                    statue = false;
                                    player.close();
                                    break;
                                }
                            }

                        }
                    }

                    Thread.sleep(500);
                    //判斷是否循環,不單曲循環,則清單循環循環,索引增加
                    if (!loop) {
                        //播放到最後一首
                        if (index == lists.size() - 1) {
                            index = -1;
                        }
                        index++;
                        if (random) {
                            //随機播放
                            index = getRandom(lists.size());
                        }
                        //設定正在播放的歌曲
                        setPlaying(lists.get(index));
                    }
                    //使用目前Runnable啟動新線程
                    Thread d = new Thread(this);
                    d.start();
                    Thread.interrupted();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    Thread.interrupted();
                }
            }
        };
        Thread th = new Thread(r);
        th.start();
        //擷取線程狀态資訊,主線程喚醒播放線程
        if (!pause.get()) {
            LockSupport.unpark(null);
        }
    }

    /**
     * 根據索引擷取一個随機數
     * @param size
     * @return
     */
    private static int getRandom(int size) {
        return ran.nextInt(size);
    }

    /**
     * 擷取歌曲的相關資訊
     * @param m
     * @return
     */
    public static Music getMusicInfo(Music m) {
        String path = m.getPath();
        try {
            file = new MP3File(path);
            //擷取歌曲名
            String name = file.getID3v2Tag().frameMap.get("TIT2").toString();
            m.setName(name.substring(6, name.length() - 3));
            //擷取歌曲演唱者
            String singer = file.getID3v2Tag().frameMap.get("TPE1").toString();
            m.setSinger(singer.substring(6, singer.length() - 3));
            //擷取歌曲專輯
            String album = file.getID3v2Tag().frameMap.get("TALB").toString();
            m.setAlbum(album.substring(6, album.length() - 3));
            //擷取歌曲時長
            MP3AudioHeader audioHeader = (MP3AudioHeader) file.getAudioHeader();
            m.setTime(audioHeader.getTrackLength());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TagException e) {
            e.printStackTrace();
        } catch (ReadOnlyFileException e) {
            e.printStackTrace();
        } catch (InvalidAudioFrameException e) {
            e.printStackTrace();
        } catch (CannotReadException e) {
            e.printStackTrace();
        }
        return m;
    }

    /**
     * 擷取歌曲封面圖檔
     *
     * @param m
     * @param dir
     * @return
     */
    @Deprecated
    public ImageIcon saveMusicImage(Music m, File dir) {

        FileOutputStream fos = null;
        ImageIcon icon = null;
        try {
            file = new MP3File(m.getPath());
            //建立存儲圖檔的檔案
            File target = new File(dir, m.getName() + "_cover.jpg");
            if (!target.exists()) {
                target.createNewFile();
            }

            AbstractID3v2Tag tag = file.getID3v2Tag();
            AbstractID3v2Frame frame = (AbstractID3v2Frame) tag.getFrame("APIC");
            FrameBodyAPIC body = (FrameBodyAPIC) frame.getBody();
            byte[] imageData = body.getImageData();
            Image img = Toolkit.getDefaultToolkit().createImage(imageData, 0, imageData.length);
            icon = new ImageIcon(img);
            fos = new FileOutputStream(target);
            fos.write(imageData);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TagException e) {
            e.printStackTrace();
        } catch (ReadOnlyFileException e) {
            e.printStackTrace();
        } catch (CannotReadException e) {
            e.printStackTrace();
        } catch (InvalidAudioFrameException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return icon;
    }

    /**
     * 時間格式轉換
     */
    public static String timeChange(int second) {
        int min = second / 60;
        int sec = second - min * 60;
        String rt1 = String.format("%02d", min);
        String rt2 = String.format("%02d", sec);
        String rt = rt1 + ":" + rt2;
        return rt;
    }
}
           

播放模式枚舉:

package com.bxwl.player.comm;

/**
 * @author bxwl
 * @create 2020-07-27 19:20
 */
public enum  PlayModel {

    SINGLE(1,"單曲循環"),
    LIST(2,"清單循環"),
    RANDOM(3,"随機播放");

    private final int i;
    /**模式*/
    private final String model;

    private PlayModel(int i,String model){
        this.i = i;
        this.model = model;
    }
    public String getModelString(){
        return model;
    }
}
           

存放歌曲的對象實體類:

package com.bxwl.player.entity;

/**
 * @author bxwl
 * @create 2020-07-23 19:07
 */
public class Music {

    /**id*/
    private int id;
    /**歌曲名*/
    private String name;
    /**歌手*/
    private String singer;
    /**歌曲時長*/
    private long time;
    /**歌曲絕對路徑名*/
    private String path;
    /**歌曲所屬專輯*/
    private String album;

    public Music() {
    }

    public Music(int id, String path) {
        this.id = id;
        this.path = path;
    }

    public Music(int id, String name, String singer, long time, String path, String album) {
        this.id = id;
        this.name = name;
        this.singer = singer;
        this.time = time;
        this.path = path;
        this.album = album;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSinger() {
        return singer;
    }

    public void setSinger(String singer) {
        this.singer = singer;
    }

    public long getTime() {
        return time;
    }

    public void setTime(long time) {
        this.time = time;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getAlbum() {
        return album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }

    @Override
    public String toString() {
        return "Music{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", singer='" + singer + '\'' +
                ", time=" + time +
                ", path='" + path + '\'' +
                ", album='" + album + '\'' +
                '}';
    }
}
           

用于對歌曲的存儲操作的類:

package com.bxwl.player.service;

import com.bxwl.player.entity.Music;

import java.util.LinkedList;
import java.util.List;

/**
 * @author bxwl
 * @create 2020-07-25 09:52
 */
public class MusicService {

    /**
     * 用于存儲歌曲的清單
     */
    public static List<Music> list = new LinkedList<>();

    /**
     * 添加歌曲到清單
     * @param m
     * @return
     */
    public boolean addMusic(Music m) {
        if (list.size() != 0) {
            for (Music music : list) {
                if (m.getName().equals(music.getName()) && m.getSinger().equals(music.getSinger())) {
                    return false;
                }
            }
        }
        return list.add(m);
    }

    /**
     * 根據歌曲id從清單移出
     * @param id
     * @return
     */
    public boolean deleteMusicById(int id) {
        Music m = null;
        if (list.size() != 0) {
            for (Music music : list) {
                if (id == music.getId()) {
                    m = music;
                    break;
                }
            }
        }
        if (m == null) {
            return false;
        }
        list.remove(m);
        return true;
    }

    public Music getMusicById(int id){
        if (list.size() != 0) {
            for (Music music : list) {
                if (id == music.getId()) {
                    return music;
                }
            }
        }
        return null;
    }
}
           

主窗體設計類:

package com.bxwl.player.ui;

import com.bxwl.player.comm.MP3Utils;
import com.bxwl.player.comm.PlayModel;
import com.bxwl.player.entity.Music;
import com.bxwl.player.service.MusicService;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

/**
 * @author bxwl
 * @create 2020-07-23 19:14
 */
public class MusicFrame extends JFrame implements ActionListener {

    private MusicService mm = new MusicService();
    /**
     * 歌曲id
     */
    private int id = 1;
    /**
     * 播放按鈕
     */
    private JButton playBtn;
    /**
     * 标記播放按鈕是否被點選
     */
    private boolean isPlayBtn = true;
    /**
     * 上一曲
     */
    private JButton lastBtn;
    /**
     * 下一曲
     */
    private JButton nextBtn;
    /**
     * 導入目錄中的歌曲按鈕
     */
    private JButton openFileBtn;
    /**
     * 播放模式選擇按鈕
     */
    private JButton playModelBtn;
    /**
     * 顯示歌曲的清單
     */
    private JTable playTable;
    /**
     * 進度條
     */
    private JLabel timeBar;
    /**
     * 預設播放模式
     */
    private PlayModel model = PlayModel.LIST;
    /**
     * 顯示歌曲名文本
     */
    private JTextArea musicNamejta;
    /**
     * 顯示歌曲時間文本
     */
    private JTextArea runtime;
    /**
     * 顯示歌曲歌手名文本
     */
    private JTextArea musicSingerjta;
    /**
     * 處理顯示時間定時器對象
     */
    private Timer showTimeTimer;
    /**
     * 顯示正在播放的歌曲的定時器對象
     */
    private Timer showMusicInfoTimer;
    /**
     * 已播放時間
     */
    private volatile int time = 0;
    /**
     * 托盤圖示對象
     */
    private TrayIcon trayIcon;
    /**
     * 本作業系統托盤對象
     */
    private SystemTray systemTray;
    /**
     * 歌曲的總播放時間
     */
    private int totalTime;

    public MusicFrame() {
        //設定标題
        setTitle("音樂播放器 v1.0.1");
        //設定窗體大小
        setSize(800, 543);
        //禁止窗體調整大小
        setResizable(false);
        //設定窗體居中
        setLocationRelativeTo(null);
        //清除布局
        setLayout(null);
        //控件布局
        init();

        //關閉窗體同時關閉程式
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //窗體監聽
        addWindowListener(new WindowAdapter() {
            //窗體退出事件
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }

            //窗體最小化事件
            @Override
            public void windowIconified(WindowEvent e) {
                systemIcon();
                //關閉窗體釋放該窗體的占用的部分資源
                dispose();
            }
        });
        //設定窗體可見
        setVisible(true);
    }

    /**
     * 初始化各按鈕
     */
    public void init() {
        //進度條
        String timeImgPath = this.getClass().getResource("/img/progressBar.png").getPath();
        Icon timeImg = new ImageIcon(timeImgPath);
        timeBar = new JLabel(timeImg);
        timeBar.setBounds(35, 420, 0, 14);
        add(timeBar);


        //顯示歌曲名文本
        musicNamejta = new JTextArea("");
        musicNamejta.setBackground(new Color(60, 63, 65));
        musicNamejta.setBounds(35, 440, 150, 45);
        //字型顔色
        musicNamejta.setForeground(Color.black);
        add(musicNamejta);

        //顯示歌手文本
        musicSingerjta = new JTextArea("");
        musicSingerjta.setBackground(new Color(60, 63, 65));
        musicSingerjta.setBounds(186, 440, 100, 45);
        //字型顔色
        musicSingerjta.setForeground(Color.black);
        add(musicSingerjta);

        //上一首按鈕
        lastBtn = new JButton();
        //擷取圖示位址
        String lastBtnImgPath = this.getClass().getResource("/img/last.png").getPath();
        //建立上一首圖示對象
        Icon lastBtnImg = new ImageIcon(lastBtnImgPath);
        //設定上一首圖示
        lastBtn.setIcon(lastBtnImg);
        //設定按鈕位置、大小
        lastBtn.setBounds(300, 440, lastBtnImg.getIconWidth(), lastBtnImg.getIconHeight()); //設定播放按鈕大小
        //設定不填充
        lastBtn.setContentAreaFilled(false);
        //不繪制邊框
        lastBtn.setBorderPainted(false);
        //設定按鈕邊框
        lastBtn.setMargin(new Insets(0, 0, 0, 0));
        //設定按鈕的标簽
        lastBtn.setActionCommand("上一曲");
        //添加按鈕監聽器
        lastBtn.addActionListener(this);
        //添加播放按鈕至視窗中
        add(lastBtn);

        //播放/暫停按鈕
        playBtn = new JButton();
        String playBtnImgPath = this.getClass().getResource("/img/play.png").getPath();
        Icon playBtnImg = new ImageIcon(playBtnImgPath);
        playBtn.setIcon(playBtnImg);
        playBtn.setBounds(352, 440, playBtnImg.getIconWidth(), playBtnImg.getIconHeight());
        playBtn.setContentAreaFilled(false);
        playBtn.setBorderPainted(false);
        playBtn.setMargin(new Insets(0, 0, 0, 0));
        playBtn.setActionCommand("播放");
        playBtn.addActionListener(this);
        add(playBtn);

        //下一首按鈕
        nextBtn = new JButton();
        String nextBtnImgPath = this.getClass().getResource("/img/next.png").getPath();
        Icon nextBtnImg = new ImageIcon(nextBtnImgPath);
        nextBtn.setBounds(405, 440, nextBtnImg.getIconWidth(), nextBtnImg.getIconHeight());
        nextBtn.setIcon(nextBtnImg);
        nextBtn.setContentAreaFilled(false);
        nextBtn.setBorderPainted(false);
        nextBtn.setMargin(new Insets(0, 0, 0, 0));
        nextBtn.setActionCommand("下一曲");
        nextBtn.addActionListener(this);
        add(nextBtn);

        //建立播放歌曲清單
        playTable = new JTable(){
            //單元格無法編輯
            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };
        //設定播放清單背景顔色
        playTable.setBackground(new Color(60, 63, 65));
        //設定播放清單字型顔色
        playTable.setForeground(Color.BLACK);
        //添加播放清單的輕按兩下事件關聯
        playTable.addMouseListener(new ListEvent());
        // 把清單元件放入滾動面闆中來實作滾動功能
        JScrollPane jsp = new JScrollPane(playTable);
        jsp.setBounds(35, 25, 730, 375);
        //設定表格可見
        jsp.setViewportView(playTable);
        add(jsp);

        //播放模式按鈕
        playModelBtn = new JButton("L");
        Font f = new Font("Arial", Font.BOLD, 22);
        playModelBtn.setFont(f);
        playModelBtn.setForeground(Color.white);
        playModelBtn.setBounds(460, 440, 52, 52);
        playModelBtn.setBackground(new Color(42, 42, 42));
        playModelBtn.setActionCommand("播放模式");
        playModelBtn.addActionListener(this);
        add(playModelBtn);

        //打開擷取歌曲清單的目錄按鈕
        openFileBtn = new JButton();
        String openFileBtnImgPath = this.getClass().getResource("/img/list.png").getPath();
        Icon openFileBtnImg = new ImageIcon(openFileBtnImgPath);
        openFileBtn.setBounds(513, 440, openFileBtnImg.getIconWidth(), openFileBtnImg.getIconHeight());
        openFileBtn.setIcon(openFileBtnImg);
        openFileBtn.setContentAreaFilled(false);
        openFileBtn.setBorderPainted(false);
        openFileBtn.setMargin(new Insets(0, 0, 0, 0));
        openFileBtn.setActionCommand("選擇目錄");
        openFileBtn.addActionListener(this);
        add(openFileBtn);

        //歌曲播放時間	控件
        runtime = new JTextArea("  00 : 00");
        runtime.setBounds(600, 450, 50, 25);
        runtime.setBackground(new Color(60, 63, 65));
        runtime.setForeground(Color.BLACK);
        add(runtime);
    }

    /**
     * 滑鼠時間類
     */
    class ListEvent extends MouseAdapter {
        @Override
        public void mouseClicked(MouseEvent e) {

            //如果滑鼠連續點選兩次
            if (e.getClickCount() == 2) {
                if (e.getSource().equals(playTable)) {
                    //添加輕按兩下播放清單中選中的音樂
                    int index = playTable.getSelectedRow();

                    //說明正有歌曲在播放
                    if (MP3Utils.isStatue()) {
                        //解除暫停按鈕的留下的線程阻塞
                        MP3Utils.setPause(false);
                        //停止播放
                        MP3Utils.setStop(true);

                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                    //初始化控件的歌曲資訊
                    musicInit();
                    //停止時間計算任務
                    timerStop();
                    //停止歌曲資訊顯示定時任務
                    stopInfoTimer();
                    //擷取點選的歌曲對象
                    Music music = MusicService.list.get(index);
                    //播放歌曲
                    MP3Utils.play(music);
                    //開啟歌曲資訊顯示定時任務
                    infoShowTask(music);
                }
            }
        }
    }

    /**
     * 歌曲資訊初始化
     */
    public void musicInit() {
        //按鈕标志為播放狀态
        isPlayBtn = true;
        //已播放時間重置
        time = 0;
    }

    /**
     * 用于歌曲資訊更新顯示的定時任務
     *
     * @param m
     */
    public void infoShowTask(Music m) {

        showMusicInfoTimer = new Timer();

        //第一首歌曲的播放時間
        totalTime = (int) m.getTime();
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //啟動定時任務
        showMusicInfoTimer.schedule(new TimerTask() {
            //臨時存儲播放的歌曲
            Music tempMusic = m;
            //标記是否換歌
            boolean flag = true;

            @Override
            public void run() {
                Music m = MP3Utils.getPlaying();
                if(flag){
                    totalTime = (int) tempMusic.getTime();
                    addMusicInfo(tempMusic);
                    //開始時間計時
                    timerFun(tempMusic);
                    flag = false;
                    System.out.println("播放時間:"+MP3Utils.timeChange(totalTime));
                    System.out.println("歌曲名:"+tempMusic.getName());
                }
                if(tempMusic.getId() != m.getId()){
                    tempMusic = m;
                    flag = true;
                    musicInit();
                    timerStop();
                }
            }
        }, 0, 1000);
    }

    /**
     * 歌曲資訊顯示任務停止
     */
    public void stopInfoTimer() {
        if (showMusicInfoTimer != null) {
            showMusicInfoTimer.cancel();
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        //擷取點選按鈕标簽
        String cmd = e.getActionCommand();

        if ("上一曲".equals(cmd)) {
            System.out.println("上一曲");
            Music m = MP3Utils.getPlaying();
            int index = -1;
            //查找目前播放歌曲所在集合的索引
            for (int i = 0; i < MusicService.list.size(); i++) {
                if (m.getId() == MusicService.list.get(i).getId()) {
                    index = i;
                }
            }
            //說明正有歌曲在播放
            if (MP3Utils.isStatue()) {
                //解除暫停按鈕的留下的線程阻塞
                MP3Utils.setPause(false);
                //停止播放
                MP3Utils.setStop(true);

            }
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            m = MusicService.list.get(index - 1);
            //各資訊初始化
            musicInit();
            timerStop();
            //播放歌曲
            MP3Utils.play(m);
            //歌曲資訊更新顯示定時任務啟動
            infoShowTask(m);
        } else if ("播放".equals(cmd)) {

            if (isPlayBtn) {
                System.out.println("暫停");
                if (MP3Utils.getPlayer() != null) {
                    //暫停,線程阻塞
                    MP3Utils.setPause(true);
                    isPlayBtn = false;
                    timerStop();
                    stopInfoTimer();
                }

            } else {
                System.out.println("播放");
                if (MP3Utils.getPlayer() != null) {
                    //解除線程阻塞
                    MP3Utils.setPause(false);
                    isPlayBtn = true;
                    infoShowTask(MP3Utils.getPlaying());
                }
            }
        } else if ("下一曲".equals(cmd)) {
            System.out.println("下一曲");
            Music m = MP3Utils.getPlaying();
            int index = -1;
            for (int i = 0; i < MusicService.list.size(); i++) {
                if (m.getId() == MusicService.list.get(i).getId()) {
                    index = i;
                }
            }
            //說明正有歌曲在播放
            if (MP3Utils.isStatue()) {
                //解除暫停按鈕的留下的線程阻塞
                MP3Utils.setPause(false);
                //停止播放
                MP3Utils.setStop(true);

            }
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            m = MusicService.list.get(index + 1);
            musicInit();
            timerStop();
            MP3Utils.play(m);
            infoShowTask(m);
        } else if ("播放模式".equals(cmd)) {
            setPlayModel();
        } else if ("選擇目錄".equals(cmd)) {
            //建立檔案選擇器對象
            JFileChooser fileChooser = new JFileChooser("D:\\");
            //設定窗體的準許按鈕
            fileChooser.setApproveButtonText("确定");
            //隻允許選擇目錄
            fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            //彈出一個“打開檔案”檔案選擇器對話框
            int returnVal = fileChooser.showOpenDialog(fileChooser);
            File selectFile = fileChooser.getSelectedFile();
            //确認目前所選檔案夾後
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                //将所選目錄下的歌曲添加到集合
                addMusicToList(selectFile);
                //在清單中顯示集合中的歌曲
                addMusicToList();
            }
            System.out.println(MusicService.list);
        }
    }


    /**
     * 添加指定目錄下的所有歌曲到集合
     * @param dir
     */
    public void addMusicToList(File dir) {
        File[] files = dir.listFiles();
        if (files != null) {
            for (File f : files) {
                if (f.isFile()) {
                    if (f.getName().endsWith(".mp3") || f.getName().endsWith(".au") || f.getName().endsWith(".wav")) {
                        Music m = new Music(id++, f.getAbsolutePath());
                        m = MP3Utils.getMusicInfo(m);
                        mm.addMusic(m);
                    }
                } else {
                    addMusicToList(f);
                }
            }
        }
    }

    /**
     * 将歌曲集合顯示在清單元件中
     */
    public void addMusicToList() {
        //擷取播放總的歌曲數目
        int rowCount = MusicService.list.size();
        //設定表頭
        String[] head = new String[]{"序号", "歌曲名", "歌手", "專輯", "播放時間"};
        //添加首行元素資料
        DefaultTableModel model = new DefaultTableModel(head, 0);
        //添加歌曲資訊到表格
        for (int j = 0; j < rowCount; j++) {
            Vector<String> vt = new Vector<>();
            int k = j + 1;
            Music m = MusicService.list.get(j);
            vt.add(k + "");
            vt.add(m.getName());
            vt.add(m.getSinger());
            vt.add(m.getAlbum());
            vt.add(MP3Utils.timeChange((int) m.getTime()));
            model.addRow(vt);
        }

        playTable.setModel(model);
        //設定表格行高
        playTable.setRowHeight(20);
        //設定表格各列的寬度
        playTable.getColumnModel().getColumn(0).setPreferredWidth(15);
        playTable.getColumnModel().getColumn(1).setPreferredWidth(245);
        playTable.getColumnModel().getColumn(2).setPreferredWidth(100);
        playTable.getColumnModel().getColumn(3).setPreferredWidth(100);
        playTable.getColumnModel().getColumn(4).setPreferredWidth(20);
        //隐藏網格線
        playTable.setShowGrid(false);
        // 更新表格
        playTable.invalidate();
    }

    /**
     * 添加歌曲資訊到對應元件
     *
     * @param m
     */
    public void addMusicInfo(Music m) {
        //将歌曲名顯示
        musicNamejta.setText("\r\n"+m.getName());
        //将歌手資訊顯示
        musicSingerjta.setText("\r\n"+m.getSinger());
    }

    /**
     * 時間定時器,計算歌曲播放時間
     * @param m
     */
    public void timerFun(Music m) {
        timerStop();
        //建立定時器
        showTimeTimer = new Timer();
        //建立定時任務
        showTimeTimer.schedule(new TimerTask() {
            int startTime = time;

            public void run() {
                //播放完
                if (totalTime == startTime) {
                    startTime = 0;
                    time = 0;
                    timeBar.setBounds(35, 420, 0, 14);
                    timeBar.validate();
                    this.cancel();
                }
                //根據時長設定進度條顯示的長度
                timeBar.setBounds(35, 420, (int) (((startTime * 1.0) / totalTime) * 730), 14);
                //重新布置該元件
                timeBar.validate();
                String rt = MP3Utils.timeChange(startTime);
                runtime.setText("  "+rt);
                startTime += 1;
                time = startTime;
            }
        }, 0, 1000);
    }

    /**
     * 停止計時器
     */
    public void timerStop() {
        if (showTimeTimer != null) {
            showTimeTimer.cancel();
        }
        timeBar.setBounds(35, 420, 0, 14);
        timeBar.validate();
    }

    /**
     * 播放模式
     */
    public synchronized void setPlayModel() {
        if (model == PlayModel.LIST) {
            //更新按鈕圖示
            playModelBtn.setText("S");
            MP3Utils.setRandom(false);
            MP3Utils.SetLoop(true);
            //設定播放模式
            model = PlayModel.SINGLE;
        } else if (model == PlayModel.SINGLE) {
            playModelBtn.setText("R");
            MP3Utils.SetLoop(false);
            MP3Utils.setRandom(true);
            model = PlayModel.RANDOM;
        } else if (model == PlayModel.RANDOM) {
            playModelBtn.setText("L");
            MP3Utils.setRandom(false);
            MP3Utils.SetLoop(false);
            model = PlayModel.LIST;
        }
        System.out.println(model.getModelString());
    }

    /**
     * 為程式設定系統托盤
     */
    private void systemIcon() {
        try {
            //判斷系統是否支援系統托盤功能
            if (SystemTray.isSupported()) {
                //建立菜單圖示
                URL resource = this.getClass().getResource("/img/Audios.png");
                ImageIcon img = new ImageIcon(resource);
                //建立彈出菜單對象
                PopupMenu popupMenu = new PopupMenu();

                //打開主界面菜單
                MenuItem item = new MenuItem("打開主界面");
                popupMenu.add(item);
                item.addActionListener(e -> {
                    // 從系統的托盤執行個體中移除托盤圖示
                    systemTray.remove(trayIcon);
                    //置此 frame 的狀态,該狀态表示為逐位掩碼
                    setExtendedState(JFrame.NORMAL);
                    setVisible(true);
                    toFront();
                });

                //退出菜單添加
                MenuItem item2 = new MenuItem("退出");
                popupMenu.add(item2);
                item2.addActionListener(e -> System.exit(0));

                // 托盤圖示
                trayIcon = new TrayIcon(img.getImage(), "音樂播放器", popupMenu);
                //輕按兩下事件
                trayIcon.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        //輕按兩下托盤菜單事件
                        if (e.getClickCount() == 2) {
                            // 從系統的托盤執行個體中移除托盤圖示
                            systemTray.remove(trayIcon);
                            //置此 frame 的狀态,該狀态表示為逐位掩碼
                            setExtendedState(JFrame.NORMAL);
                            setVisible(true);
                            toFront();
                        }
                    }
                });
                //本作業系統托盤的執行個體
                systemTray = SystemTray.getSystemTray();
                //托盤圖示添加到系統托盤執行個體
                systemTray.add(trayIcon);

            }
        } catch (AWTException e) {
            e.printStackTrace();
        }
    }
}
           

注意:對于系統托盤欄亂碼問題:在運作配置處設定VM options為"-Dfile.encoding=GB18030"即可

總結

  1. java不太适合用于實作圖形界面,可以作為樂趣來學習;
  2. 存在一些小的bug,例如:上一曲、下一曲以及輕按兩下播放清單切歌時的停頓會有雜音的問題,是由于線程問題引起的。
  3. 對于播放所依賴的包,自我感覺用的不太舒服,沒有提供暫停的方法,好處是可以直接播放.mp3格式歌曲,而java自帶的AudioClip,對播放格式有要求,無法播放主流的.mp3格式歌曲。
  4. 多動手寫一些感興趣的項目,會對自己的提升很大,感觸比較大的是多線程這塊,實際用的時候往往情況比學的時候複雜的多。