天天看点

基于线程和图形界面的音乐播放器

音乐播放器

一、所需的外包:

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. 多动手写一些感兴趣的项目,会对自己的提升很大,感触比较大的是多线程这块,实际用的时候往往情况比学的时候复杂的多。