長時間禁用UI 主執行緒
在UI 中, 若每隔一最時間就要更新, 比如計時器, 就千萬別在UI主執行緒執行迴圈, 否則整個UI就會被卡住
非UI執行緒更新畫面
在Java中, 是可以在別的執行緒使用setText來更新UI, 也保証是安全的. 但這也僅限於JLabel而以, 且官方文件並沒有說明, 那些是安全的, 那些是不安全的. 再加上Andorid, C#都是禁止非UI主行緒進行畫面更新, 所以並不建議此法.
SwingUtilities invokeLater
此法是產生一個執行緒, 稱為UIThread, 畫面的更新放在run裏面, 但我們並不使用 start()產生新執行緒來更新. 而是將此執行緒放在SwingUtilities.invokeLater()裏面. 待UI主執行緒有空時, 使用UI主執行緒來執行. 此法是最正確的作法. 程式碼如下
private void btnActionPerformed(java.awt.event.ActionEvent evt) {
new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {Thread.sleep(10);} catch (InterruptedException ex) {}
SwingUtilities.invokeLater(new UiThread(timer++));
}
}
}).start();
}
class UiThread extends Thread{
int value;
public UiThread(int v){
value=v;
}
public void run(){
lbl.setText(String.valueOf(value));
}
}
Lambda
當然, 如果懂得Lambda的話, 上面藍色的部份就可以改成如下, 然後 class UIThread就可以整個拿掉.
SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss:SSS"); boolean runFlag=false; private void btnActionPerformed(java.awt.event.ActionEvent evt) { runFlag=!runFlag; new Thread(()->{ while(runFlag){ try {Thread.sleep(1);} catch (InterruptedException ex) {} SwingUtilities.invokeLater(()->lbl.setText(sdf.format(new Date()))); } }).start(); }
JPanel繪圖
在Java Swing中簡易繪圖,可以直接把JPanel當成畫布來使用。上圖視窗中加入一個JPanel, 命名為canvas. 然後再加入一顆按鈕btn。
canvas的中心點(ox, oy)的取得,可以由(canvas.getWidth()/2, canvas.getHeight()/2)取得。
JPanel繪圖,需先使用canvas.getGraphics()取得 Graphics物件 g。然後再由 g 進行繪圖,如
g.drawLine(), g.drawCircle()…等方法
private void btnActionPerformed(java.awt.event.ActionEvent evt) { int x, y, px, py, ox, oy; int w=canvas.getWidth(); int h=canvas.getHeight(); ox=w/2;//中心點x座標 oy=h/2;//中心點y座標 int n=100;//要畫的點數 double radius=50;//半徑 Graphics painter=canvas.getGraphics(); //畫x, y軸 painter.setColor(Color.RED); painter.drawLine(0,h/2, w, h/2); painter.drawLine(w/2, 0, w/2, h); painter.setColor(Color.BLUE); //畫圓型 for (int j=0;j<10;j++){ radius=radius+20; px=(int)radius+ox;py=oy; int r=(int)(Math.random()*256); int g=(int)(Math.random()*256); int b=(int)(Math.random()*256); for (int i=0;i<=n;i++){ double angle=i*360.0/n; painter.setColor(new Color(r, g, b)); x=(int)(radius*Math.cos(Math.PI/180.0*angle)+ox); y=(int)(radius*Math.sin(Math.PI/180.0*angle)+oy); /* painter.drawOval(x, y, 40,20); //painter.drawLine(px, py, x, y); */ Graphics2D p=(Graphics2D)painter; Ellipse2D c=new Ellipse2D.Double(px, py, 5, 5); p.fill(c); px=x;py=y; } } }
paint方法
JPanel在視窗成形,resize,或外部update時,就會執行paint()方法。所以如果希望上述在移動視窗時,圖形還是保留著,那就就要把上面的代碼寫在paint()方法中。
所以此時需繼承JPanel類別,然後覆寫paint()方法
class MyPanel extends JPanel{ public void paint(Graphics g){ super.paint(g); int x, y, px, py, ox, oy; int w=getWidth(); int h=getHeight(); ox=w/2;//中心點x座標 oy=h/2;//中心點y座標 int n=100;//要畫的點數 double radius=50;//半徑 //Graphics painter=getGraphics(); //畫x, y軸 g.setColor(Color.RED); g.drawLine(0,h/2, w, h/2); g.drawLine(w/2, 0, w/2, h); g.setColor(Color.BLUE); //畫圓型 for (int j=0;j<10;j++){ radius=radius+20; px=(int)radius+ox;py=oy; int rc=(int)(Math.random()*256); int gc=(int)(Math.random()*256); int bc=(int)(Math.random()*256); for (int i=0;i<=n;i++){ double angle=i*360.0/n; g.setColor(new Color(rc, gc, bc)); x=(int)(radius*Math.cos(Math.PI/180.0*angle)+ox); y=(int)(radius*Math.sin(Math.PI/180.0*angle)+oy); //g.drawOval(x, y, 40,20); //g.drawLine(px, py, x, y); Graphics2D g2d=(Graphics2D)g; Ellipse2D c=new Ellipse2D.Double(px, py, 5, 5); g2d.fill(c); px=x;py=y; } } } }
然後在btnActionPerformed加入MyPanel()即可
private void btnActionPerformed(java.awt.event.ActionEvent evt) { MyPanel p=new MyPanel(); canvas.setLayout(new javax.swing.BoxLayout(canvas, javax.swing.BoxLayout.Y_AXIS)); canvas.add(p); canvas.updateUI(); }
動畫
底下代碼,可以實現Eve的動畫。eve圖檔下載
import java.awt.Dimension; import java.awt.Image; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.SwingUtilities; public class MainFrame extends javax.swing.JFrame { public MainFrame() { initComponents(); } @SuppressWarnings("unchecked") // private void initComponents() { canvas = new javax.swing.JPanel(); jPanel2 = new javax.swing.JPanel(); jPanel3 = new javax.swing.JPanel(); jPanel4 = new javax.swing.JPanel(); btnStop = new javax.swing.JButton(); btnNew = new javax.swing.JButton(); jPanel5 = new javax.swing.JPanel(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); getContentPane().setLayout(new javax.swing.BoxLayout(getContentPane(), javax.swing.BoxLayout.Y_AXIS)); javax.swing.GroupLayout canvasLayout = new javax.swing.GroupLayout(canvas); canvas.setLayout(canvasLayout); canvasLayout.setHorizontalGroup( canvasLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 897, Short.MAX_VALUE) ); canvasLayout.setVerticalGroup( canvasLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 360, Short.MAX_VALUE) ); getContentPane().add(canvas); jPanel2.setMaximumSize(new java.awt.Dimension(32767, 50)); jPanel2.setMinimumSize(new java.awt.Dimension(0, 50)); jPanel2.setPreferredSize(new java.awt.Dimension(897, 50)); jPanel2.setLayout(new javax.swing.BoxLayout(jPanel2, javax.swing.BoxLayout.X_AXIS)); javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); jPanel3.setLayout(jPanel3Layout); jPanel3Layout.setHorizontalGroup( jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 378, Short.MAX_VALUE) ); jPanel3Layout.setVerticalGroup( jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 50, Short.MAX_VALUE) ); jPanel2.add(jPanel3); jPanel4.setLayout(new javax.swing.BoxLayout(jPanel4, javax.swing.BoxLayout.X_AXIS)); btnStop.setText("結束"); btnStop.setMaximumSize(new java.awt.Dimension(70, 50)); btnStop.setMinimumSize(new java.awt.Dimension(70, 50)); btnStop.setPreferredSize(new java.awt.Dimension(70, 50)); btnStop.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnStopActionPerformed(evt); } }); jPanel4.add(btnStop); btnNew.setText("產生"); btnNew.setMaximumSize(new java.awt.Dimension(70, 50)); btnNew.setMinimumSize(new java.awt.Dimension(70, 50)); btnNew.setPreferredSize(new java.awt.Dimension(70, 50)); btnNew.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnNewActionPerformed(evt); } }); jPanel4.add(btnNew); jPanel2.add(jPanel4); javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5); jPanel5.setLayout(jPanel5Layout); jPanel5Layout.setHorizontalGroup( jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 378, Short.MAX_VALUE) ); jPanel5Layout.setVerticalGroup( jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 50, Short.MAX_VALUE) ); jPanel2.add(jPanel5); getContentPane().add(jPanel2); pack(); }// List<Eve> ls=new ArrayList<>(); private void btnNewActionPerformed(java.awt.event.ActionEvent evt) { try{ Eve e=new Eve((int)(Math.random()*70)+20); ls.add(e); canvas.add(e); canvas.setLayout(new javax.swing.BoxLayout(canvas, javax.swing.BoxLayout.X_AXIS)); } catch(IOException e){} } private void btnStopActionPerformed(java.awt.event.ActionEvent evt) { for (Eve e : ls){ e.close(); } System.exit(0); } public static void main(String args[]) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new MainFrame().setVisible(true); } }); } // Variables declaration - do not modify private javax.swing.JButton btnNew; private javax.swing.JButton btnStop; private javax.swing.JPanel canvas; private javax.swing.JPanel jPanel2; private javax.swing.JPanel jPanel3; private javax.swing.JPanel jPanel4; private javax.swing.JPanel jPanel5; // End of variables declaration } class Eve extends JLabel{ ImageIcon[]imgs; Thread thread; boolean runFlag=true; int index=0; int delay; public Eve(int delay) throws IOException{ int w=200; int h=180; this.delay=delay; this.setMaximumSize(new Dimension(w, h)); this.setMinimumSize(new Dimension(w, h)); this.setPreferredSize(new Dimension(w, h)); imgs=new ImageIcon[36]; for (int i=0;i<imgs.length;i++){ Image img=ImageIO.read(new File(String.format("frame_%02d.png", i+1))); imgs[i]=new ImageIcon(img.getScaledInstance(w, h, Image.SCALE_SMOOTH)); } thread=new Thread(()->{ while(runFlag){ SwingUtilities.invokeLater(()->{this.setIcon(imgs[index]);}); try{Thread.sleep(this.delay);} catch(InterruptedException e){} index=(index+1)%imgs.length; } }); thread.start(); } public void close(){ runFlag=false; thread.interrupt(); } }