Swing Application Framework – JSR296

Tags:

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... &#91;" + i + "&#93;");
	    Thread.sleep(150L);
	    setProgress(i, 0, 9);
	}
	Thread.sleep(150L);
	return null;
    }
    @Override protected void done() {
	setMessage(isCancelled() ? "Canceled." : "Done.");
    }
}
&#91;/code&#93;

Task는 SwingWorker를 확장한 클래스입니다.

이처럼 백그라운드 작업시 UI의 응답성을 보장하기 위해서는 UI가 응답하는 Event Dispatching Thread(EDT)에서는 긴 시간을 필요로하는 작업을 하면 안됩니다. 대신 별도의 쓰레드로 작업을 한 뒤 그 별도의 쓰레드에서 UI갱신을 할 때는 다음과 같이 했었습니다.

&#91;code lang="java"&#93;
void printTextField() throws Exception { // 백그라운드 쓰레드
    final String&#91;&#93; myStrings = 
       new String&#91;2&#93;;

    Runnable getTextFieldText = 
      new Runnable() {
        public void run() {
            myStrings&#91;0&#93; = 
               textField0.getText();
            myStrings&#91;1&#93; = 
               textField1.getText();
        }
    };
    SwingUtilities.invokeAndWait // 이벤트 디스패치 쓰레드를 통해 UI작업을 수행하도록 함
      (getTextFieldText);

    System.out.println(myStrings&#91;0&#93; 
                       + " " + myStrings&#91;1&#93;); // 그 뒤 이 쓰레드에서 UI의 값을 얻어올 수 있다
}
&#91;/code&#93;

물론 복잡하고 귀찮은 일입니다. Java6부터는 이를 <a href="http://java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.html">SwingWorker</a>란 클래스로 분리했습니다.

[code lang="java"]
   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이 쓸만해진 상태에 다다른듯해 보이네요.