SSISO Community검색 |
|
SSISO Community메뉴 |
|
SSISO Community카페 |
|
블로그 카테고리 |
|
|
Synchronized로 검색한 결과 |
|
등록일:2008-03-13 14:09:37 작성자: 제목:Graphics |
|
1. Graphics
이 번 장에는 앞 장에서 배웠던 AWT를 효과적으로 지원해주는 Graphics에 대하여 배워 보자. 아마 AWT에서 본 내용들이 많이 나오고, 또 이 Graphics에서 나오는 내용들은 여러분들이 기본적으로 사용하는 내용들이 많아 무심히 넘어갈 수도 있는 내용들이다. 하지만 많이 그리고 자세히 알고 넘어간다면 많은 도움이 된다. 다음 장에 나오는 Applet에서도 많이 사용되는 내용이다.
중 요한 내용들을 잠깐 보고 갈까? 이 번 장에 나오는 내용은 글씨에 대한 여러 가지 정보와 특성을 나타내는 Font 클래스, 도형의 색을 나타내거나, 글씨의 색을 표현하는 Color 클래스 , 여러 가지 도형을 그릴 때 사용되는 Graphics, 또 Graphics 클래스를 더욱 발전시켜 보기 좋게 만들어 주는 2D Graphics, 이미지 load시 필요한, Object 클래스의 wait() 메서드가 생각나는 MediaTracker 클래스 , 그리고 여러 가지 Image를 load시키는 방법이다. 이렇게 써놓고 보니 도움 정도가 아니라 꼭 필요한 내용들이다. 그리고 다음 장에서 배울 Applet도 맛배기로 등장한다. 자 그럼 내용들로 들어가 보자.
1.1. Font 클래스
Font 클래스는 시스템에서 제공하는 글꼴을 사용하는 것으로서 글꼴의 속성과 스타일을 정의한다. 폰트의 스타일로 기본 스타일과 굵기체 , 기울기 체 등을 제공하는데 굵은체이면서 이탤릭체와 같이 서로 조합해서 사용할 수 있다. 예를 들면, Font.ITALIC + Font.BOLD와 같이 사용할 수 있다. 자바에서 제공하는 Font 클래스를 사용하기 위해서는 java.awt.font 패키지를 import해야 한다. 그런데 AWT와 같이 사용한다면 java.awt 패키지만 import 해주면 된다.
Font 클래스의 사용법은 다음과 같다.
폰트를 생성하기 위해서 폰트의 이름, 스타일, 글자크기를 정한다.
Font f = new Font(“Batang”, Font.ITALIC + Font.BOLD, 12);
이렇게 생성된 폰트를 Graphics 클래스의 객체 g를 생성하여 g.setFont() 메서드를 이용해서 설정한다.
g.setFont(매개변수);
그럼 다음 예제를 통해서 Graphics 클래스를 이용해 Font 클래스가 제공하는 폰트의 스타일들을 어떻게 지정하는지 보자.
FontTest.java(Font 클래스를 테스트하기 위한 예제)
import java.awt.*;
import java.awt.Font;
import java.awt.Graphics;
public class FontTest extends Frame {
private Font font1, font2, font3;
public void paint(Graphics g) {
setTitle("FontTest");
font1 = new Font("Serif", Font.BOLD, 12);
font2 = new Font("Batang", Font.ITALIC + Font.BOLD, 24);
font3 = new Font("SansSerif", Font.PLAIN, 14);
g.setFont(font1);
g.drawString("Serif 12 point bold.", 20, 80);
g.setFont(font2);
g.drawString("바탕 24 point italic.", 20, 45);
g.setFont(font3);
g.drawString("SansSerif 14 point plain.", 20, 60);
}
public static void main(String[] args) {
FontTest f = new FontTest();
f.setSize(300, 100);
f.setVisible(true);
}
}
C:\JavaExample\14>javac FontTest.java
C:\JavaExample\14>java FontTest
결과를 보면 어려운 예제는 아니라고 생각한다. 예제를 보면, import를 확실히 하기 위해 클래스별로 import시켰다.
import java.awt.Font;
import java.awt.Graphics;
위의 두 구문은 생략해도 상관없다. 먼저 Font 클래스를 이용해 생성한 객체 font1에 글꼴(“Serif”)을 설정하고 Font 클래스의 생성자 메서드에 속성과 글자크기를 할당한다.
Font font1 = new Font("Serif", Font.BOLD, 12);
paint(Graphics g) 메서드를 호출하는 순간 Graphics 클래스의 객체 g가 생성되어 멤버 메서드인 setFont() 메서드를 이용해 폰트를 설정한다.
g.setFont(font1);
그리고 g.drawString() 메서드를 이용해서 프레임에 문자열을 (20, 80)위치에 출력한다.
g.drawString("Serif 12 point bold.", 20, 80);
같은 방법으로 다른 글씨와 크기, 폰트를 바꾸어서 출력을 하였다.
☞ Font 클래스
Font font = new Font(String name, int style, int size);
String name : 글꼴의 이름
int style : 글꼴의 스타일
int size : 글자의 크기
폰트를 사용하다 보면 글자가 깨지거나 자기가 지정한 글꼴로 안 나오는 경우가 있다.
이 것은 시스템이 지원하는 폰트가 다르기 때문이다. 이 문제를 해결하기 위해서 JDK에서는 영문의 표준으로 Serif, Monospaced, SansSerif, Dialog, DialogInput 폰트에 한해서 동일한 모양을 유지시키고 있다. 가능하면 이 폰트를 디폴트로 사용하는 것이 좋다. 한글은 바탕, 바탕체, 굴림 등과 같은 글씨체를 쓰는 편이 좋을 것이다.
Font 생성자 메서드
■ public Font(String name, int style, int size) : 지정된 이름, 양식, 크기로 새로운 글꼴을 작성한다.
Font 주요 멤버필드
■ public final static int PLAIN : 평범한 스타일의 글꼴을 나타내는 변수
■ public final static int BOLD : 굵은 스타일의 글꼴을 나타내는 변수
■ public final static int ITALIC : 이탤릭체 스타일의 글꼴을 나타내는 변수
Font 주요 멤버 메서드
■ public static Font decode(String str) : 인자로 전달된 명칭 str을 사용해 그에 맞는 폰트를 구한다.
■ public String getFontName() : 폰트 이름을 얻어낸다.
■ public String getFamily() : 현재의 폰트가 소속되어 있는 폰트 패밀리의 이름을 리턴하는데 정확한 폰트 패밀리의 이름은 시스템마다 다르다.
■ public String getName() : 폰트의 이름을 문자열로 리턴한다.
■ public int getStyle() : 현재 폰트의 스타일을 정수값으로 리턴한다. 이 정수들은 Font 클래스에 이미 정의되어 있는 상수들로써, Font.PLAIN, Font.ITALIC, Font.BOLD과 같은 값이며 혹은 이들이 조합된 정수값이다.
■ public int getSize() : 폰트 크기를 포인트(point) 단위로 리턴한다.
1.2. Color 클래스
앞 선 Font 클래스에 이어 이번에는 Color 클래스에 대하여 알아보자. 이 Color 클래스는 설명할 것도 없을 정도로 쉬워 보이지 않나? 바로 색깔에 관련된 클래스이다. 도형의 색깔을 나타내거나, 글씨의 색깔을 나타내는 클래스이다. 간단한 예제를 보자.
ColorTest.java(Color 클래스는 테스트하기 위한 예제)
import java.awt.*;
public class ColorTest {
public static void main(String[] args) {
int a,b,c;
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[1]);
c = Integer.parseInt(args[2]);
Frame f = new Frame();
f.setSize(100,100);
f.setBackground(new Color(a,b,c));
f.setVisible(true);
}
}
C:\JavaExample\14>javac ColorTest.java
C:\JavaExample\14>java ColorTest 125 125 125
위의 예제는 Frame에 색깔을 넣는 예제이다. 예제를 보면 이해할 것이다.
먼저 컴파일을 한 후 프로그램을 실행 시킬 때 색의 숫자를 입력해야 한다. 숫자는 0부터 255 사이의 값을 넣어야 한다.
int a = Integer.parseInt(args[0]);
int b = Integer.parseInt(args[1]);
int c = Integer.parseInt(args[2]);
받아온 값을 int형으로 변환시켜 Color 클래스의 생성자 메서드의 매개변수로 넣어준다.
f.setBackground(new Color(a,b,c));
setBackground() 메서드는 배경 색을 나타내 주는 메서드인거 알고 있을 것이다. 이 메서드는 매개변수로 받은 값들을 혼합하여 Frame의 배경 색을 나타내 준다. 여기서 a는 Red를 나타내고, b는 Green을 나타내며, c는 Blue를 나타낸다. 즉 a가 0이면 빨강빛이고, 255면 하얀빛을 나타낸다. (0, 0, 0)이면 검정색이고, (255, 255, 255)는 하얀색이다. 그리고 조금 더 말씀드리면 빨강색은 (255, 0, 0), 초록색은 (0, 255, 0), 파랑색은 (0, 0, 255)이다. 보통 흔히 말하는 RGB라는 것은 Red, Green, Blue이 세 가지 색을 혼합하는 것을 말한다. 위의 예제를 숫자를 바꾸어 보면서 테스트 해보면 금방 이해할 수 있을 것이다.
☞ Color 클래스
RGB 형식을 사용하여 색을 캡슐화(encapsulate)한다.
RGB 형식으로 빨강, 파랑, 초록 컴포넌트는 각각 0-255 사이의 정수로 표시된다.
0 값은 기본 색상이 전혀 포함되지 않음을 나타내고, 255 값은 이 색상 컴포넌트의 최대 강도를 나타낸다.
Color 멤버필드
■ public static Color black : black을 나타낸다.
■ public static Color blue : blue를 나타낸다.
■ public static Color cyan ; cyan을 나타낸다.
■ public static Color darkGray : darkGray를 나타낸다.
■ public static Color gray : gray를 나타낸다.
■ public static Color green : green을 나타낸다.
■ public static Color lightGray : lightGray를 나타낸다.
■ public static Color magenta : magenta를 나타낸다.
■ public static Color orange : orange를 나타낸다.
■ public static Color pink : pink를 나타낸다.
■ public static Color red : red를 나타낸다.
■ public static Color white : white를 나타낸다.
■ public static Color yellow : yellow를 나타낸다.
Color 생성자 메서드
■ public Color (ColorSpace cspace, float[] components, float alpha) : float 배열로 지정된 색성분과 지정된 알파를 사용해 지정된 ColorSpace의 색을 작성한다.
■ public Color (float r, float g, float b) : 범위 (0.0 - 1.0)의 지정된 빨강, 초록, 파랑의 값을 사용해 불투명한 sRGB 칼라를 작성한다.
■ public Color (float r, float g, float b, float a) : 범위 (0.0 ~ 1.0)의 지정된 빨강, 초록, 파랑 및 알파의 값을 사용해 sRGB 칼라를 작성한다.
■ public Color (int rgb) : 비트 16-23 의 적색 성분, 비트 8-15 의 녹색 성분 및 비트 0-7의 청색 성분으로부터 합성된, 지정된 RGB 값을 사용해 불투명한 sRGB 칼라를 작성한다.
■ public Color (int rgba, boolean hasalpha) : 비트 24 ~ 31의 알파 성분, 비트 16 ~ 23의 적색 성분, 비트 8 ~ 15의 녹색 성분 및 비트 0 ~ 7의 청색 성분으로 구성되는, 지정된 RGBA 값을 가지는 sRGB 칼라를 작성한다.
■ public Color (int r, int g, int b) : 범위 (0 ~ 255)의 지정된 빨강, 초록, 파랑의 값을 사용해 불투명한 sRGB 칼라를 작성한다.
■ public Color (int r, int g, int b, int a) : 범위 (0 - 255)의 지정된 빨강, 초록, 파랑 및 알파값을 사용해 sRGB 칼라를 작성한다.
Color 주요 멤버 메서드
■ public Color brighter () : 이 Color를 보다 밝게 한, 새로운 Color를 작성한다.
■ public PaintContext createContext(ColorModel cm, Rectangle r, Rectangle2D r2d, AffineTransform xform, RenderingHints hints) : 솔리드 칼라 패턴을 작성하기 위해서 사용되는 PaintContext를 작성해 돌려준다.
■ public Color darker () : 이 Color를 보다 어둡게 한, 새로운 Color를 작성한다.
■ public static Color decode (String nm) : String을 정수값으로 변환해, 지정된 불투명한 Color를 돌려준다.
■ public boolean equals (Object obj) : 다른 Object가 이 Color와 같은지 어떤지를 판정 한다.
■ public int getAlpha () : 0 ~ 255 의 범위에 있는 알파 성분을 돌려준다.
■ public int getBlue () : Default의 sRGB 영역의 0 ~ 255 의 범위에 있는 파랑색 성분을 돌려준다.
■ public static Color getColor (String nm) : 시스템 특성에서 색을 검색한다.
■ public static Color getColor (String nm, Color v) : 시스템 특성에서 색을 검색한다.
■ public static Color getColor (String nm, int v) : 시스템 특성에서 색을 검색한다.
■ public float[] getColorComponents (ColorSpace cspace, float[] compArray) : cspace 파라미터에 의해 지정된 ColorSpace로 나타내지는 Color의 색성분만큼을 격납 하는 float 배열을 돌려준다.
■ public float[] getColorComponents (float[] compArray) : Color의 ColorSpace로 나타나는 Color의 색성분만큼을 격납하는 float 배열을 돌려준다.
■ public ColorSpace getColorSpace () : 이 Color의 ColorSpace를 돌려준다.
■ public float[] getComponents (ColorSpace cspace, float[] compArray) : cspace 파라미터에 의해 지정된 ColorSpace로 나타내지는 Color의 색성분 및 알파 성분을 격납 하는 float배열을 돌려준다.
■ public float[] getComponents (float[] compArray) : Color의 ColorSpace로 나타내지는 Color의 색성분 및 알파 성분을 격납하는 float 배열을 돌려준다.
■ public int getGreen () : 디폴트의 sRGB 영역의 0 ~ 255 의 범위에 있는 초록색 성분을 돌려준다.
■ public static Color getHSBColor (float h, float s, float b) : HSB 칼라 모델에 지정된 값에 근거해 Color Object를 작성한다.
■ public int getRed () : Default의 sRGB 영역의 0 ~ 255의 범위에 있는 빨강색 성분을 돌려준다.
■ public int getRGB () : Default의 sRGB ColorModel의 색을 나타내는 RGB값을 돌려준다(비트 중 24 ~ 31 은 알파, 16 ~ 23은 빨강, 8 ~ 15는 초록, 0 ~ 7은 파랑).
■ public float[] getRGBColorComponents (float[] compArray) : 디폴트의 sRGB 칼라 영역에서 나타내지는 Color의 색성분만큼을 포함 하는 float배열을 돌려준다.
■ public float[] getRGBComponents (float[] compArray) : 디폴트의 sRGB 칼라 영역에서 나타내지는 Color의 색성분 및 알파 성분을 포함 하는 float배열을 돌려준다.
■ public int getTransparency () : 이 Color의 투명도 모드를 돌려준다.
■ public int hashCode () : 이 Color의 hash code value를 계산한다.
■ public static int HSBtoRGB (float hue, float saturation, float brightness) : HSB 모델에 의해 지정되는 색의 성분을, 대응하는 디폴트의 RGB 모델의 값세트로 변환한다.
■ public static float[] RGBtoHSB (int r, int g, int b, float[] hsbvals) : 디폴트의 RGB 모델에 의해 지정된 색의 성분을, HSB 모델의 3 개의 성분인 색상, 채도, 명도의 값의 대응하는 세트로 변환한다.
■ public String toString () : 이 Color의 캐릭터 라인 표현을 돌려준다.
1.3. Graphics 클래스
Graphics 클래스는 그림에 관련된 프로그램을 작성하는 데 꼭 필요한 클래스이다. 여러 가지 도형을 그릴 수 있는 메서드들이 들어 있다. 이 Graphics 클래스는 보통 다른 클래스의 paint() 메서드 안에서 객체가 생성되고, 그 객체를 이용하여 Graphics 클래스의 멤버 메서드가 호출되어 사용된다.
우 리가 많이 사용하는 도형들을 보면 Rectangle(사각형), Oval(원형), Polygon(다각형), Line(선)들이 있다. 그리고 글씨 그리기, 구역 복사하기, 구역 삭제하기에 대해서 알아보자. 아래의 예제는 Graphics의 여러 메서드들을 호출하여 Canvas에 도형들을 그린 후, Canvas를 Frame에 부착하는 예제이다. 그럼 예제를 테스트 해보자.
GraphicsTest.java (Graphics 클래스를 테스트하기 위한 예제)
import java.awt.*;
public class GraphicsTest extends Frame {
public GraphicsTest() {
add(new Canvas() {
public void paint(Graphics g) {
g.setColor(Color.red);
g.draw3DRect( 0, 0, 46, 36, true);
g.drawOval(50, 0, 46, 36);
int x1[] = new int[] { 100, 300, 273 };
int y1[] = new int[] { 0, 0, 36 };
g.drawPolygon(x1, y1, x1.length);
g.setColor(Color.blue);
g.fill3DRect( 0, 40, 46, 36, true);
g.fillOval(50, 40, 46, 36);
int x2[] = new int[] { 100, 300, 273 };
int y2[] = new int[] { 40, 40, 76 };
g.fillPolygon(x2, y2, x2.length);
g.drawLine(150, 40, 396, 76);
g.setColor(Color.black);
g.drawString("www.jabook.org", 10, 115);
g.setColor(Color.cyan);
g.drawRoundRect( 0, 120, 46, 36, 10, 10);
g.fillRoundRect(50, 120, 46, 36, 10, 10);
g.clearRect(30, 30, 250, 30);
}
});
}
public static void main(String args[]) {
GraphicsTest f = new GraphicsTest();
f.setTitle("Graphics");
f.setSize(420, 200);
f.setVisible(true);
}
}
C:\JavaExample\14>javac GraphicsTest.java
C:\JavaExample\14>java GraphicsTest
위의 예제는 Canvas에 Graphics 클래스의 객체를 생성하여 Graphics 클래스의 여러 멤버 메서드들을 호출하여 여러 모양의 도형들을 그려본 예제이다.
Graphics 클래스의 객체는 Canvas의 paint(Graphics g) 메서드가 생성하였다.
public void paint(Graphics g)
생성한 객체 g를 이용하여 g.setColor(Color c) 메서드를 사용해 생성될 도형의 색깔을 지정한다. 다시 색을 지정할 때까지 같은 색의 도형이나 글씨가 출력된다.
g.setColor(Color.red);
사 각형을 그리는 메서드는 g.draw3DRect(int x, inty, int width, int height, boolean raised)와 g.drawRect(int x, int y, int width, int height)가 있다. 먼저 나온 메서드는 사각형을 3D로 그려주고, 뒤에 나온 메서드는 사각형을 2D로 그린다. 그럼 메서드의 매개변수에 대하여 설명 하겠다.
■ int x : 사각형이 시작하는 x좌표를 나타낸다.
■ int y : 사각형이 시작하는 y좌표를 나타낸다.
■ int width : 사각형의 가로길이를 나타낸다.
■ int height : 사각형의 세로길이를 나타낸다.
■ boolean raised : 사각형이 영역 위로 올라오는지 영역 아래로 가라앉는지를 나타낸다. (draw3DRect() 메서드)
■ boolean raised : 사각형이 영역 위로 올라오는지 아니면 표면이 에칭(etching)되는 지를 나타낸다. (fill3DRect() 메서드)
g.fill3DRect(int x, int y, int width, int height, boolean raised)와 g.fillRect(int x, int y, int width, int height) 메서드 또한 사각형을 그리는 메서드이다. 다만 사각형의 내부를 꽉 채워 출력한다.
g.draw3DRect(0, 0, 46, 36, true);
g.fill3DRect(0, 40, 46, 36, true);
g.drawRect(50, 160, 300, 100);
g.drawRoundRect(0, 120, 46, 36, 10, 10);
g.fillRoundRect(50, 120, 46, 36, 10, 10);
위 의 구문 중 g.drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) 메서드는 모서리와 모서리가 만나는 각을 rounding 처리하는 매개변수의 값을 더 요구 한다. Rectangle을 그리는 메서드들의 매개변수들은 거의 비슷하다. 그래서 한 번 읽어보고 API를 참조해서 사용하면 별로 지장이 없을 듯 싶다. 별로 어려울 것이 없다.
Rectangle을 그리는 메서드들의 매개변수
■ int x : 사각형이 시작하는 x 좌표를 나타낸다.
■ int y : 사각형이 시작하는 y 좌표를 나타낸다.
■ int width : 사각형의 가로 길이를 나타낸다.
■ int height : 사각형의 세로 길이를 나타낸다.
■ boolean raised : 사각형이 영역 위로 올라오는지 영역 아래로 가라앉는지를 나타낸다(draw3DRect() 메서드).
■ boolean raised : 사각형이 영역 위로 올라오는지 영역으로 에칭(etching)되는지를 나타낸다(fill3DRect() 메서드).
■ int arcWidth : 네 모서리에서 호의 수평 직경을 나타낸다.
■ int arcHeight : 네 모서리에서 호의 수직 직경을 나타낸다.
다음으로, 원과 타원을 그리는 메서드들을 보자.
■ g.drawOval(50, 0, 46, 36);
■ g.fillOval(50, 40, 46, 36);
위의 구문에서 위의 메서드는 속이 빈 원이 그려지고, 아래의 메서드는 속이 찬 원이 그려지게 된다. drawOval(int x, int y, int width, int height) 메서드를 보면서 설명한다.
■ int x : 원 중심의 x좌표를 나타낸다.
■ int y : 원 중심의 y좌표를 나타낸다.
■ int width : 원의 가로길이를 나타낸다.
■ int height : 원의 세로길이를 나타낸다.
width와 height가 같게 되면 원이 되고, 다르면 타원이 된다.
Oval을 그리는 메서드들의 매개변수
■ int x : 원 중심의 x좌표를 나타낸다.
■ int y : 원 중심의 y좌표를 나타낸다.
■ int width : 원의 가로 길이를 나타낸다.
■ int height : 원의 세로 길이를 나타낸다.
width와 height가 같게 되면 원이 되고, 다르면 타원이 된다.
다음은 다각형을 그리는 메서드를 보자. 위의 예제에서는 삼각형을 그려보았다.
int x1[] = new int[] 100, 300, 273;
int y1[] = new int[] 0, 0, 36;
g.drawPolygon(x1, y1, x1.length);
int x2[] = new int[] 100, 300, 273;
int y2[] = new int[] 40, 40, 76;
g.drawPolygon(x2, y2, x2.length);
위 의 메서드는 속이 빈 삼각형이 그려지고, 아래의 메서드는 속이 찬 삼각형이 그려진다. 배열에 점이 3개 밖에 없다. 그래서 삼각형이 그려진다. drawPolyline(int[] xPoints, int[] yPoints, int nPoints) 메서드를 보면서 설명한다.
■ int[] xPoints : x의 좌표값들을 모은 배열
■ int[] yPoints : y의 좌표값들을 모은 배열
■ int nPoints : 점의 갯수
이 메서드는 매개변수로 포함된 점들을 연결한 다각형을 그리는 것이다. 다시 한번 말하지만 점이 3개여서 삼각형이 그려진 것이다. 왜 삼각형 그리는 메서드는 없냐고 불평해도 소용없다. Sun사에서 지원을 안 하니깐.
Polygon을 그리는 메서드들의 매개변수
■ int[] xPoints : x의 좌표값들을 모은 배열을 나타낸다
■ int[] yPoints : y의 좌표값들을 모은 배열을 나타낸다
■ int nPoints : 점의 개수를 나타낸다
그리고 선을 그리는 메서드가 있다.
■ g.drawLine(150, 40, 396, 76);
이 메서드의 원형은 drawLine(int x1, int y1, int x2, int y2)이다.
■ int x1 : 시작점의 x좌표를 나타낸다.
■ int y1 : 시작점의 y좌표를 나타낸다.
■ int x2 : 끝점의 x좌표를 나타낸다.
■ int y2 : 끝점의 x좌표를 나타낸다.
네 개의 좌표로서 생긴 두 점을 연결하면 선이 그려진다.
Line을 그리는 메서드의 매개변수
■ int x1 : 시작점의 x좌표를 나타낸다.
■ int y1 : 시작점의 y좌표를 나타낸다.
■ int x2 : 끝점의 x 좌표를 나타낸다.
■ int y2 : 끝점의 x 좌표를 나타낸다
그리고 글씨를 그릴 수 있다.
■ g.drawString("www.jabook.org", 10, 115); : 이 메서드는 “www.jabook.org”라는 글을 Canvas에 그렸다. 메서드의 원형은 drawString(String str, int x, int y)이다.
■ String str : 그려질 글씨이다.
■ int x : 글씨가 그려질 x좌표의 위치이다.
■ int y : 글씨가 그려질 y좌표의 위치이다.
글씨를 그린다고 생각하면 우스울지도 모르지만 사실 그린다는 개념이 맞다. 그러나 글씨를 그리든지 쓰든지 잘 나오기만 하면 되는 것이다.
String을 그리는 메서드의 매개변수
■ String str : 그려질 글씨를 나타낸다.
■ int x : 글씨가 그려질 x좌표의 위치이다.
■ int y : 글씨가 그려질 y좌표의 위치이다.
구역을 지우는 메서드도 있다. 바로 clearRect(int x, int y, int width, int height) 메서드이다.
■ int x : 사각형이 시작하는 x좌표를 나타낸다.
■ int y : 사각형이 시작하는 y좌표를 나타낸다.
■ int width : 사각형의 가로 길이를 나타낸다.
■ int height : 사각형의 세로 길이를 나타낸다.
만들어진 사각형만큼의 구역을 삭제한다.
■ g.clearRect(80, 180, 250, 30);
clearRect() 메서드의 매개변수
■ int x : 사각형이 시작하는 x좌표를 나타낸다.
■ int y : 사각형이 시작하는 y좌표를 나타낸다.
■ int width : 사각형의 가로길이를 나타낸다.
■ int height : 사각형의 세로길이를 나타낸다.
구역을 복사하는 메서드도 있다. 그런데 그 구역은 네모난 구역만 복사 된다. 한 번 예제를 보고나서 설명을 하겠다. 별로 어렵지는 않는다.
CopyAreaTest.java(copyArea() 메서드를 테스트하기 위한 예제)
import java.awt.*;
public class CopyAreaTest extends Frame {
public CopyAreaTest() {
add(new Canvas() {
public void paint(Graphics g) {
g.setColor(Color.blue);
g.drawString("www.jabook.org", 10, 15);
g.copyArea( 0, 0, 100, 20, 100, 0);
g.copyArea( 0, 0, 100, 20, 0, 30);
g.copyArea( 0, 0, 100, 20, 100, 30);
}
});
}
public static void main(String args[]) {
CopyAreaTest f = new CopyAreaTest();
f.setTitle("CopyAreaTest");
f.setSize(220, 80);
f.setVisible(true);
}
}
C:\JavaExample\14>javac CopyAreaTest.java
C:\JavaExample\14>java CopyAreaTest
위의 예제는 글씨를 그린 다음, 일정 구역을 복사 한 다음 그 구역을 다른 곳에 붙이는 예제이다. “www.jabook.org”라는 글을 (10, 15) 지점에 그린 것은 알 것이다.
g.setColor(Color.blue);
g.drawString("www.jabook.org", 10, 15);
글은 파란색으로 그렸다. 그리고 바로 다음에 구역을 복사하는 메서드인 copyArea() 메서드가 나온다.
g.copyArea( 0, 0, 100, 20, 100, 0);
g.copyArea( 0, 0, 100, 20, 0, 30);
g.copyArea( 0, 0, 100, 20, 100, 30);
이 제 매개변수를 보면 예상이 되지 않나? 아직 아닌가? 메서드의 원형을 보면서 설명하겠다. abstract void copyArea(int x, int y, int width, int height, int dx, int dy) 메서드이다. 이 메서드는 다른 메서드와 달리 추상메서드이므로 사용할 때는 상속을 받아 몸체를 구현해야 한다.
■ int x : 복사가 시작되는 x좌표를 나타낸다.
■ int y : 복사가 시작되는 y좌표를 나타낸다.
■ int width : 시작점의 위치에서 사각형의가로 길이를 나타낸다.
■ int height : 시작점의 위치에서 사각형의 세로 길이를 나타낸다.
■ int dx : 픽셀을 복사할 수평 거리를 나타낸다.
■ int dy : 픽셀을 복사할 수직 거리를 나타낸다.
위 의 메서드는 결국 (0, 0)의 점부터 가로의 길이로 100, 세로의 길이로 30만큼의 구역을 복사하여, 가로로 100 떨어진 지점부터, 세로는 0만큼 떨어진 지점으로 이동해서 다시 그리라는 뜻이다. 아래의 두 구문들은 가로로 0, 세로로 30 이동하라는 뜻과 가로로 100, 세로로 30 이동하라는 뜻이다. 가로로 dx만큼 떨어진 거리에 세로로 dy만큼 떨어진 거리에 복사한 구역을 그리는 거죠. dx, dy가 음의 값을 가질 경우 왼쪽과 위로 움직여 그리라는 뜻이다.
copyArea() 메서드의 매개변수
■ int x : 복사가 시작되는 x 좌표를 나타낸다.
■ int y : 복사가 시작되는 y 좌표를 나타낸다.
■ int width : 시작점의 위치에서 사각형의 가로 길이를 나타낸다.
■ int height : 시작점의 위치에서 사각형의 세로 길이를 나타낸다.
■ int dx : 픽셀을 복사할 수평 거리를 나타낸다.
■ int dy : 픽셀을 복사할 수직 거리를 나타낸다.
위에 설명한 메서드들은 그래픽에서 많이 쓰여지는 메서드들이다. 이외에 자바에서 그래픽을 지원하는 메서드는 많이 있다. 아래에 주요 멤버 메서드에 대하여 설명하였으니 꼭 읽어 보고 가야 한다.
Graphics 클래스는 오로지 그림만을 위해서 존재하는 클래스이다. 이 클래스가 있어서 사용자가 그래픽 작업을 편하게 할 수 있다. 하지만 메서드가 많이 존재하는 클래스이다. 능력이 된다면, 전부 암기를 하는 것도 좋지만 API를 보고 사용하는 데 지장이 없을 정도만 이해하고 넘어가도 아무 문제는 없다.
Graphics 주요 멤버 메서드
■ public abstract void clearRect(int x, int y, int width, int height) : 주어진 사각형을 배경색으로 지운다.
■ public abstract void clipRect(int x, int y, int width, int height) : 주어진 사각형을 잘라서 현재 클립에 저장한다.
■ public abstract void copyArea(int x, int y, int width, int height, int dx, int dy) : 컴포넌트의 주어진 영역을 dx와 dy 거리만큼에 복사한다.
■ public void draw3DRect(int x, int y, int width, int height, boolean raised) : 주어진 크기와 모양(나오거나 들어간 모양)을 갖는 3D 사각형을 그린다.
■ public abstract void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) : 주어진 크기의 사각형에 해당하는 호를 그리는데, 시작 각부터 주어진 각만큼의 크기를 갖는 호를 그린다.
■ public void drawBytes(byte[] data, int offset, int length, int x, int y) : 현재 폰트와 컬러를 이용하여 주어진 바이트 배열에 있는 문자열을 해당 위치에 그린다.
■ public void drawChars(char[] data, int offset, int length, int x, int y) : 현재 폰트와 컬러를 이용하여 주어진 문자 배열에 있는 문자열을 해당 위치에 그린다.
■ public abstract void drawLine(int x1, int y1, int x2, int y2) : (x1, y1)에서 (x2, y2)까지의 라인을 그린다.
■ public abstract void drawOval(int x, int y, int width, int height) : 주어진 사각형에 맞는 타원을 그린다.
■ public abstract void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) : 주어진 좌표들을 꼭지점으로 하는 다각형을 그린다.
■ public void drawPolygon(Polygon p) : 주어진 좌표들을 꼭지점으로 하는 다각형을 그린다.
■ public abstract void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) : 주어진 좌표들을 연결하는 라인을 그린다.
■ public void drawRect(int x, int y, int width, int height) : 주어진 사각형을 그린다.
■ public abstract void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) : 주어진 조건에 해당하는 둥근 사각형을 그리는데, 각의 너비와 높이에 맞게 둥글게 처리한다.
■ public abstract void drawString(String str, int x, int y) : 현재 폰트와 컬러를 사용하여 주어진 문자열을 그린다.
■ public void fill3DRect(int x, int y, int width, int height, boolean raised) : 3D 사각형을 그려 현재 컬러로 채운다.
■ public abstract void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) : 부채꼴 호를 그려 채운다.
■ public abstract void fillOval(int x, int y, int width, int height) : 다원을 그려 채운다.
■ public abstract void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) : 다각형을 채운다.
■ public void fillPolygon(Polygon p) : 다각형을 채운다.
■ public abstract void fillRect(int x, int y, int width, int height) : 사각형을 채운다.
■ public abstract void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) : 둥근 사각형을 채운다.
■ public abstract Shape getClip() : 현재 클립 영역을 얻는다.
■ public abstract Rectangle getClipBounds() : 현재 클립 영역의 사각형 바운드를 얻는다.
■ public Rectangle getClipBounds(Rectangle r) : 현재 클립 영역의 사각형 바운드를 얻는다.
■ public Rectangle getClipRect() : getClipBounds() 메서드로 바뀌었다.
■ public abstract Color getColor() : 현재 컬러를 얻는다.
■ public abstract Font getFont() : 현재 폰트를 얻는다.
■ public FontMetrics getFontMetrics() : 현재 폰트의 폰트 매트릭스를 얻는다.
■ public abstract FontMetrics getFontMetrics(Font f) : 주어진 폰트에 대한폰트 매트릭스를 얻는다.
■ public boolean hitClip(int x, int y, int width, int height) : 주어진 사각형이 현재 클립 영역의 테두리와 겹치는 지를 얻는다.
■ public abstract void setClip(int x, int y, int width, int height) : 주어진 사각형으로 현재 클립을 설정한다.
■ public abstract void setClip(Shape clip) : 주어진 사각형으로 현재 클립을 설정한다.
■ public abstract void setColor(Color c) : 주어진 컬러로 현재 컬러를 설정한다.
■ public abstract void setFont(Font font) : 주어진 폰트로 현재 폰트를 설정한다.
■ public abstract void setPaintMode() : 현재 컬러로 페인트 모드를 설정한다.
■ public abstract void setXORMode(Color c1) : 현재 컬러와 주어진 컬러를 이용하여 페인트 모드를 설정한다.
1.4. MediaTracker 클래스
“어, 못 들어본 클래스네!” 라고 생각하는 이들이 많을 것이다. 이 MediaTracker 클래스는 주로 네트워크 상 에서 많이 사용되는 클래스이다. 물론 네트워크와 관계없이 로컬 상에서도 사용된다. 하지만 이 MediaTracker 클래스의 기능을 알고 보면 왜 네트워크 상태에서 많이 사용하는지 알게 될 것이다. 이 MediaTracker 클래스는 로딩을 보장하기 위한 클래스이다. 쉽게 설명하면 그림이 전부 전송 될 때까지 기다리는 역할을 한다. 그럼 예제를 보자.
다음은 MediaTracker 클래스는 이용해 이미지를 로딩하는 예제이다.
MediaTrackerTest.java(MediaTracker 클래스는 테스트하기 위한 예제)
import java.awt.*;
import java.awt.image.*;
public class MediaTrackerTest extends Frame {
private Image image=null;
private BufferedImage bi = null;
private int width =0;
private int height =0;
public static void main(String args[]) {
MediaTrackerTest mtt = new MediaTrackerTest();
mtt.makeImage("java_logo.jpg");
}
public MediaTrackerTest() {
this.setSize(150,170);
this.show();
}
public void makeImage(String filename) {
try {
image = Toolkit.getDefaultToolkit().getImage(filename);
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(image,0);
tracker.waitForID(0);
width= image.getWidth(null);
height= image.getHeight(null);
bi = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
Graphics gg = bi.getGraphics();
gg.drawImage(image, 0,0, this);
gg.dispose();
repaint();
} catch (Exception ex){
System.out.println(ex.toString());
}
}
public void paint (Graphics g) {
if(bi!=null)
g.drawImage(bi, 50,50, this);
}
}
C:\JavaExample\14>javac JPEGFileEncodeImage.java
C:\JavaExample\14>java JPEGFileEncodeImage
MediaTracker 클래스는 설명하기 위해 예제를 냈는데, 다른 부분이 훨씬 더 많다. 예제의 자세한 부분은 뒤에 이미지 로딩 부분에서 설명 드리고 지금은 원래대로 MediaTracker 클래스에 대해서 설명한다.
MediaTracker 클래스는 10장에서 배웠던 Object 클래스의 wait() 메서드와 비슷하다. 눈치 빠른 분들은 이해했을 것이다.
예제에서 보면 MediaTracker 클래스는 사용하기 바로 아래 구문에서 이미지를 로드하고 있다. 먼저 Image클래스의 객체 image를 생성했다.
Image image = Toolkit.getDefaultToolkit().getImage(filename);
java.awt.Toolkit 클래스의 멤버 메서드인 getDefaultToolkit() 메서드와 getImage(String filename) 메서드를 사용하여 image 객체를 얻어오고 있다. 이 때 MediaTracker 클래스를 사용한다.
MediaTracker tracker = new MediaTracker(this);
우 선 MediaTracker 클래스의 객체 tracker를 생성했다. 근데 여기에서 전에 보던 놈이 나왔다. this라는 것이 갑자기 튀어 나왔다. 우리는 이 this에 대하여 4장에서 배웠다. 기억이 안 나면 4장을 확인 해 보자. ‘언젠가 생길 내 주소의 this’ 생각이 나나? 그래도 아리송하나? MediaTracker 클래스의 생성자 메서드는 객체를 생성할 때 이미지를 그릴 컴포넌트를 요구한다. 그런데 위의 예제에서 이미지를 그릴 컴포넌트는 Frame이다. 하지만 지금 프로그램을 디자인하고 있어, 메모리가 생성도 되지 않았는데 어떻게 참조값을 넘겨 줄 수 있겠나? 그래서 여기에서 this를 사용해서 지금은 참조값이 없지만 프로그램이 실행되어 Frame이 생성되면 그 Frame의 메모리 주소를 넘겨주겠다는 뜻이다. 별로 어려울 것이 없다.
tracker.addImage(image, 0);
tracker.waitForID(0);
그 리고 생성한 객체 tracker를 이용하여 addImage(Image image, int id) 메서드를 호출하였다. 이 메서드는 tracker 객체가 어떤 이미지를 목표로 삼을지를 알려주고, 그 이미지에 id값을 준다. 그리고 tracker 객체는 waitForID(int id) 메서드를 호출하여 준다. 이 메서드는 입력된 id값을 가지고 있는 image가 전부 로딩이 될 때까지 프로그램을 wait시킨다. 그런 후 image가 전부 로딩되면 자동으로 notify 해주는 것이다. 이제 왜 네트워크 상에서 많이 사용하는지 이유를 알 것 같지 않나? 만약 온라인 상에서 그림이 전부 넘어오지도 않았는데 프로그램이 계속 진행된다면 어떻게 될까? 바로 그런 것을 방지해주는 것이다. 만약 이미지로드 또는 배율조정 중에 오류가 발생하면 그 이미지의 로드는 완료된 것으로 간주한다. 그럴 때 isErrorAny() 메서드와 isErrorID() 메서드를 사용해서 오류를 점검하면 된다.
그 런데 이렇게 길게 설명했는데 예제의 굵은 글씨로 표시된 부분을 빼고 컴파일하여도 에러는 나타나지 않는다. 하지만 결과가 똑같이 나온다고 보장은 할 수 없다. 똑같이 나오는 예제도 있고, 그렇지 않은 예제도 있다. 위의 예제는 안 해주면 에러가 뜨는 예제이다. 실행 시의 에러 발생이다. 만약 사용하지 않아도 되는 예제는 사용자가 결정하면 된다. 하지만 사용하는 것이 안전하다.
왜 MediaTracker 클래스는 네트워크 상에서 많이 사용한다는 것을 이제 좀 이해하리라 믿는다. “일종의 wait() 메서드다.”라고 생각하면 그다지 어려울 것도 없는 놈이다. 하지만 꼭 사용하라는 것은 아니니 잘 생각하여 프로그램을 작성하자. 그리고 이 MediaTracker 클래스는 이미지뿐만 아니라 오디오를 지원하게 만든다고 했지만 아직 지원하지 않는다.
MediaTracker
■ MediaTracker tracker = new MediaTracker(this);
addImage(Image image, int id) : 메서드를 사용해서 추적해야 할 이미지를 설정하고, id값을 설정한다.
waitForID(int id) : 메서드를 사용해서 입력된 id값의 image가 로딩 될 때까지 프로그램을 wait시킨다.
MediaTracker 생성자 메서드
■ public MediaTracker(Component comp) : 지정된 컴포넌트의 이미지를 추적하는 MediaTracker 객체를 작성한다.
MediaTracker 주요 멤버 메서드
■ public void addImage(Image image, int id) : 매체 추적자가 추적 중인 이미지의 목록에 이미지를 추가한다.
■ public void addImage(Image image, int id , int w, int h) : 매체 추적자가 추적 중인 이미지의 목록에 배율을 조정한 이미지를 추가한다.
■ public boolean checkAll() : 매체 추적자가 추적 중인 이미지를 모두 로드했는 지 여부를 점검한다.
■ public boolean checkAll(boolean load) : 매체 추적자가 추적 중인 이미지를 모두 로드했는 지 여부를 점검한다.
■ public boolean checkID(int id) : 매체 추적자가 추적하는 지정된 식별자를 가진 이미지를 모두 로드했는 지 여부를 점검한다.
■ public boolean checkID(int id, boolean load) : 매체 추적자가 추적하는 지정된 식별자를 가진 이미지를 모두 로드했는 지 여부를 점검한다.
■ public Object[] getErrorsAny() : 오류가 발생한 모든 매체 목록을 리턴한다.
■ public Object[] getErrorsID(int id) : 지정된 ID를 가졌으며 오류가 발생한 매체 목록을 리턴한다.
■ public boolean isErrorAny() : 모든 이미지의 오류 상태를 점검한다.
■ public boolean isErrorID(int id) : 매체 추적자가 추적하는 지정된 식별자를 가진 모든 이미지의 오류 상태를 점검한다.
■ public void removeImage(Image image) : 매체 추적자에서 지정된 이미지를 제거한다.
■ public void removeImage(Image image, int id) : 매체 추적자의 지정된 추적 ID에서 지정된 이미지를 제거한다.
■ public void removeImage(Image image, int id, int w, int h) : 매체 추적자에서 지정된 가로 길이, 세로 길이 및 ID를 가진 지정된 이미지를 제거한다.
■ public int statusAll(boolean load) : 매체 추적자가 추적하는 모든 매체 상태의 bitwise inclusive OR을 계산하고 리턴한다.
■ public int statusID(int id, boolean load) : 매체 추적자가 추적하는 지정된 식별자는 가진 모든 매체 상태의 bitwise inclusive OR을 계산하고 리턴한다.
■ public void waitForAll() : 매체 추적자가 추적하는 모든 이미지의 로드를 시작한다.
■ public boolean waitForAll(long ms) : 매체 추적자가 추적하는 모든 이미지의 로드를 시작한다.
■ public void waitForID(int id) : 매체 추적자가 추적하는 지정된 식별자를 가진 모든 이미지 로드를 시작한다.
■ public boolean waitForID(int id, long ms) : 매체 추적자가 추적하는 지정된 식별자는 가진 모든 이미지 로드를 시작한다.
1.5. 2D Graphics
자 바 2D Graphics는 2D에서 알 수 있듯이 자바의 Graphics 클래스의 메서드들을 이용해서 구현했던 여러 가지 도형들이나 선, 그리고 글씨들까지도 2차원으로 표현한 것이다. 2차원을 구현했기 때문에 그려지는 것들이 동적이며 매우 화려하다. 이 2D Graphics를 지원해 주는 클래스가 Graphics2d 클래스이다. 클래스의 멤버 메서드들을 잠깐 봐도 Graphics와 많이 비슷하다. 왜냐하면 Graphics2d 클래스는 Graphics를 상속받았기 때문이다. 쉽게 Graphics보다 좀 더 화려해지고, 동적으로 변했다라고 생각하면 될 것 같다.
Graphics2D를 제대로 구현하려면 java.awt.geom 패키지 등 여러 패키지를 import시켜야 한다. 하여간 먼저 예제를 보자.
Graphics2DTest.html (Applet을 로딩하기 위한 html파일)
<applet code="Graphics2DTest" width=400 height=300>
</applet>
Graphics2DTest.java (2D Graphics를 테스트하기 위한예제)
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.TextLayout;
import java.awt.font.TextHitInfo;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
public class Graphics2DTest extends Applet implements Runnable {
private static String text[] = { "www.jabook.org", "Graphics2D" };
private static Color colors[] = { Color.cyan, Color.lightGray };
private static Font smallF = new Font("Georgia", Font.PLAIN, 8);
private int[] curPos;
private TextLayout[] layouts;
private Font[] fonts;
private long sleepAmount = 900;
private Thread thread;
private BufferedImage bimg;
public void init() {
final Graphics2DTest g2d = new Graphics2DTest();
setBackground(Color.white);
fonts = new Font[2];
layouts = new TextLayout[fonts.length];
curPos = new int[fonts.length];
}
public void reset(int w, int h) {
fonts[0] = new Font("Georgia",Font.PLAIN,w/text[0].length()+8);
fonts[1] = new Font("serif", Font.BOLD,w/text[1].length());
for (int i = 0; i < layouts.length; i++ ) {
curPos[i] = 0;
}
}
public void step(int w, int h) {
for (int i = 0; i < 2; i++) {
if (layouts[i] == null) {
continue;
}
if (curPos[i]++ == layouts[i].getCharacterCount()) {
curPos[i] = 0;
}
}
}
public void drawg2d(int w, int h, Graphics2D g2) {
FontRenderContext frc = g2.getFontRenderContext();
for (int i = 0; i < 2; i++) {
layouts[i] = new TextLayout(text[i], fonts[i], frc);
float rx = (float) (w/2-layouts[i].getBounds().getWidth()/2);
float ry = (float) ((i == 0) ? h/3 : h * 0.75f);
float rw = (float) (layouts[i].getBounds().getWidth());
float rh = (float) (layouts[i].getBounds().getHeight());
Shape hilite = layouts[i].getLogicalHighlightShape(0, curPos[i]);
AffineTransform at = AffineTransform.getTranslateInstance(rx, ry);
hilite = at.createTransformedShape(hilite);
float hy = (float) hilite.getBounds().getY();
float hh = (float) hilite.getBounds().getHeight();
g2.setColor(colors[i]);
g2.fill(hilite);
Shape[] shapes = layouts[i].getCaretShapes(curPos[i]);
Shape caret = at.createTransformedShape(shapes[0]);
g2.setColor(Color.black);
layouts[i].draw(g2, rx, ry);
g2.draw(caret);
g2.draw(new Rectangle2D.Float(rx,hy,rw,hh));
for (int j = 0; j <= layouts[i].getCharacterCount(); j++) {
float[] cInfo = layouts[i].getCaretInfo(TextHitInfo.leading(j));
String str = String.valueOf((int) cInfo[0]);
TextLayout tl = new TextLayout(str,smallF,frc);
tl.draw(g2, (float) rx+cInfo[0], hy+hh+tl.getAscent()+1.0f);
}
}
}
public Graphics2D createGraphics2D(int w, int h) {
Graphics2D g2 = null;
if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
bimg = (BufferedImage) createImage(w, h);
reset(w, h);
}
g2 = bimg.createGraphics();
g2.setBackground(getBackground());
g2.clearRect(0, 0, w, h);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
return g2;
}
public void start() {
thread = new Thread(this);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
public void paint(Graphics g) {
Dimension d = getSize();
step(d.width, d.height);
Graphics2D g2 = createGraphics2D(d.width, d.height);
drawg2d(d.width, d.height, g2);
g2.dispose();
g.drawImage(bimg, 0, 0, this);
}
public Synchronized void stop() {
thread = null;
}
public void run() {
try {
thread.sleep(1000);
} catch (InterruptedException e) { return; }
Thread me = Thread.currentThread();
while (thread == me) {
repaint();
try {
thread.sleep(sleepAmount);
} catch (InterruptedException e) { break; }
}
thread = null;
}
}
C:\JavaExample\14>javac Graphics2DTest.java
C:\JavaExample\14>appletviewer Graphics2DTest.html
조 금 긴 것 같은 예제였지만 별로 길지는 않다? 이 예제는 실행해 보면 알겠지만 글씨가 써진 블록 부분이 조금씩 색으로 채워지는 Applet이 나타난다. Applet은 다음 장에서 자세히 설명하겠다. 앞에서 13장 AWT에서 보아왔던 것과는 조금 다르다고 느낄 것이다. 위의 두 개의 파일은 따로 저장해야 한다. Applet은 html로 시작한다. 그 것이 특이한 점 중 하나이다. 예제를 보면 알겠지만 main() 메서드가 없다. 대신에 html이 로딩을 시켜주며 처음 시작은 init() 메서드부터 시작한다.
2D Graphics는 java.awt.geom 패키지에 있는 클래스들로 여러 가지 형태의 도형(곡선, 호, 선 등)들을 그릴 수 있다. 그리고 그 도형을 Graphics2d 클래스의 객체를 사용하여 도형의 기능들을 더욱 강력하게 만들어 준다. 먼저 java.awt.geom 패키지를 import시켜야 한다.
import java.awt.geom.Rectangle2D;
import java.awt.geom.AffineTransform;
여 기에서는 직접 클래스를 import시켰다. Rectangle2D 클래스는, 위치(x, y)및 사이즈(width X height)로 정의되는 사각형을 기술한다. AffineTransform 클래스는 측도 변환을 비롯해 좌표계의 이동 및 회전을 모두 포함하는 좌표변환을 하는 클래스이다. 사용자 공간에서 디바이스 공간으로 도형을 옮겨 표현할 때, affine transform적용된다. 사용자 공간(User Space)이란 프로그래머 혹은 프로그램에서 사용하는 디바이스 독립한 좌표공간을 말하며, 디바이스 공간(Device Space)이란 출력 장치가 사용하는 좌표 공간이다. 어려워지나?
또 다른 패키지의 클래스를 import시켰다.
import java.awt.font.TextLayout;
import java.awt.font.TextHitInfo;
import java.awt.font.FontRenderContext;
TextLayout 클래스는 여러 가지 기능이 있다만 여기에서는 커서의 위치 지정과 이동 기능을 하는 역할을 한다. TextHitInfo 클래스는 텍스트 모델내의 문자의 위치 및 그 문자의 bias or side를 나타낸다. bias는, leading(왼쪽에서 오른쪽으로 기술하는 문자의 왼쪽 끝) 또는 trailing(왼쪽에서 오른쪽으로 기술하는 문자의 오른쪽 끝)의 어느 쪽인가 이다. TextHitInfo 클래스의 객체는 텍스트내의 글자 및 삽입 위치를 지정하기 위해서 사용된다. FontRenderContext 클래스는 텍스트의 size를 올바르게 측정하는데 필요한 정보의 컨테이너이다. 텍스트의 size는 아우트라인을 픽셀에 표시하는 규칙이나, 어플리케이션이 제공하는 표현 힌트에 의해 바뀌는 경우가 있다. 그렇지만 위의 클래스가 설명한 기능만 하는 것이 아님을 알아야 한다.
public class Graphics2DTest extends Applet implements Runnable { }
예 제가 Applet을 상속받았다. 그리고 Runnable 인터페이스를 implements하였다. 여기서는 특이하게 final로 지정하고 있는 데 그 이유는 시간의 흐름에 따라 스레드바가 변하는데 변하는 동안 스레드바를 제외한 텍스트들과 TextLayout, Font, 스레드바의 Color 등이 변하지 않게 하기 위해서 이다.
위에서 Applet은 html로 시작한다고 말했다. 그리고 시작은 init() 메서드부터 이다.
public void init() { }
이 제 init() 메서드부터 시작해 볼까? 참, Applet은 주기라는 것이 있다. init() → start() → paint() → stop() → destroy() 이렇게 자동으로 실행이 된다. 편리한 놈이다. init() 메서드 안에서 객체를 생성시켰다.
public void init() {
final Graphics2DTest g2d = new Graphics2DTest();
setBackground(Color.white);
fonts = new Font[2];
layouts = new TextLayout[fonts.length];
curPos = new int[fonts.length];
}
여기에서 생성된 3개의 객체에서 fonts는 두 개의 폰트 배열을 나타내고, layout은 폰트 배열의 개수만큼 TextLayout 배열을 나타내며, curPos는 폰트 배열의 개수만큼의 int형 배열이다.
init() 메서드는 자동으로 start() 메서드를 호출한다. 호출된 start() 메서드에서 thread를 생성하고, 이 thread를 start시켰다.
public void start() {
thread = new Thread(this);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
여 기에서 처음에 실행된 메서드는 Applet의 start() 메서드이고, 그 안에서 호출된 start() 메서드는 Thread의 start() 메서드이다. Thread의 start() 메서드는 run() 메서드를 호출한다. 알고 있을 것이다?
public void run() {
try {
thread.sleep(1000);
} catch (InterruptedException e) { return; }
Thread me = Thread.currentThread();
while (thread == me) {
repaint();
try {
thread.sleep(sleepAmount);
} catch (InterruptedException e) { break; }
}
thread = null;
}
여기에서 thread의 정지시간을 1000ms(1초)으로 주고 현재 실행중인 thread가 자기의 것이라면 repaint 해 준다. 그리고 정해준 시간만큼 또 정지하게 된다.
위에서 Applet의 start() 메서드를 호출하였는데, 이 메서드는 paint(Graphics g) 메서드를 호출하게 된다.
public void paint(Graphics g) {
Dimension d = getSize();
step(d.width, d.height);
Graphics2D g2 = createGraphics2D(d.width, d.height);
drawg2d(d.width, d.height, g2);
g2.dispose();
g.drawImage(bimg, 0, 0, this);
}
이 paint(Graphics g) 메서드가 가장 핵심적인 부분이다. 먼저 getSize() 메서드를 사용하여 Dimension 클래스의 객체 d를 만들었다. 그리고 d.width와 d.height를 매개변수로 사용하여 step() 메서드를 호출하였다. 그리고 createGraphics2D(d.width, d.height) 메서드를 사용하여 Graphics2d 클래스의 객체 g2를 생성하였다.
public Graphics2D createGraphics2D(int w, int h) {
Graphics2D g2 = null;
if (bimg == null || bimg.getWidth() != w || bimg.getHeight() != h) {
bimg = (BufferedImage) createImage(w, h);
reset(w, h);
}
g2 = bimg.createGraphics();
g2.setBackground(getBackground());
g2.clearRect(0, 0, w, h);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
return g2;
}
이 메서드 속을 들여다보자. 먼저 if문을 사용하여 BufferedImage 클래스의 bimg 객체가 null 값을 갖거나, 이미지의 크기가 다르다면 reset(w, h) 메서드를 사용해서 폰트 배열 객체를 생성하고 있다. BufferedImage 클래스의 멤버 메서드인 createGraphics()를 사용하여 Graphics2d 클래스의 객체 g2를 생성하였다. 이렇게 생성된 Graphics2d 클래스의 g2로 setBackground(getBackground()) 메서드를 사용하여 Graphics2D 문맥의 백그라운드 칼라를 설정한다. 백그라운드 칼라는, 영역을 clear하기 위해서 사용된다. 그 후 clearRect() 메서드를 사용하여 영역을 clear하였다. 그리고 setRenderingHint() 메서드를 사용하여 표현 알고리즘의 추천 설정의 값을 1개 설정하였죠. 그 후 객체 g2를 리턴시켰다.
다시 paint() 메서드로 돌아와서 d.width, d.height, 그리고 g2를 매개변수로 써서 drawg2d() 메서드를 호출하였다.
public void drawg2d(int w, int h, Graphics2D g2) { }
그 리고 g2.getFontRenderContext() 메서드를 사용하여 FontRenderContext 클래스의 객체 frc를 생성하였다. getFontRenderContext() 메서드는 Graphics2d 클래스 문맥에서의 Font의 표현 문맥을 리턴 한다. 말이 좀 어렵다. 그냥 표현해야 하는 정보를 반환하는 정도로 생각하셔도 무방할 듯 싶다. 그리고 for문을 사용하여 여러 가지 값들과 메서드들을 호출하였다.
FontRenderContext frc = g2.getFontRenderContext();
for (int i = 0; i < 2; i++) {
// 여러 가지 값과 메서드들이 들어간다.
}
먼저 layouts 객체배열을 구했다. TextLayout 클래스의 생성자 메서드를 이용하여 값을 결정하였다. 그리고 다른 메서드들의 매개변수의 값들을 구했다.
layouts[i] = new TextLayout(text[i], fonts[i], frc);
float rx = (float) (w/2-layouts[i].getBounds().getWidth()/2);
float ry = (float) ((i == 0) ? h/3 : h * 0.75f);
float rw = (float) (layouts[i].getBounds().getWidth());
float rh = (float) (layouts[i].getBounds().getHeight());
계 속해서 getLogicalHighlightShape(int firstEndpoint, int secondEndpoint) 메서드를 사용해서 지정된 범위의 논리적인 선택 범위를 둘러싸는 Shape를, 이 TextLayout의 자연 경계까지 확장해 돌려준다. 그리고 AffineTransform클래스의 TranslateInstance(double tx, double ty) 메서드를 사용하여 평행이동 변환을 표현하는 변환을 얻어내고, createTransformedShape(Shape pSrc) 메서드를 사용하여 지정된 Shape를 이 변환에 의해 변환해, 그 Shape의 구조에 의해 정의 되는 새로운 Shape클래스의 객체를 돌려준다.
Shape hilite = layouts[i].getLogicalHighlightShape(0, curPos[i]);
AffineTransform at = AffineTransform.getTranslateInstance(rx, ry);
hilite = at.createTransformedShape(hilite);
또 여기서, 메서드에 사용될 매개변수를 구했다. getBound() 메서드는 Shape를 완전하게 둘러싸는 정수의 Rectangle을 반환한다
float hy = (float) hilite.getBounds().getY();
float hh = (float) hilite.getBounds().getHeight();
그 다음 색을 설정하고, 그 도형의 내부를 색칠을 한다. 그리고 문자와 Rectangle을 그려준다.
g2.setColor(colors[i]);
g2.fill(hilite);
Shape[] shapes = layouts[i].getCaretShapes(curPos[i]);
Shape caret = at.createTransformedShape(shapes[0]);
g2.setColor(Color.black);
layouts[i].draw(g2, rx, ry);
g2.draw(caret);
g2.draw(new Rectangle2D.Float(rx,hy,rw,hh));
이렇게 paint(Graphics g) 메서드를 설명하였다. 정말 길다. Thread가 repaint() 메서드를 호출하면 다시 paint(Graphics g) 메서드를 실행시켜주면 된다.
상 당히 어려운 예제였다. 하지만 천천히 다시 살펴보면 처음보다는 쉽다. Graphics2d 클래스는 Graphics 클래스를 상속 받아 생성된 클래스이다. 그래서 Graphics 클래스의 모든 멤버 메서드를 사용할 수 있다. 거기에 자기의 메서드들이 더해지고 java.awt.geom 패키지나 java.awt.font 패키지의 라이브러리를 사용하여 강력한 기능을 나타내고 있다. 메서드들의 반환형들이 생소한 타입들이 많이 나오는 편이다. 그래서 많이들 어려워한다. 하지만 사용만 할 수 있다면 더욱 멋진 Graphic을 사용할 수 있다.
|
|
|
|
|
|