반응형




개발 도구
소스코드 관리
빌드 스크립트 도구
빌드 시스템
CI 도구 (Continuous integration )
이슈 추적 도구
테스트 프레임워크
프로젝트 관리
커뮤니케이션 도구, 위키
성능분석
기타
출처 : 개발이 좋아 개발자가된 많은 사람들에게 말하고 싶은 이야기. by k16wire
http://moai.tistory.com/notice/270
스윙에서의 Drag & Drop 을 구현해 보자. 과정이 꽤 복잡하고 기능상에 약간의 제약은 있으나 한번 구현해 볼 만한 예제라고 생각을 한다. 소스코드는 자바소프트에서 발췌했음을 알아두길 바란다. 중간중간에 풍선도움말을 두겠으니 단풍잎에 마우스를 갖다대면 도움말이 뜨니 참고하길 바란다.
| Drag & Drop 을 구현하는 것은 비교적 쉽다. 그렇지만 가장 난해한 것은 모든 과정에 대해서 이해하는 것이다. 다음의 순서를 먼저 익혀두길 바란다. [ DnD Source Download] 1. Drag source 에 대한 참조를 가진다. - DragSource.getDefaultSource() 나 new DragSource() 를 통해서. 2. Drag gesture recognizer 를 생성한다. - DragSource.createDafaultGestureRecognizer() 3. Drop target 를 생성한다. - 하나의 컴포넌트와 Drop target listener 를 명세한다. 4. Transferable 로 옮겨질 수 있는 데이타를 wrap 한다. 5. Drag 를 초기화한다. - DragSource.startDrag() 6. DropTargetLisenter, DragSource 인터페이스를 구현함으로써 Drop 를 핸들링한다. Drag & Drop (이하 D&D) 는 하나의 datasource 와 여러개의 droptarget 에 - 흔히 콤포넌트와 연관되어져 있다 - 와 연관되어 이뤄진다. 다음에 나열된 클래스와 인터페이스는 D&D 에서 사용되는 것을 정리한 것이다. 대부분의 것들은 java.awt.dnd 패키지에 있고, 오직 하나 Transferable 은 java.awt.datatransfer 패키지에 있다. DragGestureRecognizer : 컴포넌트에서 drag 를 수행할려고 할때 이벤트를 발사한다. DragSource 는 startDrag() 메소드로 초기화작업을 수행하고, createDragGestureRecognizer() 나 createDafaultDragGestureRecognizer() 를 이용하여 DragGestureRecognizer 를 생성하게 되는 것이다. DropTarget 은 하나의 컴포넌트와 Listener 객체와 연관되어 있어서 drop target event 가 발생할때 listener 객체가 통보를 받게 된다. DragGestureListener 는 recognizer 에 의해 drag gesture 를 통보받는데 전형적인 반응예는 DragSource.startDrag() 메소들르 호출하게 되었을 경우이다. DragSourceListener 는 drag 가 초기화된 후 dragsource 에서 일어나게 된다. 다소 나열적인 설명이었지만 어느 정도 감을 잡기 위해서 중복 설명했다. D&D 하는 과정과 메소드호출, 클래스와 인터페이스의 API 를 꼼꼼하게 살펴보면 이해할 수 있으리라 생각한다. 가장 기본적인 D&D 를 하기 위해서는 반드시 DragSource, DropTarget, DragGestureRecognizer, Transferable 이 반드시 생성이 되어야 하고, Listener 가 구현되어야 한다는 것을 명심하길 바란다. 그러나 실제 구현은 몇가지 객체를 생성하고 데이타를 Wrapping 하고 핸들링하는 것을 빼면 나머지 동작은 프로그래머 재량에 상관없이 자체적으로 구현이 된다. |
| 여기서도 두가지 방법이 있을 수가 있는데 한가지 방법으로는 Swing Component 를 상속해 객체를 만들어 그것을 DragSource, DropTarget 으로 만들어 사용하는 방법과 제 3의 콤포넌트를 생성해 두가지를 수행하는 방법이 있다. 첫번째 방법으로 수행을 하면 반드시 객체만이 D&D 특성을 가질수가 있고, 표준 Component 는 그런 특성을 공유할 수 없음을 명심하길 바란다. 가령 JList 를 상속한 ListDragSource 객체를 만들어서 D&D 를 추가해 사용한다면 ListDragSource 만이 그런 동작을 할 수 있다는 것을 의미한다. 아래에 있는 그림은 이 프로그램을 실행시켰을때 볼 수 있는 화면이다. 왼쪽은 DragSource 로 JTree 를 이용해 구현했고, 오른쪽은 DropTarget 은 JTextPane 을 이용해 구현했음을 보길 바란다. 이것은 .txt 와 .java 로만 한정되어 있기 때문에 다른 확장자를 가진 파일을 D&D 했을 경우에는 에러창을 발견하게 될 것이다. 그리고 .txt 와 .java 파일은 그 내용을 JTextPane 에서 볼 수 있을 것이다. 오른쪽의 파일을 D&D 수행할 수가 있다 확장자가 .txt 와 .java 가 아닐 경우에 볼 수 있는 경고창이다. 위의 프로그램은 얼마든지 응용해서 확장할 수 있다고 생각한다. 다른 파일도 볼 수 있게끔 할 수 있고, image 도 가능하리라 생각한다. 다음 장에서는 소스코드 분석에 들어가도록 하겠다. 먼저 해당 API 를 꼼꼼히 살펴본 다음에 코드분석에 임하길 바란다. |
| Test.java 소스이다. main() 와 기본 인터페이스가 설정돼 있다. 아래의 코드를 살펴보면 알겠지만 Test 생성자는 drop target 을 생성해낸다. 즉 TextPane 을 drop target component 로 생성하는 것이다. 실제 drop 이 일어났을때 Test.drop() 메소드가 호출이 된다. drop 과 연관된 transferable 은 String 으로써 데이타를 제공해주며 drop 을 받아들인 후에 readFile() 메소드가 호출이 돼 file 의 내용을 TextPane 으로 로딩을 하게 된다. Drop 이 완료된 후 e.dropComplete(true) 가 호출이 돼 실제 drop 이 완료되게 된다. import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; import java.awt.*; import java.awt.event.*; import java.awt.datatransfer.*; import java.awt.dnd.*; import java.io.*; public class Test extends JFrame implements DropTargetListener { private JTextPane textPane = new JTextPane(); public Test() { super("Drag and Drop With Swing"); new DropTarget(textPane,DnDConstants.ACTION_COPY_OR_MOVE,this); JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,createTreePanel(),createTextPanel()); splitPane.setDividerLocation(250); splitPane.setOneTouchExpandable(true); getContentPane().add(splitPane, BorderLayout.CENTER); } public static void main(String args[]) { Test test = new Test(); test.setBounds(300,300,850,350); test.setVisible(true); test.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); test.addWindowListener(new WindowAdapter() { public void windowClosed(WindowEvent e) { System.exit(0); } }); } private JPanel createTreePanel() { JPanel treePanel = new JPanel(); DragTree tree = new DragTree(); treePanel.setLayout(new BorderLayout()); treePanel.add(new JScrollPane(tree), BorderLayout.CENTER); treePanel.setBorder(BorderFactory.createTitledBorder( "Drag source for filenames")); return treePanel; } private JPanel createTextPanel() { JPanel textPanel = new JPanel(); textPanel.setLayout(new BorderLayout()); textPanel.add(new JScrollPane(textPane),BorderLayout.CENTER); textPanel.setMinimumSize(new Dimension(375,0)); textPanel.setBorder(BorderFactory.createTitledBorder( "Drop target for filenames")); return textPanel; } private void readFile(final String filename) { EditorKit kit = textPane.getEditorKit(); Document document = textPane.getDocument(); try { document.remove(0,document.getLength()); kit.read(new FileReader(filename), document, 0); } catch(Exception ex) { ex.printStackTrace(); } } public void drop(DropTargetDropEvent e) { try { DataFlavor stringFlavor = DataFlavor.stringFlavor; Transferable tr = e.getTransferable();
|

var x; var x : String; var x = z + 22; var x = bind f(q);
javafxc tutorial/one.fx tutorial/two.fx javafx tutorial/two
Hello from one.fx!
Hello from one.fx! (in function printMessage)
// Inside file tutorial/one.fx package tutorial; public-read var x = 1; // Inside tutorial/two.fx package tutorial; println(one.x);
javafxc tutorial/one.fx tutorial/two.fx javafx tutorial/two
1// Inside tutorial/two.fx package tutorial; one.x = 2; println(one.x);
tutorial/two.fx:3: x has script only (default) write access in tutorial.one
one.x = 2;
^
1 error
// Inside file tutorial/one.fx package tutorial; package public-read var x = 1; // Inside tutorial/two.fx package tutorial; one.x = 2; println(one.x);
// Inside file tutorial/one.fx
package tutorial;
public class one {
public-init var message; //초기화 접근 속성만을 제공함.
}
// Inside file two.fx
import tutorial.one;
var o = one {
message: "Initialized this variable from a different package!"
}
println(o.message);//읽기 속성은 public 이므로 아무런 문제가 없다.
|
// Inside file two.fx
import tutorial.one;
var o = one {
message: "Initialized this variable from a different package!"
}
o.message = "Changing the message..."; // WON'T COMPILE
println(o.message);
|
two.fx:12: message has script only (default) write access in tutorial.one
o.message = "Changing the message..."; // WON'T COMPILE
^
1 error
package addressbook;
class Address {
var street: String;
var city: String;
var state: String;
var zip: String;
}
package addressbook;
public class Address {
public var street: String;
public var city: String;
public var state: String;
public var zip: String;
}
// Approach #1
addressbook.Address {
street: "1 Main Street";
city: "Santa Clara";
state: "CA";
zip: "95050";
} |
// Approach #2
import addressbook.Address;
Address {
street: "1 Main Street";
city: "Santa Clara";
state: "CA";
zip: "95050";
}
|
def customer = Customer {
firstName: "John";
lastName: "Doe";
phoneNum: "(408) 555-1212"
address: Address {
street: "1 Main Street";
city: "Santa Clara";
state: "CA";
zip: "95050";
}
}
customer.printName();
customer.printPhoneNum();
customer.printAddress();
class Address {
var street: String;
var city: String;
var state: String;
var zip: String;
}
class Customer {
var firstName: String;
var lastName: String;
var phoneNum: String;
var address: Address;
function printName() {
println("Name: {firstName} {lastName}");
}
function printPhoneNum(){
println("Phone: {phoneNum}");
}
function printAddress(){
println("Street: {address.street}");
println("City: {address.city}");
println("State: {address.state}");
println("Zip: {address.zip}");
}
}
abstract class Account {
var accountNum: Integer;
var balance: Number;
function getBalance(): Number {
return balance;
}
function deposit(amount: Number): Void {
balance += amount;
}
function withdraw(amount: Number): Void {
balance -= amount;
}
}
|
class SavingsAccount extends Account {
var minBalance = 100.00;
var penalty = 5.00;
function checkMinBalance() : Void {
if(balance < minBalance){
balance -= penalty;
}
}
}
|
class CheckingAccount extends Account {
var hasOverDraftProtection: Boolean;
override function withdraw(amount: Number) : Void {
if(balance-amount<0 and hasOverDraftProtection){
// code to borrow money from an overdraft account would go here
} else {
balance -= amount; // may result in negative account balance!
}
}
}
var myStreet = "1 Main Street";
var myCity = "Santa Clara";
var myState = "CA";
var myZip = "95050";
def address = bind Address {
street: myStreet;
city: myCity;
state: myState;
zip: myZip;
};
println("address.street == {address.street}");
myStreet = "100 Maple Street";
println("address.street == {address.street}");
address.street == 1 Main Street
address.street == 100 Maple Street
def address = bind Address {
street: bind myStreet;
city: bind myCity;
state: bind myState;
zip: bind myZip;
};
def address = Address {
street: bind myStreet;
city: bind myCity;
state: bind myState;
zip: bind myZip;
};
var scale = 1.0;
bound function makePoint(xPos : Number, yPos : Number) : Point {
Point {
x: xPos * scale
y: yPos * scale
}
}
class Point {
var x : Number;
var y : Number;
}
var myX = 3.0;
var myY = 3.0;
def pt = bind makePoint(myX, myY);
println(pt.x);
myX = 10.0;
println(pt.x);
scale = 2.0;
println(pt.x); |
3.0
10.0
20.0
3.0
10.0
10.0
var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
printSeqs();
function printSeqs() {
println("First Sequence:");
for (i in seq1){println(i);}
println("Second Sequence:");
for (i in seq2){println(i);}
}
First Sequence:
1
2
3
4
5
6
7
8
9
10
Second Sequence:
2
4
6
8
10
12
14
16
18
20var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
insert 11 into seq1;
printSeqs();
function printSeqs() {
println("First Sequence:");
for (i in seq1){println(i);}
println("Second Sequence:");
for (i in seq2){println(i);}
}
|
First Sequence:
1
2
3
4
5
6
7
8
9
10
11
Second Sequence:
2
4
6
8
10
12
14
16
18
20
22
var password = "foo" on replace oldValue {
println("\nALERT! Password has changed!");
println("Old Value: {oldValue}");
println("New Value: {password}");
};
password = "bar";
|
ALERT! Password has changed!
Old Value:
New Value: foo
ALERT! Password has changed!
Old Value: foo
New Value: bar