題外話:正好趕上OperatingSystem的作業要做一個模擬線程/進程調度的問題,決定用JAVA實現才發現這些內容和之前學過的GUI制作是兩碼事兒- -b
通過學習java.swing庫的ActionListener接口我們初步了解了一些關於java框體程序的各個部件JFrame,JPanel,JComponent和控件之間是如何聯系通訊的,然而這次我們要從另一個視角來看java程序框體。
從一個框體實現“表”的代碼我們來初步看一下java是如何在JFrame平台上制作出自己想要的圖案的吧!
(實現效果圖)
有幾個可能會比較陌生的類先篩選出來:
(1)GeneralPath:是java.awt.geom.Path2D的一個子類,path的設定過程主要用兩個函數moveTo(double x,double y)和linkTo(double x,double y),當然如果需要實現path的動態繪制的話需要的方法遠不止這兩個,可以自行去javadoc查閱。
(2)Ellipse2D:是java.awt.geom下的一個類,可以用來繪制理論上的橢圓構造參數為(double x,double y,double width,double height)意義不多贅述。
(3)AffineTransform:這個是整個代碼中最有分量的一個類,這個類的引用的獲取方法是用靜態方法AffineTransform.getInstance(double theta,double x,double y)這里的x和y指的是錨點也就是整個旋轉過程參照的旋轉中心,theta是要用弧度制表示的旋轉角度。具體使用方法下文中會詳細總結。
(4)Timer類是在我接觸到的新的控件,和JButton或者JLabel等等不同的是這個空間不要手動加上ActionListener的接口來進行事件驅動。使用方法在代碼中有詳述。

1 import java.awt.*; 2 import java.awt.event.*; 3 import java.awt.geom.*; 4 import java.util.Calendar; 5 import javax.swing.*; 6 7 public class Main extends JPanel implements ActionListener 8 { 9 // Create a shape for the face of the clock 10 protected static Ellipse2D face = new Ellipse2D.Float(3, 3, 94, 94); 11 12 // Create a path that represents a tick mark 13 protected static GeneralPath tick = new GeneralPath(); 14 static 15 { 16 tick.moveTo(100, 100); 17 tick.moveTo(49, 0); 18 tick.lineTo(51, 0); 19 tick.lineTo(51, 6); 20 tick.lineTo(49, 6); 21 tick.lineTo(49, 0); 22 23 } 24 25 // Create a cool hour hand 26 protected static GeneralPath hourHand = new GeneralPath(); 27 static 28 { 29 hourHand.moveTo(50, 15); 30 hourHand.lineTo(53, 50); 31 hourHand.lineTo(50, 53); 32 hourHand.lineTo(47, 50); 33 hourHand.lineTo(50, 15); 34 } 35 36 // Create a cool minute hand 37 protected static GeneralPath minuteHand = new GeneralPath(); 38 static 39 { 40 minuteHand.moveTo(50, 2); 41 minuteHand.lineTo(53, 50); 42 minuteHand.lineTo(50, 58); 43 minuteHand.lineTo(47, 50); 44 minuteHand.lineTo(50, 2); 45 } 46 47 // And a cool second hand 48 protected static GeneralPath secondHand = new GeneralPath(); 49 static 50 { 51 secondHand.moveTo(49, 5); 52 secondHand.lineTo(51, 5); 53 secondHand.lineTo(51, 62); 54 secondHand.lineTo(49, 62); 55 secondHand.lineTo(49, 5); 56 } 57 58 // Create some colors for the pieces of the clock 59 protected static Color faceColor = new Color(220, 220, 220); 60 protected static Color hourColor = Color.red.darker(); 61 protected static Color minuteColor = Color.blue.darker(); 62 protected static Color secondColor = new Color(180, 180, 0); 63 protected static Color pinColor = Color.gray.brighter(); 64 65 // Create circles for the pivot and center pin 66 protected Ellipse2D pivot = new Ellipse2D.Float(47, 47, 6, 6); 67 protected Ellipse2D centerPin = new Ellipse2D.Float(49, 49, 2, 2); 68 69 70 // Create three transforms that center around the pivot point 71 protected AffineTransform hourTransform = 72 AffineTransform.getRotateInstance(0, 50, 50); 73 protected AffineTransform minuteTransform = 74 AffineTransform.getRotateInstance(0, 50, 50); 75 protected AffineTransform secondTransform = 76 AffineTransform.getRotateInstance(0,50,50); 77 78 // Create a timer that fires once a second and a Calendar 79 // instance for getting the time values 80 protected Timer timer = new Timer(1000, this); 81 protected Calendar calendar = Calendar.getInstance(); 82 83 // Constructor - hardcode a preferred size of 100x100 84 public Main() 85 { 86 setPreferredSize(new Dimension(100, 100)); 87 } 88 89 // Invoked when panel is added to a container 90 public void addNotify() 91 { 92 // Call the superclass and start the timer 93 super.addNotify(); 94 timer.start(); 95 } 96 97 // Invoked when panel is removed from a container 98 public void removeNotify() 99 { 100 // Call the superclass and stop the timer 101 timer.stop(); 102 super.removeNotify(); 103 } 104 105 // 106 public void actionPerformed(ActionEvent event) 107 { 108 // Update the calendar's time 109 this.calendar.setTime(new java.util.Date()); 110 111 // Extract the hours minutes and seconds 112 int hours = this.calendar.get(Calendar.HOUR); 113 int minutes = this.calendar.get(Calendar.MINUTE); 114 int seconds = this.calendar.get(Calendar.SECOND); 115 116 // Using a little trigonometry, set the transforms to rotate 117 // each hand into the proper position. Center the rotation 118 // around the pivot point (50, 50) instead of the origin 119 hourTransform.setToRotation(((double) hours) * 120 (Math.PI / 6.0), 50, 50); 121 minuteTransform.setToRotation(((double) minutes) * 122 (Math.PI / 30.0), 50, 50); 123 secondTransform.setToRotation(((double) seconds) * 124 (Math.PI / 30.0), 50, 50); 125 126 // Force the component to repaint ASAP 127 repaint(); 128 } 129 130 // This is an alternative to creating a UI delegate. Since JPanel's 131 // paint() method only paints the border and backgound, we can just 132 // override the paint method of the component to do the graphics. 133 public void paint(Graphics g) 134 { 135 // Call the superclass first to paint the border (if one is assigned) 136 super.paint(g); 137 138 // Get the graphics context and turn on anti-aliasing 139 Graphics2D g2 = (Graphics2D) g; 140 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 141 RenderingHints.VALUE_ANTIALIAS_ON); 142 143 // Set the paint for the clock face and fill it in 144 g2.setPaint(faceColor); 145 g2.fill(face); 146 147 // Set the paint to black and draw the clock's outline 148 g2.setPaint(Color.black); 149 g2.draw(face); 150 151 // Fill in the 12 ticks around the face of the clock 152 for (double p = 0.0; p < 12.0; p += 1.0) 153 { 154 // This is probably terribly inefficient and should be 155 // done statically or in the constructor - draw the 156 // tick as a transformed shape that is rotated. 157 g2.fill(tick.createTransformedShape( 158 AffineTransform.getRotateInstance((Math.PI / 6.0) * p, 159 50, 50))); 160 } 161 162 // Set the paint and draw the hour hand. It is lowest in the 163 // 'z-order' so will appear underneath the other hands. Notice 164 // how each hand is transformed by a different <AffineTransform>. 165 g2.setPaint(hourColor); 166 g2.fill(hourHand.createTransformedShape(hourTransform)); 167 168 // Set the paint and draw the minute hand, the second hand, 169 // the pivot and the center pin 170 g2.setPaint(minuteColor); 171 g2.fill(minuteHand.createTransformedShape(minuteTransform)); 172 g2.setPaint(secondColor); 173 g2.fill(secondHand.createTransformedShape(secondTransform)); 174 g2.fill(pivot); 175 g2.setPaint(pinColor); 176 g2.fill(centerPin); 177 } 178 179 // A little test frame to show off our fancy clock 180 public static void main(String[] args) 181 { 182 JFrame frame = new JFrame(); 183 frame.setLocation(700, 400); 184 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 185 frame.getContentPane().add(new Main()); 186 frame.pack(); 187 frame.show(); 188 } 189 }
Okay,Here are the points :-P
下面進階深入探究一下這段代碼:
#1#關於每次圖形變換之后,變化前圖形的擦除?
Ans:是這樣的,一般來講有兩種辦法實現所謂的擦除效果以完成動畫:
1.手動擦除,直接對這個paint出來的geom下的類調用clear**方法,或者直接使用新的顏色/畫布將需要擦除的形狀刪除/覆蓋。
2.super.paint(g)這個方法有點神奇,以后需要進一步探討一下,直接在子類的paint方法之中調用父類的paint的方法來實現“擦除”。
代碼中方法其實是第一種,在每次repaint過程中,都會有一次將整個表盤“覆蓋”重畫的過程,但是不得不說,這么實現的話每秒鍾都需要對整個表盤全部的空間進行重畫,是很浪費時間/空間的。
#2#怎么遞推實現的表盤圓周上的12個標記的繪制?
Ans:這里用了一個新的類叫做AffineTransform。使用這個類的方法是在geom下的幾何類中直接調用createTransformedShape(AffineTransform at)返回一個Shape類型,這時匿名調用這個類進行draw(勾勒輪廓)/fill(填充顏色)方法可以完成對轉換完成之后的幾何體的繪制。關於AffineTransform類的更多用法和數學解釋:
http://www.apihome.cn/api/java/AffineTransform.html
#3#怎么實現消除斜線的鋸齒狀?
Ans:這個我真的不懂,但是我知道是下面這段神奇的代碼起到的作用,至於這個“渲染提示”具體的用法還得慢慢學..
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
#4#阿西巴,addNotify方法到底有沒有用?
Ans:addNotify方法是在JPanel添加到JComponent上的時候用的,本例中是在添加pane的直接啟動計時器(Timer),關於代碼最后那個若有若無的getContentPane()為什么會出現,其實還要從JFrame的四層結構開始說起(傳送門見下):
http://tieba.baidu.com/p/2004216123