如何使用swing创建一个BeatBox

编程爱好者联盟 2017-01-04

首先,我们需要回顾一些内容(2017-01-04 14:32:14):

       1.Swing组件

      Swing的组件(component,或者称之为元件),是较widget更为正确的术语,它们就是会放在GUI(Graphical User Interface)上面的东西,这些内容就是一些用户会看到并且与其交互的,如Text Field、Button、scrollable、list、radio button等,这些组件均是继承自java.swing.JComponent;

      在Swing中,几乎所有的组件都是安置到其他组件当中。

     2.创建GUI的四个步骤:

  • 创建window(JFrame)

 1 JFrame frame=new JFrame(); 

  • 创建组件

1 JButton button=new JButton(“click me”);

  • 将组件添加到frame上

1 frame.getContentPane.add(BorderLayout.EAST,button);

  • 显示出来

 1 frame.setSize(300,300);

2 frame.setVisible(true); 

3.布局管理器(layout Managers)

首先,布局管理器是与特定组件相关联的java对象,他大多数是背景组件。

其次,布局管理器是用来控制所关联组件上携带的其他组件,换言之,如果某个框架带有面板,面板上有按钮的情况下,则面板布局管理器就是控制按钮的大小与位置,而框架的布局管理器是控制着面板的大小与位置。

将对应的按键添加到对应的面板上,可以如下实现:

 1 JPanel jpanel=newJPanel();   

2 JButton button=newJButton(“click me”);

3 jpanel.add(button); 

4.三大首席管理器:

  • border
  • flow
  • box

    1)BorderLayout:该管理器会将组建分割成5个区域,每个区域只能放置一个组件,由此管理员安置的组件不会取得默认的大小。这个也是框架默认得布局管理器;

    2)FlowLayout:该管理器的行为与文书处理程序的版面配置方式差不多。这个组件会依照理想的大小呈现,并且胡依照从左到右,依次加入的顺序(中间可能会换行)排列,因此如果组建放不下一行的时候会自动换行。这个是面板默认得布局管理器。

   3)BoxLayout:它就像FlowLayout一样让每个组件按照默认得大小,依次按照加入的顺序进行排列,它是以垂直的方式排列(也可以水平,但是通常我们只关心垂直的方式)。与FlowLayout不 同的是,它是需要插入某种换行的机制来强制组件从新的一列进行排列。

    接下来我们看一下干货:    

3 import java.awt.BorderLayout;
  4 import java.awt.GridLayout;
  5 import java.awt.Label;
  6 import java.awt.event.ActionEvent;
  7 import java.awt.event.ActionListener;
  8 import java.util.ArrayList;
  9 
 10 import javax.sound.midi.*;
 11 import javax.swing.*;
 12 
 13 public class BeatBox {
 14     JPanel mainJPanel;
 15     ArrayList<JCheckBox> checkboxslist;
 16     Sequencer sequencer;
 17     Sequence sequence;
 18     Track track;
 19     JFrame theFrame;
 20     String[]instrumentNames={
 21             "Bass Drum","closed Hi-Hat","open Hi-Hat","Acoustic Snare",
 22             "Crash Cymbal","Hand Clap","High Tome","Hi Bongo","Maracas",
 23             "Whistal","Low Conga","Cowbell","Vibraslap","Low_mid Tom",
 24             "High Agogo","open High Coga"
 25     };//乐器的名称,用String的array维护
 26     int []instrument={35,42,46,38,49,39,50,60,70,72,64,56,58,47,67,63};//实际乐器的关键字,比如说35是Bass Drum,42是closed Hi-Hat
 27     public static void main(String[] args) {
 28         // TODO Auto-generated method stub
 29        new BeatBox().buildGUI();
 30     }
 31     public void buildGUI(){
 32         theFrame = new JFrame("Cyber BeatBox");
 33         theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗口关闭时关闭程序
 34         BorderLayout layout = new BorderLayout();//定义了一个BorderLayout面板对象
 35         JPanel background=new JPanel(layout);//将面板对象实例化JPanel对象
 36         background.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));//用于设置边缘空白大小
 37         
 38         checkboxslist=new ArrayList<JCheckBox>();
 39         Box buttonBox = new Box(BoxLayout.Y_AXIS);        
 40         
 41         JButton start = new JButton("start");
 42         start.addActionListener(new MyStartlistener());
 43         buttonBox.add(start);
 44         
 45         JButton stop = new JButton("stop");
 46         start.addActionListener(new MyStoplistener());
 47         buttonBox.add(stop);
 48         
 49         JButton upTempo = new JButton("Tempo up");
 50         start.addActionListener(new MyupTempolistener());
 51         buttonBox.add(upTempo);
 52         
 53         JButton downTempo = new JButton("Tempo down");
 54         start.addActionListener(new MydownTempolistener());
 55         buttonBox.add(downTempo);
 56         
 57         Box nameBox=new Box(BoxLayout.Y_AXIS);
 58         for(int i=0;i<16;i++){
 59             nameBox.add(new Label(instrumentNames[i]));
 60         }
 61         
 62         background.add(BorderLayout.EAST,buttonBox);
 63         background.add(BorderLayout.WEST,nameBox);
 64         
 65         theFrame.getContentPane().add(background);
 66         
 67         GridLayout gridLayout=new GridLayout(16,16);//创建具有指定行数和列数的网格布局。 布局中的所有组件都具有相等的大小。
 68                                            //行和列中的一个(但不是两者)可以为零,这意味着任何数量的对象都可以放置在一行或一列中。
 69         gridLayout.setVgap(1);//将组件之间的垂直间距设置为指定的值。
 70         gridLayout.setHgap(2);//将组件之间的水平间距设置为指定的值。
 71         mainJPanel=new JPanel(gridLayout);
 72         background.add(BorderLayout.CENTER,mainJPanel);
 73         
 74         for(int i=0;i<256;i++){
 75             JCheckBox checkBox=new JCheckBox();
 76             checkBox.setSelected(false);
 77             checkboxslist.add(checkBox);
 78             mainJPanel.add(checkBox);
 79         }//结束循环
 80         //创建checkBox组,并将其设定成未勾选的false,并添加到arraylist以及面板上
 81         
 82         setUpMidi();
 83         
 84         theFrame.setBounds(50, 50, 300, 300);
 85         theFrame.pack();//pack()函数:使此窗口的大小适合其子组件的首选大小和布局。 如果任一维度小于由上次调用setMinimumSize方法指定的最小大小,则会自动放大窗口的最终宽度和高度。
 86                           //如果窗口和/或其所有者不可显示,则在计算优选大小之前使它们两者都可显示。 窗口在计算其大小后生效。
 87         theFrame.setVisible(true);
 88     }//关闭buildGUI()方法
 89     
 90     public void setUpMidi(){
 91         try{
 92             
 93             sequencer= MidiSystem.getSequencer();//此方法等效于调用getSequencer(true) 创建sequencer
 94             sequencer.open();
 95             //创建并打开队列
 96             
 97             sequence=new Sequence(Sequence.PPQ, 4);
 98             track=sequence.createTrack();
 99             //创建队列并track
100             
101             sequencer.setTempoInBPM(120);
102             
103         }catch(Exception e){
104             e.printStackTrace();
105         }
106     }//关闭 setUpMidi()方法
107     
108     public void buildTrackAndStart(){
109         
110         int []trackList=null;//创建出16个元素的数组来存储一项乐器值,如果有该演奏,其值就将会是关键字值,否则将值为零
111         
112         sequence.deleteTrack(track);        
113         track=sequence.createTrack();//清除旧的track,做一个新的
114         
115         for (int i = 0; i < 16; i++) {//每个乐器执行一次
116             trackList=new int[16];
117             
118             int key=instrument[i];//设代表乐器的关键字
119             
120             for(int j = 0;j<16;j++){//每一拍执行一次
121                 JCheckBox jc=(JCheckBox)checkboxslist.get(j+16*i);
122                 if(jc.isSelected()){//如果勾选,那么关键字的值放到数组得该位置上,不然的话,就补零
123                     trackList[j]=key;
124                 }else{
125                     trackList[j]=0;
126                 }
127             }//关闭循环
128             
129             makeTracks(trackList);
130             track.add(makeEvent(176,1,127,0,16));//创建此乐器事件,并添加到track上;
131         }//关闭外循环
132         
133         
134         track.add(makeEvent(192,9,1,0,15));
135         
136         try{
137             sequencer.setSequence(sequence);
138             sequencer.setLoopCount(sequencer.LOOP_CONTINUOUSLY);
139             sequencer.start();
140             sequencer.setTempoInBPM(120);//以每分钟的拍数设置速度。 播放的实际速度是指定值和速度因子的乘积。
141         }catch(Exception e){
142             e.printStackTrace();
143         }
144     }//结束buildTrackAndStart方法
145     
146     public class MyStartlistener implements ActionListener{
147         //按钮的监听者
148         @Override
149         public void actionPerformed(ActionEvent e) {
150             // TODO Auto-generated method stub
151             buildTrackAndStart();
152         }
153         
154     }//内部类关闭
155     public class MyStoplistener implements ActionListener{
156         //按钮的监听者
157         @Override
158         public void actionPerformed(ActionEvent e) {
159             // TODO Auto-generated method stub
160             sequencer.stop();
161         }
162         
163     }//内部类关闭
164     public class MyupTempolistener implements ActionListener{
165         //按钮的监听者
166         @Override
167         public void actionPerformed(ActionEvent e) {
168             // TODO Auto-generated method stub
169             float tempoFactor=sequencer.getTempoFactor();
170             sequencer.setTempoFactor((float)(tempoFactor*1.03));//节奏因子,预设为1.0,每次调整3%
171         }
172         
173     }//内部类关闭
174     public class MydownTempolistener implements ActionListener{
175         //按钮的监听者
176         @Override
177         public void actionPerformed(ActionEvent e) {
178             // TODO Auto-generated method stub
179             float tempoFactor=sequencer.getTempoFactor();
180             sequencer.setTempoFactor((float)(tempoFactor*.97));//节奏因子,预设为1.0,每次调整3%
181         }
182         
183     }//内部类关闭
184     public void makeTracks(int []list){
185         
186         for (int i = 0; i < 16; i++) {
187             int key=list[i];
188             if (key!=0) {
189                 track.add(makeEvent(144,9,key,100,i));
190                 track.add(makeEvent(128,9,key,100,i+1));//创建Note on以及Note off,并添加到track上
191             }
192         }
193     }//关闭makeTracks方法
194     public MidiEvent makeEvent(int comd,int chan,int one,int two,int tick){
195          MidiEvent event=null;
196          try{
197              ShortMessage shortMessage=new ShortMessage();//创建消息实例
198              shortMessage.setMessage(comd, chan, one, two);//调用setMessage
199              event=new MidiEvent(shortMessage, tick);//制作消息的MidiEvent实例
200          }catch(Exception e){
201              e.printStackTrace();
202          }
203         return event;
204     }//关闭makeEvent方法,该方法是制作消息以及事件
205 }

   对应的界面呈现为:

DbhVozhcEZvQGMfR6Pkc-A33RMAoVqH3kdctdDxLPixW0cU_9iHLN2rZZq5x8-UpP1RJnTc5AigO6JzD9HhfOca1PV4T5RcrsIdgPeP4e5X6FLTz6cmUaQMezaF7-iie.png

问:框架为什么不能像面板那样直接加上组件呢?

      答:JFrame会这么特殊是因为它是让无显示在画面上的接点。

      因为Swing的组件纯粹是由Java构成的,JFrame必须要连接到底层的操作系统以便于存储显示装置。我们可以把面板想做是安置在JFrame上的100%的java层,或者把JFrame想作是支撑面板的框架,他甚至可以使用 自定义的方式换掉框架的面板: 

nyframe.setContentpane(myPanel);

相关推荐