如何使用swing创建一个BeatBox
首先,我们需要回顾一些内容(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 }
对应的界面呈现为:
问:框架为什么不能像面板那样直接加上组件呢?
答:JFrame会这么特殊是因为它是让无显示在画面上的接点。
因为Swing的组件纯粹是由Java构成的,JFrame必须要连接到底层的操作系统以便于存储显示装置。我们可以把面板想做是安置在JFrame上的100%的java层,或者把JFrame想作是支撑面板的框架,他甚至可以使用 自定义的方式换掉框架的面板:
nyframe.setContentpane(myPanel);