在大一的时候老师让我用swing做了个有关音乐播放器的案例如下:
1,启动类(App):调用Ui类展示窗口效果
package App;
import java.io.IOException;
public class App {
public static void main(String[] args) throws IOException {
Ui ui = new Ui();
}
}
2,界面类(Ui):具体的窗口效果和功能的实现
实际上这里我没有做好,因为按照分层解耦的实现应该把窗口展示和具体功能分开做
package App;
import com.sun.source.util.Trees;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;
import javazoom.jl.player.advanced.AdvancedPlayer;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.AudioHeader;
import org.jaudiotagger.audio.exceptions.CannotReadException;
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
import org.jaudiotagger.audio.mp3.MP3File;
import org.jaudiotagger.tag.TagException;
import org.w3c.dom.Text;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.*;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.List;
import java.util.Timer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Ui extends JFrame implements MouseListener, Runnable {//主界面
private JLabel LargeBox = new JLabel();//大框
private JLabel playlist = new JLabel("播放列表");
private JLabel listBox = new JLabel();//装列表 添加,删除,清空的大框
private JButton addSong = new JButton("添加歌曲");
private JButton dropSong = new JButton("删除歌曲");
private JButton emptySong = new JButton("清空列表");
private String[] arrList = new String[100];
private static int arrListI = 0;
private JList<String> listLarge;//列表框
private JScrollPane listScroll;//列表滚动条
private JButton previousSong = new JButton("上一曲");
private JButton nextSong = new JButton("下一曲");
private JButton paused = new JButton("暂停");
private JButton start = new JButton("开始");
private JButton forward = new JButton("快进5s");
private String[] playStr = {" 顺序播放", " 单曲播放", " 随机播放"};
private JComboBox play = new JComboBox(playStr);//播放
//左框(大部分功能动态刷新)
private JLabel labelLarge = new JLabel();//框左上
private JLabel labe2Large = new JLabel();//框左中
private JLabel labe3Large = new JLabel();//框左下
private JLabel ImageLarge;//图片框放左下
private int pngI = 1;//图片编号
private JLabel times = new JLabel();//歌时间
private JTextField timesLarge = new JTextField();//时间框
private static JSlider timeProgress = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 0);//时间进度条
public Ui() throws IOException {
this.hostUi();//主ui
this.setVisible(true);//窗口可见
}
private void hostUi() throws IOException {//主ui
int x = 1200, y = 700;
this.setSize(x, y);//主界面的大小
this.setTitle("音乐播放器");
this.setAlwaysOnTop(true);//标题置顶
this.setLocationRelativeTo(null);//设置页面居中
this.setDefaultCloseOperation(3);//界面关闭
this.setLayout(null);//关闭默认布局
LargeBox.setBounds(0, 0, x - 16, y - 38);//大框设置
LargeBox.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
playbackFunction();//播放功能
songPlayback();//歌曲播放
leftFunction();//左功能区
Images();//添加图片
this.getContentPane().add(LargeBox);//添加大框
//事件
events();//所有事件
}
private void addArrList() throws IOException {//列表添加
FileReader fileReader = new FileReader(new File("src\\addMusicsMp3.txt"));
int i;
int b = 0;
StringBuilder stringBuilder = new StringBuilder();
while ((i = fileReader.read()) != -1) {
if (b != 0) {
stringBuilder.append((char) i);
}
b = 1;
}
fileReader.close();
//去重复数据加载到列表
arrList = stringBuilder.toString().split("@");
TreeSet<String> treeSet = new TreeSet<>();
for (int i1 = 0; i1 < arrList.length; i1++) {
treeSet.add(arrList[i1]);
}
arrList = new String[treeSet.size()];
i = 0;
for (String string : treeSet) {
arrList[i] = string;
i++;
}
}
private void playbackFunction() throws IOException {//播放功能
listBox.removeAll();//清空
//播放列表
playlist.setBounds(0, 0, 350, 50);
playlist.setHorizontalAlignment(SwingConstants.CENTER);
Font playlistF = new Font("宋体", Font.BOLD, 24);
playlist.setFont(playlistF);
playlist.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
//装列表 添加,删除,清空的大框
listBox.setBounds(0, 50, 350, 480);
listBox.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
//添加,删除,清空
addSong.setBounds(10, 40, 100, 30);
dropSong.setBounds(10, 100, 100, 30);
emptySong.setBounds(10, 160, 100, 30);
//测试 音乐列表下拉框
addArrList();
listLarge = new JList<>(arrList);
listLarge.setVisibleRowCount(arrList.length);
listScroll = new JScrollPane(listLarge);
listScroll.setBounds(140, 40, 180, 400);
listBox.add(listScroll);
//播放功能装框
listBox.add(addSong);
listBox.add(dropSong);
listBox.add(emptySong);
//标题和播放功能框装大框
LargeBox.add(playlist);
LargeBox.add(listBox);
listBox.repaint();//刷新
}
private void songPlayback() {//歌曲播放
int x = 3, y = 531, width = 100, height = 50;
//上一曲
previousSong.setBounds(x, y, width, height);
LargeBox.add(previousSong);
//暂停
x = x + width + 20;
paused.setBounds(x, y, width, height);
LargeBox.add(paused);
//下一曲
x = x + width + 20;
nextSong.setBounds(x, y, width, height);
LargeBox.add(nextSong);
//播放
x = 3;
y += 78;
start.setBounds(x, y, width, height);
LargeBox.add(start);
//
x = x + width + 20;
play.setBounds(x, y, width, height);
Font f = new Font("宋体", Font.BOLD, 12);
play.setFont(f);
LargeBox.add(play);
//快进5s
x = x + width + 20;
forward.setBounds(x, y, width, height);
LargeBox.add(forward);
}
private void leftFunction() {
//.removeAll();//删除
//框左上 歌词名字
leftFunction1();
//框左中 歌词
leftFunction2(null);
//歌时间
leftFunction3("0:00", "0:00");
//.repaint();//刷新
}
private void leftFunction1() {
//框左上 歌词名字
labelLarge.removeAll();//删除
labelLarge.setBounds(350, 0, 850, 50);
labelLarge.setHorizontalAlignment(SwingConstants.CENTER);
Font playlistF = new Font("宋体", Font.BOLD, 24);
labelLarge.setFont(playlistF);
labelLarge.setText(songName);//歌词名字
labelLarge.setBorder(BorderFactory.createLineBorder(Color.BLACK, 3));
LargeBox.add(labelLarge);//放大框里面
labelLarge.repaint();//刷新
}
private void leftFunction2(String s) {
//框左中 歌词
labe2Large.removeAll();//删除
labe2Large.setBounds(350, 50, 850, 60);
labe2Large.setHorizontalAlignment(SwingConstants.CENTER);
Font playlistF2 = new Font("宋体", Font.BOLD, 24);
labe2Large.setFont(playlistF2);
labe2Large.setText(s);//歌词
labe2Large.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
LargeBox.add(labe2Large);//放大框里面
labe2Large.repaint();//刷新
}
private void leftFunction3(String s, String s2) {
//歌时间
times.removeAll();//删除
times.setBounds(345, 531, 836, 60);
timesLarge.setHorizontalAlignment(SwingConstants.CENTER);
Font playlistF4 = new Font("宋体", Font.BOLD, 24);
timesLarge.setFont(playlistF4);
timesLarge.setText("当前时间: " + s + " || 总时间: " + s2);
timesLarge.setEditable(false);
timesLarge.setBounds(0, 0, 836, 60);
timesLarge.setForeground(Color.gray);
times.add(timesLarge);//时间文本框放时间框里
LargeBox.add(times);//歌时间文本框
times.repaint();//刷新
timeProgress.setBounds(345, 610, 836, 40);
LargeBox.add(timeProgress);//歌时间文本框
}
private void Images() {//添加图片
labe3Large.removeAll();//清空已经添加的图片
//框左下
labe3Large.setBounds(350, 110, 850, 420);
labe3Large.setHorizontalAlignment(SwingConstants.CENTER);
Font playlistF3 = new Font("宋体", Font.BOLD, 24);
labe3Large.setFont(playlistF3);
labe3Large.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
// 图片框放左下
ImageLarge = new JLabel(new ImageIcon("src\\00" + pngI + ".png"));
ImageLarge.setBounds(2, 0, 830, 417);
labe3Large.add(ImageLarge);
LargeBox.add(labe3Large);//放大框里面
labe3Large.repaint();//刷新
}
private void events() {//所有事件
labe3Large.addMouseListener(this);//背景图片事件
addSong.addMouseListener(this);//添加歌曲事件
dropSong.addMouseListener(this);//删除歌曲事件
emptySong.addMouseListener(this);//清空歌曲事件
start.addMouseListener(this);//点击开始后放歌曲
paused.addMouseListener(this);//暂停
previousSong.addMouseListener(this);//上一曲
nextSong.addMouseListener(this);//下一曲
play.addMouseListener(this);//播放模式
forward.addMouseListener(this);//快进5s
timeProgress.addMouseListener(this);//拖动条事件
}
private static AdvancedPlayer player;//播放
private static Object lock = new Object();//锁
private static int state = 0;//状态
private Thread t;
private static int playState = 0;//播放模式状态 0 顺序播放 1 单曲播放 2 随机播放
private static String songName;//歌名
private static LocalTime songTime;//歌曲时间
private static LocalTime sumSongTime;//歌曲时间和
private ArrayList<String> listsong;//歌词
private ArrayList<Integer> second;//秒值
private DateTimeFormatter dd = DateTimeFormatter.ofPattern("mm:ss");//分钟:秒
private Timer myTimer;//计时器
private int pos = 0;//进度条
private int len;//获取歌曲时长
private void playlrc(String song) throws IOException {//获取lrc相关信息并实现显示功能
File file = new File(song + ".lrc");//看看歌曲有没有lrc文件
if (!file.exists()) {//没有歌词
listsong = new ArrayList<>();//歌词
second = new ArrayList<>();//毫秒值
} else {//有歌词
FileReader fileReader = new FileReader(file);//获取当前歌曲的lrc
StringBuilder stringBuilder = new StringBuilder();
int i;
while ((i = fileReader.read()) != -1) {
stringBuilder.append((char) i);
}
fileReader.close();
//时间 歌词
ArrayList<String> listtime = new ArrayList<>();
listsong = new ArrayList<>();
String[] arrString = stringBuilder.toString().split("\\[");
for (int j = 1; j < arrString.length; j++) {
String s = arrString[j];
listtime.add(s.split("\\]")[0].split("\\.")[0]);//时间戳优化
listsong.add(s.split("\\]")[1]);
}
//毫秒值
second = new ArrayList<>();
for (String string : listtime) {
int i1 = Integer.parseInt(string.split("\\:")[0]);
int i2 = Integer.parseInt(string.split("\\:")[1]);
i2 = i2 + (i1 * 60);
second.add(i2);
}
}
//歌曲时间和
File file2 = new File(song + ".mp3");
try {
FileInputStream fis = new FileInputStream(file2);
//获取歌曲时长
MP3File mp3File = (MP3File) AudioFileIO.read(file2);
AudioHeader mp3Header = mp3File.getAudioHeader();
len = mp3Header.getTrackLength();
//歌曲时间和 //歌曲时间
sumSongTime = LocalTime.of(0, len / 60, len % 60);//总时长
songTime = LocalTime.of(0, pos / 60, pos % 60);//初始时间
//显示歌名 刷新
leftFunction1();
//控制进度条
timeProgress.setMinimum(0);
timeProgress.setMaximum(len);
timeProgress.setValue(pos);
//pos = 0;
//创建定时器,实现控制进度条每隔1秒钟移动一步
myTimer = new Timer();
myTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
//5秒播放下张图片
if (pos % 5 == 0) {
if (pngI != 3) {
pngI++;
} else {
pngI = 1;
}
Images();
}
//显示歌词和刷新
songTime = LocalTime.of(0, pos / 60, pos % 60);//时间0:00++
int k = 0;
for (; k < second.size(); k++) {
int num = second.get(k);
if (songTime.compareTo(LocalTime.of(0, num / 60, num % 60)) == 0) {
break;
}
}
if (k != second.size()) {
leftFunction2(listsong.get(k));//歌词
}
if (pos == 1) {//切歌时候清理掉上一首歌的歌词
leftFunction2(" ");//歌词
}
//时间条刷新
if (pos < len) {
leftFunction3(dd.format(LocalTime.of(0, 0, 0).plusSeconds(pos)).toString()
, dd.format(sumSongTime).toString());
}
//拖动条刷新
timeProgress.setValue(pos);
pos++;
}
}, 0, 1000);
} catch (CannotReadException | IOException | TagException | ReadOnlyFileException |
InvalidAudioFrameException e) {
e.printStackTrace();
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
Object obj = e.getSource();
if (obj == labe3Large) {//背景图片事件
if (pngI != 3) {
pngI++;
} else {
pngI = 1;
}
Images();
} else if (obj == addSong || obj == dropSong) {//添加歌曲事件,删除歌曲事件
boolean b = true;
if (obj == dropSong) {
b = false;
}
try {
PopUpBox popUpBox = new PopUpBox(b);
if (obj == addSong) {
playbackFunction();
//写进文件里
File file = new File("src\\addMusicsMp3.txt");
file.delete();
FileWriter fileWriter = new FileWriter(file);
for (int i1 = 0; i1 < arrList.length; i1++) {
fileWriter.write("@");
fileWriter.write(arrList[i1]);
}
fileWriter.close();
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
} else if (obj == emptySong) {//清空
try {
File f = new File("src\\addMusicsMp3.txt");
f.delete();
FileWriter fw = new FileWriter(f);
fw.close();
playbackFunction();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
} else if (obj == start) {//播放功能
if (state != 1) {
t = new Thread(this);
state = 1;
t.start();
}
} else if (obj == paused) {//暂停
myTimer.cancel();
pos = timeProgress.getValue();//
state = 0;
player.close();//结束
} else if (obj == previousSong) {//上一曲
if (arrListI - 1 < 0) {
arrListI = arrList.length - 1;
} else {
arrListI--;
}
pos = 0;//
if (state == 1) {//播放中
myTimer.cancel();
if (playState == 0 || playState == 2) {//顺序播放和随机播放
arrListI--;
}
player.close();//结束
}
state = 1;
t = new Thread(this);
t.start();
} else if (obj == nextSong) {//下一曲
if (arrListI + 1 > arrList.length - 1) {
arrListI = 0;
} else {
arrListI++;
}
pos = 0;//
if (state == 1) {//播放中
myTimer.cancel();
if (playState == 0 || playState == 2) {//顺序播放和随机播放
arrListI--;
}
player.close();//结束
}
state = 1;
t = new Thread(this);
t.start();
} else if (obj == forward) {//快进5s
//先暂停
myTimer.cancel();
pos = timeProgress.getValue();
state = 0;
player.close();//结束
if (pos + 5 < len) {//歌曲快进还够5秒
pos += 5;
} else {
pos = len - 1;
}
//开始
if (state != 1) {
t = new Thread(this);
state = 1;
t.start();
}
} else if (obj == play) {//播放模式
String s = obj.toString().split("\\ ")[1];
s = s.substring(0, s.length() - 1);
if (s.equals("顺序播放")) {
playState = 0;
} else if (s.equals("单曲播放")) {
playState = 1;
} else {//随机播放
playState = 2;
Random random = new Random();
for (int i = 0; i < arrList.length; i++) {
int j = random.nextInt(arrList.length);
String s1 = arrList[i];
arrList[i] = arrList[j];
arrList[j] = s1;
}
}
} else if (obj == timeProgress) {//拖动条控制事件
//先暂停
myTimer.cancel();
pos = timeProgress.getValue();
state = 0;
player.close();//结束
//开始
if (state != 1) {
t = new Thread(this);
state = 1;
t.start();
}
}
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void run() {
synchronized (lock) {
while (state == 1) {
try {
String path = "src\\Musics\\" + arrList[arrListI];
songName = arrList[arrListI].split("\\.mp3")[0];//歌曲名字
playlrc(path.split("\\.mp3")[0]);//获取lrc相关信息
//播放器
player = new AdvancedPlayer(new FileInputStream(path));
player.play((int) (pos * 1000 / 26.122), Integer.MAX_VALUE);//播放
} catch (JavaLayerException ex) {
throw new RuntimeException(ex);
} catch (FileNotFoundException ex) {
throw new RuntimeException(ex);
} catch (IOException e) {
throw new RuntimeException(e);
}
if (state == 1) {
if (playState == 0 || playState == 2) {//顺序播放和随机播放
if (arrListI == arrList.length - 1) {
arrListI = 0;
} else {
arrListI++;
}
}
}
//单曲播放
myTimer.cancel();//停止读条
if (pos >= len) {//下一首歌时候为0
pos = 0;
}
}
}
}
}
3,事件弹框类(PopUpBox):添加删除歌单列表时候会在一个指定的文件进行添加和删除
这里路径可以指定默认的存放音乐的文件夹,我是创建了个文件夹需要自己加音乐和歌词到该文件夹进去
package App;
import javax.swing.*;
import javax.xml.crypto.Data;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.*;
import java.util.ArrayList;
public class PopUpBox implements MouseListener {//弹框
private JList<String> listLarge2;//列表框
private JScrollPane listScroll2;//列表滚动条
private JDialog jDialog;//弹框
private String[] strs = new String[100];
private int size;//数组长度
private boolean aBoolean;//判断添加,删除
public PopUpBox(boolean b) throws IOException {
aBoolean = b;
popUp();
}
private void popUp() throws IOException {//弹框列表
songNamesPrintWriter(songNameFun(true), "src\\MusicsMp3.txt", false);//歌词名写进文件里
songNamesFileReader();//获取歌曲名
if (jDialog != null) {
//jDialog.removeAll();//清空
jDialog.setVisible(false);
jDialog2.setVisible(false);
}
listLarge2 = new JList<>(strs);
listLarge2.setVisibleRowCount(size);
listScroll2 = new JScrollPane(listLarge2);
listLarge2.addMouseListener(this);//弹框列表中的弹框事件
listScroll2.setBounds(30, 30, 100, 200);
jDialog = new JDialog();
jDialog.setSize(350, 350);
jDialog.add(listScroll2);
jDialog.setAlwaysOnTop(true);
jDialog.setLocationRelativeTo(null);
jDialog.setModal(true);
jDialog.setVisible(true);
//jDialog.repaint();//刷新
}
private void songNamesFileReader() throws IOException {
FileReader fileReader = new FileReader("src\\MusicsMp3.txt");
int i;
StringBuilder stringBuilder = new StringBuilder();
while ((i = fileReader.read()) != -1) {
stringBuilder.append((char) i);
}
strs = stringBuilder.toString().split("@");
size = strs.length;
}
private void songNamesPrintWriter(ArrayList<String> songMp3, String path, boolean b) throws IOException {
File file = new File(path);
FileWriter fileWriter = new FileWriter(file, b);//b是否续写
for (int i = 0; i < songMp3.size(); i++) {
fileWriter.write("@");
fileWriter.write(songMp3.get(i));
}
fileWriter.close();
}
private ArrayList<String> songNameFun(boolean b) {//获取歌曲名字
ArrayList<String> list1 = new ArrayList<>();//获取mp3
ArrayList<String> list2 = new ArrayList<>();//获取lrc
File file = new File("src\\Musics");
for (File listFile : file.listFiles()) {
String songName = listFile.getName().toString();
String[] songNameSplit = songName.split("\\.");
int len = songNameSplit.length;
if (songNameSplit[len - 1].equals("mp3")) {
list1.add(songName);
} else {
list2.add(songName);
}
}
if (b) {
return list1;
} else {
return list2;
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
private JDialog jDialog2;//取消和确定弹框
private JButton jButton1, jButton2;//添加删除按钮
private String data;//选择数据(后面增加还是删除做依据)作为正则表达式
@Override
public void mouseReleased(MouseEvent e) {
Object obj = e.getSource();
if (obj == listLarge2) {//弹框里的列表框
data = listLarge2.getSelectedValue();
//System.out.println(data);
jDialog2 = new JDialog();
jDialog2.setSize(250, 120);
jDialog2.setLayout(null);
if (aBoolean) {//true添加,flase删除
jButton1 = new JButton("确定添加");
jButton2 = new JButton("取消添加");
} else {
jButton1 = new JButton("确定删除");
jButton2 = new JButton("取消删除");
}
jButton1.addMouseListener(this);
jButton2.addMouseListener(this);
jButton1.setBounds(10, 10, 100, 50);
jButton2.setBounds(120, 10, 100, 50);
jDialog2.add(jButton1);
jDialog2.add(jButton2);
jDialog2.setAlwaysOnTop(true);
jDialog2.setLocationRelativeTo(null);
jDialog2.setModal(true);
jDialog2.setVisible(true);
} else if (obj == jButton1) {
if (aBoolean) {//添加
try {
ArrayList<String> addStorage = new ArrayList<>();//存放添加歌曲
addStorage.add(data);
songNamesPrintWriter(addStorage, "src\\addMusicsMp3.txt", true);//歌词名写进文件里
jDialog2.setVisible(false);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
} else {//删除
File file = new File("src\\Musics\\", data);
file.delete();
try {
popUp();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
jDialog2.setVisible(false);
}
} else if (obj == jButton2) {//取消
jDialog2.setVisible(false);
}
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
4,最终展示效果图
4.1,Ui效果展示
4.2,PopUpBox效果展示
4.3,最终运行效果
5,缺点
- 没做好分层解耦
- 添加音乐到列表没做好
- 因为根据io流和字符串截取,所以对歌词文件的格式需要统一,可能部分音乐会因为歌词文件时间对不上导致有延时效果
- 歌词只能单条显示
- 中间很大的一片空白是循环播放图片,不过这里我用的是简易的数组存储指定路径的图片如001,002,003进入多线程依次循环播放