關於Graphics/Graphics2D以及簡單的幾何圖像制作(一個簡單鍾表的實現)


 

題外話:正好趕上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 }
View Code

 


 

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM