https://appframework.dev.java.net/
https://appframework.dev.java.net/intro/index.html
http://weblogs.java.net/blog/hansmuller/archive/ts-3399-final.pdf
스윙 API가 너무 복잡하니까 단순하게 해보자는 것입니다. 일단 다음과 같이 JFrame이나 content pane 같은 개념이 사라집니다.
public class SingleFrameExample1 extends SingleFrameApplication {
public void startup(String[] args) {
JLabel label = new JLabel(" Hello World ");
label.setFont(new Font("LucidaSans", Font.PLAIN, 32));
show(label);
}
public static void main(String[] args) {
launch(SingleFrameExample1.class, args);
}
}
리소스로 label 의 설정을 분리 가능합니다. 먼저 코딩은 다음과 같이 합니다.
public class SingleFrameExample2 extends SingleFrameApplication {
public void startup(String[] args) {
JLabel label = new JLabel();
label.setName("label");
show(label);
}
public static void main(String[] args) {
launch(SingleFrameExample2.class, args);
}
}
그리고 별도의 파일로 label을 정의합니다.
label.opaque = true label.background = 0, 0, 0 label.foreground = 255, 255, 255 label.text = Hello World label.font = Lucida-PLAIN-48 label.icon = earth.png
버튼이나 메뉴를 누르면 하는 동작은 다음과 같이 정의한다음
/**
* Load the specified file into the textPane or popup an error
* dialog if something goes wrong.
*/
@Action public void open() {
JFileChooser chooser = new JFileChooser();
int option = chooser.showOpenDialog(getMainFrame());
if (option == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
textPane.setPage(file.toURI().toURL());
// error handling omitted for clarity
}
}
/**
* Replace the contents of the textPane with the value of the
* "defaultText" resource.
*/
@Action public void close() {
ApplicationContext ac = ApplicationContext.getInstance();
String ac.getResourceMap(getClass()).getString("defaultText");
textPane.setText(defaultText);
}
메뉴에다가 action을 붙일때는 다음과 같이 합니다.
openMenuItem.setAction(getAction("open"));
closeMenuItem.setAction(getAction("close"));
물론 메뉴의 설정도 리소스 파일로 합니다.
open.Action.text = &Open... open.Action.accelerator = control O open.Action.shortDescription = open a document close.Action.text = &Close close.Action.shortDescription = close the document
백그라운드 쓰레드로 하는 작업도 다음과 같이 단순히 합니다.
class DoNothingTask extends Task {
@Override protected Void doInBackground() throws InterruptedException {
for(int i = 0; i < 10; i++) {
setMessage("Working... [" + i + "]");
Thread.sleep(150L);
setProgress(i, 0, 9);
}
Thread.sleep(150L);
return null;
}
@Override protected void done() {
setMessage(isCancelled() ? "Canceled." : "Done.");
}
}
Task는 SwingWorker를 확장한 클래스입니다.
이처럼 백그라운드 작업시 UI의 응답성을 보장하기 위해서는 UI가 응답하는 Event Dispatching Thread(EDT)에서는 긴 시간을 필요로하는 작업을 하면 안됩니다. 대신 별도의 쓰레드로 작업을 한 뒤 그 별도의 쓰레드에서 UI갱신을 할 때는 다음과 같이 했었습니다.
void printTextField() throws Exception { // 백그라운드 쓰레드
final String[] myStrings =
new String[2];
Runnable getTextFieldText =
new Runnable() {
public void run() {
myStrings[0] =
textField0.getText();
myStrings[1] =
textField1.getText();
}
};
SwingUtilities.invokeAndWait // 이벤트 디스패치 쓰레드를 통해 UI작업을 수행하도록 함
(getTextFieldText);
System.out.println(myStrings[0]
+ " " + myStrings[1]); // 그 뒤 이 쓰레드에서 UI의 값을 얻어올 수 있다
}
물론 복잡하고 귀찮은 일입니다. Java6부터는 이를 SwingWorker란 클래스로 분리했습니다.
final JLabel label;
class MeaningOfLifeFinder extends SwingWorker<String, Object> {
@Override
public String doInBackground() { // 여기서 긴 시간의 작업
return findTheMeaningOfLife();
}
@Override
protected void done() { // 여기서 긴 시간의 작업이 끝나고 UI를 갱신할 코드
try {
label.setText(get());
} catch (Exception ignore) {
}
}
}
(new MeaningOfLifeFinder()).execute(); // 백그라운드 쓰레드 시작
Task는 이 새로운 방식을 받아들인 클래스입니다.
이제야 Swing이 쓸만해진 상태에 다다른듯해 보이네요.
Post a Comment