黑马程序员技术交流社区

标题: 论音乐播放器的正确编写姿势 [打印本页]

作者: 龙骑将杨影枫    时间: 2014-12-8 19:48
标题: 论音乐播放器的正确编写姿势

因为某种原因,我想动手编写一个音乐播放器。
确定目标了,就先找一个模仿的例子。当然我是一个很懒的人,酷狗用了许多年,决定就是你了

参照物有了,下面动手编程。
首先,我需要一个播放音乐的模块。
很久以前也曾经写过一个,准确的说是一个音乐闹钟。然后当我把其中的播放音乐的模块分离出来以后,赫然发现居然不能用了。以前我用的是Applet.newAudioClip(url)方法创建一个AudioCip类,然后调用其play()函数。现在不论我怎么写,都音乐都未曾响起。我猜测或许是因为换了最新的jdk或者是换了系统的缘故。Anyway,只好换其他方式。
这个其他方式,就是所谓的JMF。
当然除了JMF之外,我知道还有一种方式可以播放音乐,就是写一个音乐流读音乐文件(仅限于理论上 未曾实践)。但懒惰如我,既然别人已经封装好框架了,那为啥不拿来用呢?
从CSDN上下载了一个JMF包,分享资料的这位大哥真是热心人。不但0积分下载,还在下载包里附带了入门教程和项目实例。雷锋啊!
稍微研究了一下,动手编写了一个Music类,专门播放音乐。
  1. package tiantian;

  2. import java.io.IOException;
  3. import java.net.URL;
  4. import javax.media.Manager;
  5. import javax.media.NoPlayerException;
  6. import javax.media.Player;
  7. import javax.swing.JOptionPane;

  8. public class Music {

  9.     private Player player=null;
  10.     private URL url=null;
  11.     public Music(URL url){
  12.         this.url=url;
  13.     }
  14.    
  15.     public void initPlayer(){
  16.         if(this.url!=null)
  17.             try {
  18.                 player=Manager.createPlayer(this.url);
  19.             } catch (NoPlayerException e) {
  20.                 // TODO Auto-generated catch block
  21.                 e.printStackTrace();
  22.             } catch (IOException e) {
  23.                 // TODO Auto-generated catch block
  24.                 e.printStackTrace();
  25.             }
  26.     }

  27.     public URL getUrl() {
  28.         return url;
  29.     }

  30.     public void setUrl(URL url) {
  31.         this.url = url;
  32.     }

  33.     public void play(){
  34.         if(player==null)
  35.             JOptionPane.showMessageDialog(null, "请选中音乐再播放!", "出错啦!", JOptionPane.ERROR_MESSAGE);
  36.         else
  37.             player.start();
  38.     }
  39.     public void stop(){
  40.         if(player==null)
  41.             JOptionPane.showMessageDialog(null, "请选中音乐再停止!", "出错啦!", JOptionPane.ERROR_MESSAGE);
  42.         else{
  43.             player.stop();
  44.             player.close();
  45.             }
  46.     }
  47. }
复制代码

然后动手编写测试类Demo
  1. package tiantian;

  2. import java.io.File;
  3. import java.net.MalformedURLException;

  4. public class Demo {

  5.         public static void main(String[] args) throws MalformedURLException  {
  6.                 File file=new File("E:\\test.mp3");
  7.                 Music music=new Music(file.toURL());
  8.                 music.initPlayer();
  9.                 music.play();
  10.         }

  11. }
复制代码
运行成功。






作者: 龙骑将杨影枫    时间: 2014-12-8 20:17
当然,比较起我选定的界面而言,还是有不小差距的。
最关键的一点是,人家的歌是可以随便选的。我播放的音乐,是定死的。
不爽,于是继续优化。
首先是手动选歌的问题,写死是自寻死路,用手敲路径也是自寻死路(我自问就没那个耐心一首首的去敲),思来想去,还是要用图形化界面。
于是想到了JFileChooser类。
JFileChooser类自然是要连接到按钮上,一点就是打开文件。然而1个按钮是不够的,还需要至少3个:播放、停止、删除。而且还需要1个状态栏去显示正在播放的文件。
于是动手编写代码。
  1. package tiantian;

  2. import java.applet.Applet;
  3. import java.awt.Color;
  4. import java.awt.GridBagConstraints;
  5. import java.awt.GridBagLayout;
  6. import java.awt.GridLayout;
  7. import java.awt.event.ActionEvent;
  8. import java.awt.event.ActionListener;
  9. import java.io.File;
  10. import java.io.IOException;
  11. import java.net.MalformedURLException;

  12. import javax.media.Manager;
  13. import javax.media.NoPlayerException;
  14. import javax.media.Player;
  15. import javax.swing.JButton;
  16. import javax.swing.JFileChooser;
  17. import javax.swing.JLabel;
  18. import javax.swing.JOptionPane;
  19. import javax.swing.JPanel;
  20. import javax.swing.JTextField;
  21. import javax.swing.UIManager;
  22. import javax.swing.UnsupportedLookAndFeelException;

  23. public class MusicPanel extends JPanel implements ActionListener{
  24.         private File file;//播放文件
  25.         private JLabel jfStatus;//状态栏
  26.         private JButton jbPlay;//播放按钮
  27.         private JButton jbStop;//停止按钮
  28.         private JButton jbOpen;//打开文件按钮
  29.         private JButton jbClose;//关闭文件按钮
  30.         private Music music;//音乐播放类
  31.         public MusicPanel() throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException{
  32.                 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());//将界面转化成windows风格
  33. //                UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");
  34. //                UIManager.setLookAndFeel("com.apple.mrj.swing.MacLookAndFeel");
  35.                 jfStatus=new JLabel("未选择文件");//状态栏初始化
  36.                 jbPlay=new JButton("播放");//四大按钮初始化,并添加监听
  37.                 jbPlay.addActionListener(this);
  38.                 jbStop=new JButton("停止");
  39.                 jbStop.addActionListener(this);
  40.                 jbOpen=new JButton("打开");
  41.                 jbOpen.addActionListener(this);
  42.                 jbClose=new JButton("关闭");
  43.                 jbClose.addActionListener(this);
  44.                
  45.                
  46.                 //panel界面初始化
  47.                 this.setSize(360, 100);
  48.                 this.setVisible(true);
  49.                 //布局初始化
  50.                 //思来想去我决定先用GridBagLayout,虽然很复杂,但是很精确。
  51.                 GridBagLayout gbl=new GridBagLayout();
  52.                 gbl.columnWidths=new int[]{7,7,7,7,7,7,7,7};
  53.                 gbl.rowHeights=new int[]{7,7,7,7,7,7,7,7};
  54.                 this.setLayout(gbl);

  55.                 GridBagConstraints gbc0=new GridBagConstraints();
  56.                 gbc0.weighty = 0.1;
  57.                 gbc0.weightx = 0.1;
  58.                 gbc0.gridy = 1;
  59.                 gbc0.gridx = 1;
  60.                 gbc0.gridwidth=8;
  61.                 gbc0.fill = GridBagConstraints.HORIZONTAL;
  62.                 gbc0.anchor = GridBagConstraints.CENTER;
  63.                 this.add(jfStatus,gbc0);
  64.                
  65.                 GridBagConstraints gbc1=new GridBagConstraints();
  66.                 gbc1.weighty = 0.1;
  67.                 gbc1.weightx = 0.1;
  68.                 gbc1.gridy = 2;
  69.                 gbc1.gridx = 1;
  70.                 gbc1.fill = GridBagConstraints.HORIZONTAL;
  71.                 gbc1.anchor = GridBagConstraints.CENTER;
  72.                 this.add(jbOpen,gbc1);
  73.                
  74.                 GridBagConstraints gbc2=new GridBagConstraints();
  75.                 gbc2.weighty = 0.1;
  76.                 gbc2.weightx = 0.1;
  77.                 gbc2.gridy = 2;
  78.                 gbc2.gridx = 3;
  79.                 gbc2.fill = GridBagConstraints.HORIZONTAL;
  80.                 gbc2.anchor = GridBagConstraints.CENTER;
  81.                 this.add(jbClose,gbc2);

  82.                 GridBagConstraints gbc3=new GridBagConstraints();
  83.                 gbc3.weighty = 0.1;
  84.                 gbc3.weightx = 0.1;
  85.                 gbc3.gridy = 2;
  86.                 gbc3.gridx = 5;
  87.                 gbc3.fill = GridBagConstraints.HORIZONTAL;
  88.                 gbc3.anchor = GridBagConstraints.CENTER;
  89.                 this.add(jbPlay,gbc3);
  90.                
  91.                 GridBagConstraints gbc4=new GridBagConstraints();
  92.                 gbc4.weighty = 0.1;
  93.                 gbc4.weightx = 0.1;
  94.                 gbc4.gridy = 2;
  95.                 gbc4.gridx = 7;
  96.                 gbc4.fill = GridBagConstraints.HORIZONTAL;
  97.                 gbc4.anchor = GridBagConstraints.CENTER;
  98.                 this.add(jbStop,gbc4);
  99.                
  100.         }

  101.         @Override
  102.         public void actionPerformed(ActionEvent e) {
  103.                 if(e.getSource()==jbPlay){
  104.                         this.play();
  105.                 }
  106.                 if(e.getSource()==jbStop){
  107.                         this.stop();
  108.                 }
  109.                 if(e.getSource()==jbOpen){
  110.                         this.openFile();
  111.                 }
  112.                 if(e.getSource()==jbClose){
  113.                         this.closeFile();
  114.                 }
  115.         }

  116.         //关闭文件
  117.         private void closeFile() {
  118.                 this.music=null;
  119.                 this.jfStatus.setText("未选择文件");
  120.         }

  121.         //打开文件
  122.         private void openFile() {
  123.                 JFileChooser jfc=new JFileChooser();
  124.                 if(jfc.showOpenDialog(this)==JFileChooser.APPROVE_OPTION){
  125.                         this.file=jfc.getSelectedFile();
  126.                         jfStatus.setText(file.getName());
  127.                 }
  128.         }

  129.         //停止
  130.         private void stop() {
  131.                         music.stop();
  132.         }

  133.         //播放
  134.         private void play() {
  135.                         try {
  136.                                 this.music=new Music(file.toURL());
  137.                         } catch (MalformedURLException e) {
  138.                                 e.printStackTrace();
  139.                         }
  140.                         this.music.initPlayer();
  141.                         this.music.play();
  142.         }
  143.        
  144. }
复制代码

然后动手编写测试代码。
  1. package tiantian;

  2. import java.net.MalformedURLException;

  3. import javax.swing.JFrame;
  4. import javax.swing.UnsupportedLookAndFeelException;

  5. public class Demo {

  6.         public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException  {
  7.                 MusicPanel mp=new MusicPanel();
  8.                 JFrame jf=new JFrame();
  9.                 jf.add(mp);
  10.                 jf.pack();
  11.                 jf.setVisible(true);
  12.                 jf.setLocationRelativeTo(null);
  13.         }

  14. }
复制代码
运行效果









作者: 水竹    时间: 2014-12-8 23:18
一个帖子赚了八分,好强大……
作者: 龙骑将杨影枫    时间: 2014-12-9 00:14
当然,运行起来依然比较挫,虽然是能用,但远远达不到实用的角度。
现在基础的功能有了,关键是长的不够好看。
而之所以长的不好看,是因为界面颜色比较单调。


好吧,是丑了点。虽然进入到这里,已经超出我的知识领域。不过没关系,边学边优化。
首先优化背景颜色,这个比较简单。直接调用JPanel类的.setBackground(new Color(int x,int y,int z));就可以。
具体来说,在构造函数里增加        this.setBackground(new Color(96,190,228));//背景颜色 语句。
当然,状态栏jlStatus里的字体颜色也要改,字体类型嘛暂时不用动。
于是,在构造函数里增加        jlStatus.setForeground(new Color(255,255,255));
字体颜色优化完,下面优化按钮。
老实说我以前虽然用过jbutton,但是基本都是用的JButton("")这种构造方法,没用过图标。不过没用见过猪肉,好歹见过猪跑。度娘过后,扒拉出来这么一段语句。
    ImageIcon icon = new ImageIcon("image\\play.png");
    jbPlay=new JButton(icon);//四大按钮初始化,并添加监听
play.png是我从网上找的素材后用ps修剪过的按钮图标,放在项目下专门建立的文件夹image中。我打算将所有音乐器相关的图片都放在这里(归类是个好习惯)。


当然,仅仅是加上图片还不够,还要调整图片的填充格式。
在构造方法里jbPlay后增加
        jbPlay.setContentAreaFilled(false);//是否填充空白区域
        jbPlay.setFocusPainted(false); //焦点区域是否显示
试运行。

有点意思了。
优化到这里,按钮的静态墨阳已经优化的差不多了。但是呢,经过对比发现一个比较严重的问题。
酷狗的按钮,鼠标移上去,是这个样子 ,按下去,是这个样子 ,有所差别。
对比起来,我的播放器无论是移上去还是按下去,都是一个样子。
于是继续优化。
鼠标移上去还是按下去,很明显是监听的鼠标动作。于是将类声明修改为
public class MusicPanel extends JPanel implements ActionListener,MouseListener,增加MouseListener接口的方法,在其中加入图标改变的语句。
  1.         @Override
  2.         public void mouseClicked(MouseEvent e) {
  3.                 // TODO Auto-generated method stub
  4.                
  5.         }

  6.         @Override
  7.         public void mouseEntered(MouseEvent e) {
  8.                 ImageIcon icon = new ImageIcon("image\\play_over.png");
  9.                 this.jbPlay.setIcon(icon);
  10.         }

  11.         @Override
  12.         public void mouseExited(MouseEvent e) {
  13.                 ImageIcon icon = new ImageIcon("image\\play.png");
  14.                 this.jbPlay.setIcon(icon);
  15.         }

  16.         @Override
  17.         public void mousePressed(MouseEvent e) {
  18.                 ImageIcon icon = new ImageIcon("image\\play_pressed.png");
  19.                 this.jbPlay.setIcon(icon);
  20.                
  21.         }

  22.         @Override
  23.         public void mouseReleased(MouseEvent e) {
  24.                 ImageIcon icon = new ImageIcon("image\\play.png");
  25.                 this.jbPlay.setIcon(icon);
  26.                
  27.         }
复制代码
于是我的播放器也变成了移过去 和按下去
朕心甚慰。







作者: 李贵栋    时间: 2014-12-9 10:15
厉害啊   




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2