1.概述
我们是基于java.swing框架下设计的用户界面,实现播放器的基本交互功能。本代码由于设计之初考虑不是很周到导致代码冗余严重,其次我在代码内只设置了三首歌,其实可以增加一个从文件选择歌曲的按钮用来将歌曲导入列表。
代码需要导入的包如下:
import java.io.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import java.util.Timer;
import javax.sound.sampled.*;
目录
2.各功能模块的设计思路和方法
2.1暂停停按钮
(1)实现功能:能将正在播放的歌曲暂停,并且将暂停图标会变成播放图标,同时歌词滚动停止。鼠标悬停和按下有特殊效果,悬停时伴有文字提示功能。
(2)具体代码实现:
buttonStop = new JButton();//添加暂停按钮
buttonStop.setToolTipText("暂停");
buttonStop.setBounds(211, 615, 52, 54); //设置暂停按钮大小
Icon icon_stop = new ImageIcon(".//applestop.jpg");//创建暂停图标对象
buttonStop.setIcon(icon_stop); //设置暂停图标
buttonStop.setBorder(BorderFactory.createEmptyBorder());
buttonStop.setFocusPainted(false);
buttonStop.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
// 鼠标进入按钮时的效果
buttonStop.setBorder(BorderFactory.createLineBorder(Color.WHITE, 2));
}
@Override
public void mouseExited(MouseEvent e) {
// 鼠标离开按钮时的效果恢复到默认状态
buttonStop.setBorder(BorderFactory.createEmptyBorder());
}
@Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
buttonStop.setBorder(BorderFactory.createLineBorder(Color.WHITE, 2));
remove(buttonStop);
remove(blank);
add(buttonPlay);
audioplay.stop();
}
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
buttonStop.setBorder(BorderFactory.createLineBorder(Color.GRAY, 2));
}
});
2.2播放按钮
(1)实现功能: 能将暂停的歌或者选定的歌重新播放重新播放,播放按钮的位置变成暂停按钮的样式,对于鼠标悬停以及按压会产生一定的效果,鼠标悬停有文字提示其功能。
(2)具体代码实现:
buttonPlay = new JButton();//添加播放按钮
buttonPlay.setToolTipText("播放");
buttonPlay.setBounds(211, 615, 52, 54); //设置播放按钮大小
Icon icon = new ImageIcon(".//appleplay.png");//创建播放图标对象
buttonPlay.setIcon(icon); //设置播放图标
buttonPlay.setBorder(BorderFactory.createEmptyBorder());
buttonPlay.setFocusPainted(false);
buttonPlay.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
// 鼠标进入按钮时的效果
buttonPlay.setBorder(BorderFactory.createLineBorder(Color.WHITE, 2));
}
@Override
public void mouseExited(MouseEvent e) {
// 鼠标离开按钮时的效果恢复到默认状态
buttonPlay.setBorder(BorderFactory.createEmptyBorder());
}
@Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
buttonPlay.setBorder(BorderFactory.createLineBorder(Color.WHITE, 2));
textName.setText(""+SongName[choose].substring(0,SongName[choose].length()-4));
remove(buttonPlay);
remove(GIF[0]);
remove(GIF[1]);
remove(GIF[2]);
revalidate();
repaint();
add(GIF[choose-1]);
add(buttonStop);
add(blank);
//System.out.println(audioplay.GetTotaltime());
if (audioplay.getFramePosition() > 0) {
audioplay.playAfterPause(audioplay.getFramePosition());
} else {
audioplay.play(0);
}
}
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
buttonPlay.setBorder(BorderFactory.createLineBorder(Color.GRAY, 2));
}
});
add(buttonPlay);//添加播放按钮至窗口中
2.3上一首/下一首按钮
(1) 实现功能:能将播放的歌曲切换成列表的下一首/上一首,并且将所有的效果全部更换成下一首/上一首的(包括歌名、动图,歌词等等),如果此时时暂停歌曲的状态,点击下一首会直接跳转成播放下一首的状态,各图标随之改变成对应状态。这两个按钮同样具有鼠标的悬停以及按压的特殊效果,以及悬停时的文字提示功能(这里作补充解释:我将歌曲定义在列表中,每个歌曲都有对于的编号,实现上一首/下一首的实际就是将编号加一或者减一,并且模歌曲总数量)
(2)具体代码实现(这里仅展示下一首的代码,上一首按钮代码类似):
buttonRight = new JButton();
buttonRight.setToolTipText("下一首");
buttonRight.setBounds(300, 615, 67, 51);
Icon icon_right = new ImageIcon(".//right.png");
buttonRight.setIcon(icon_right);
buttonRight.setBorder(BorderFactory.createEmptyBorder());
buttonRight.setFocusPainted(false);
buttonRight.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
// 鼠标进入按钮时的效果
buttonRight.setBorder(BorderFactory.createLineBorder(Color.WHITE, 2));
}
@Override
public void mouseExited(MouseEvent e) {
// 鼠标离开按钮时的效果恢复到默认状态
buttonRight.setBorder(BorderFactory.createEmptyBorder());
}
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
buttonRight.setBorder(BorderFactory.createLineBorder(Color.GRAY, 2));
}
@Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
buttonRight.setBorder(BorderFactory.createLineBorder(Color.WHITE, 2));
choose++;
if(choose==4) choose=1;
audioplay.SetPlayAudioPath("./"+SongName[choose]);
remove(buttonPlay);
add(blank);
remove(GIF[0]);
remove(GIF[1]);
remove(GIF[2]);
revalidate();
repaint();
textName.setText(""+SongName[choose].substring(0,SongName[choose].length()-4));
add(GIF[choose-1]);
add(buttonStop);
audioplay.play(0);
}
});
add(buttonRight);
2.4歌曲列表按钮
(1) 实现功能:能另外再创建一个窗口,并且悬停在原窗口上方,在此窗口展示期间不允许对原窗口进行任何操作。窗口内展示歌曲列表,我们可以单击歌曲列表,从而选中某一首歌,此时是选中,不播放,可以在主界面中手动点击播放按钮从而实现播放,也可以双击列表,即播放该歌曲。
(2)具体代码实现:
if (e.getSource() == buttonList) {//如果是打开文件按钮点击事件
JDialog floatingWindow = new JDialog(this, "歌曲选择", true);
floatingWindow.setSize(300, 200);
floatingWindow.setLocationRelativeTo(this);
JPanel contentPane = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
// 绘制背景图片
super.paintComponent(g);
ImageIcon backgroundImage = new ImageIcon("choice_ground.jpg");
g.drawImage(backgroundImage.getImage(), 0, 0, getWidth(), getHeight(), null);
}
};
floatingWindow.setContentPane(contentPane);
// 创建一个列表模型
DefaultListModel<String> listModel = new DefaultListModel<>();
listModel.addElement("飘洋过海 00:53");
listModel.addElement("山外小楼夜听雨 01:40");
listModel.addElement("我和我的祖国 00:59");
// 创建列表,并设置选择监听器
JList<String> songList = new JList<>(listModel);
songList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
songList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
// 处理列表项选择事件
choose=songList.getSelectedIndex()+1;
String selectedSong = SongName[choose];
audioplay.SetPlayAudioPath("./"+selectedSong);
textName.setText(""+SongName[choose].substring(0,SongName[choose].length()-4));
add(buttonPlay);
remove(blank);
remove(buttonStop);
remove(GIF[0]);
remove(GIF[1]);
remove(GIF[2]);
progressBar.setValue(0);
revalidate();
repaint();
}
}
});
songList.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
if(e.getClickCount()==2){
textName.setText(""+SongName[choose].substring(0,SongName[choose].length()-4));
add(buttonStop);
remove(buttonPlay);
remove(GIF[0]);
remove(GIF[1]);
remove(GIF[2]);
revalidate();
repaint();
add(GIF[choose-1]);
add(blank);
audioplay.play(0);
}
}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
});//增加双击功能
songList.setOpaque(false);
// 将列表放置在滚动面板中,并将滚动面板添加到悬浮窗口中
JScrollPane scrollPane = new JScrollPane(songList);
//scrollPane.setOpaque(false);
floatingWindow.getContentPane().add(scrollPane, BorderLayout.CENTER);
floatingWindow.setVisible(true);
}
2.5进度条按钮(可交互)
(1) 实现功能:能另外再创建一个窗口,并且悬停在原窗口上方,在此窗口展示期间不允许对原窗口进行任何操作。窗口内展示歌曲列表,我们可以单击歌曲列表,从而选中某一首歌,此时是选中,不播放,可以在主界面中手动点击播放按钮从而实现播放,也可以双击列表,即播放该歌曲。
(2)具体代码实现:
(第一部分:通过歌曲时间控制进度条,注:该部分代码放在定时器函数体内部)
double p = (double) audioplay.getFramePosition() / audioplay.GetTotaltime();
if(audioplay.getFramePosition()>0){
nPlayTime = (int) (p*100);
progressBar.setValue(nPlayTime);
(第二部分:通过点击进度条从而达到控制歌曲进度的目的)
progressBar=new JProgressBar();
progressBar.setMaximum(100);
progressBar.setMinimum(0);
progressBar.setBounds(50,565,388,6);
progressBar.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
int mouseX=e.getX();
int progressBarWidth=progressBar.getWidth();
int progress=(int)((double)mouseX/progressBarWidth*progressBar.getMaximum());
progressBar.setValue(progress);
long time=(long)((double)mouseX/progressBarWidth*audioplay.GetTotaltime());
audioplay.SetTime(time);
}
});
progressBar.setBackground(Color.GRAY);
progressBar.setForeground(Color.WHITE);
progressBar.setBorder(BorderFactory.createEmptyBorder());
add(progressBar);
2.6歌词模块
(1) 实现功能:能实现在特定的区域展示每首歌曲带来的歌词,并且歌词会随着歌曲的变换而实现对应的变换,这里还要求实现歌词能随着歌曲的播放实现对应歌词高亮显示,这里我还实现了字体的一个放大的效果。
(2)具体代码实现:
(这里的歌词展示也是在定时器函数体内实现的,使用多个for循环输出歌词,这里歌词是在代码内部定义的,何时跳转也是在代码内部定义,这里可以改进为直接从歌曲文件中读入,通过时间戳文件来进行跳转)
if(audioplay.getFramePosition()>0){
textLyrics.setText("");
try {
// 添加文本,并设置样式
int light=0;
while(breakTime[choose][light]<second){
if(breakTime[choose][light+1]==-1) break;
light++;
}
for(int i=0;i<light;i++){
doc.insertString(doc.getLength(), sLyric[choose][i], style);
}
if(light<sLyric[choose].length) doc.insertString(doc.getLength(), sLyric[choose][light], style2);
light++;
for(;light<sLyric[choose].length;light++){
doc.insertString(doc.getLength(), sLyric[choose][light], style);
}
} catch (BadLocationException e) {
e.printStackTrace();
}
}//歌词设计
2.7开场动画模块
(1) 实现功能:运行程序之初播放一段视频,并且在一定的时间(我这里设置的是3s)后自动结束并且正常运行程序。
(2)具体代码实现:
(这里的begin组件就是一个JLabel组件,并且有动图)
add(Begin);
setVisible(true);
try {
Thread.sleep(3000);
}catch (InterruptedException e){
e.printStackTrace();
}
remove(Begin);
2.8播放歌曲GIF图模块
(1) 实现功能:能够在播放歌曲的时候有GIF动图展示,并且在切换歌曲是动图也会随之切换。
(2)具体代码实现:
(这里是为了利用数组下标,定义一个GIF数组)
Icon icon_gif3=new ImageIcon(".//GIF2.gif");
Icon icon_gif1=new ImageIcon(".//GIF6.gif");
Icon icon_gif2=new ImageIcon(".//GIF5.gif");
GIF[0]=new JLabel(icon_gif1);
GIF[0].setBounds(98,122,290,290);
GIF[1]=new JLabel(icon_gif2);
GIF[1].setBounds(98,122,290,290);
GIF[2]=new JLabel(icon_gif3);
GIF[2].setBounds(98,122,290,290);
(注:这里没有列出具体的切换逻辑,具体的逻辑体现在具体的按钮功能模块代码中)
2.9播放时间动态展示模块
(1) 实现功能:能够随着歌曲的播放,即时的展示播放时间,以及剩余时间
(2)具体代码实现:
int second=(int)audioplay.getFramePosition()/1000000;
int rest=(int)audioplay.GetTotaltime()/1000000-second;
if(audioplay.getIsRunning()&&audioplay.getFramePosition()>0){
String time1="0"+second/60+":";
String time2="-0"+rest/60+":";
if(second%60<10) time1+="0";
if(rest%60<10) time2+="0";
time1=time1+second%60;
time2=time2+rest%60;
textTime1.setText(time1);
textTime2.setText(time2);
}//时间组件设置
3.效果展示
3.1开场动画效果展示
(这里其实是设置一个动图,所以在实际的展示效果中使用动态效果的)
3.2未播放任何歌曲时的展示
3.3歌曲列表展示
3.4按钮效果展示
(这里展示的是当鼠标悬停在按钮上时按钮的效果,按下按钮时按钮边框会变灰,释放时恢复)
3.5播放效果展示
(图示中左边所展示图片是一个动图,当在播放歌曲时未在播放字样消失,出现歌曲名称,此时的进度条是可交互的,还有时间也会随着进度条或者歌曲的变化而变化,左侧的是已播放时间,右侧的是剩余时间)