Drag & Drop의 구현

스윙에서의 Drag & Drop 을 구현해 보자. 과정이 꽤 복잡하고 기능상에 약간의 제약은 있으나 한번 구현해 볼 만한 예제라고 생각을 한다. 소스코드는 자바소프트에서 발췌했음을 알아두길 바란다. 중간중간에 풍선도움말을 두겠으니 단풍잎에 마우스를 갖다대면 도움말이 뜨니 참고하길 바란다.

Drag & Drop step
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 : Drag 와 DragGestureRecognizer 를 초기화한다.
DropTarget : 연관된 컴포넌트 상에서 Drop 이 일어난다.
Transferable ; D&D 를 통해서 전송되어지는 데이타를 위한 Wrapper 이다.
DragGestureListener : DragGestureRecognizer 에 의해 통보를 받고 drag 를 초기화한다.
DragSourceListener : DragSource 이벤트에 반응한다.
DropTargetListener : Drop 포함한 target 이벤트를 핸들링한다.

D&D 는 하나의 기미(Gesture) 즉, 마우스를 내려서 드래깅을 시도할때 초기화된다. 그러면 DragGestureRecognizer 는 이런 기미를 포착하고 이벤트를 발생하는 것이다.
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 하고 핸들링하는 것을 빼면 나머지 동작은 프로그래머 재량에 상관없이 자체적으로 구현이 된다.
Adding D&D to Swing Component
여기서도 두가지 방법이 있을 수가 있는데 한가지 방법으로는 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 를 꼼꼼히 살펴본 다음에 코드분석에 임하길 바란다.


 
  

이번 장에서는 소스분석과 함께 설명을 하겠다. 단풍잎 부분에 API 에 대한 설명을 첨가하겠다.

Source Analysis
DragTree.java 소스이다. 이전 장에서 본 JTree 부분의 DragSource 를 정의한 부분이다.
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import java.io.*;

class DragTree extends JTree implements DragGestureListener, DragSourceListener {

     public DragTree() {
       DragSource dragSource = DragSource.getDefaultDragSource();
         // static 메소드인 위 메소드로 DragSource 에 대한 참조를 얻는다.
            그리고, 위 참조객체를 이용해 DragGestureRecognizer 를 생성하게 되는 것이다.
  
       dragSource.createDefaultDragGestureRecognizer(
	 this, // component where drag originates
	 DnDConstants.ACTION_COPY_OR_MOVE, // actions
	 this); // drag gesture recognizer  
  public DragGestureRecognizer createDefaultDragGestureRecognizer(
                      Component c, int actions, DragGestureListener dgl)

       c - The Component target for the recognizer
       actions - The permitted source actions
       dgl - The DragGestureListener to notify
setModel(createTreeModel()); // Tree 에 관한 코딩부분으로 innerclass 로 구현돼 있다. addTreeExpansionListener(new TreeExpansionListener(){ public void treeCollapsed(TreeExpansionEvent e) {} public void treeExpanded(TreeExpansionEvent e) { TreePath path = e.getPath(); if(path != null) { FileNode node = (FileNode) path.getLastPathComponent(); if( ! node.isExplored()) { DefaultTreeModel model = (DefaultTreeModel)getModel(); node.explore(); model.nodeStructureChanged(node); } } } }); } DragGestureListener 인터페이스에서 구현해야 하는 메소드이다. public void dragGestureRecognized(DragGestureEvent e) { String s = getFilename(); // 확장자를 .txt 와 .TXT, .java 로 한정지은 부분이다. 다른 파일까지 읽고 싶으면 이 부분을 수정하면 된다. if(s.endsWith(".txt") || s.endsWith(".TXT") || s.endsWith(".java")) { e.startDrag(DragSource.DefaultCopyDrop, // cursor new StringSelection(s), // transferable
           public void startDrag(Cursor dragCursor,
                      Transferable transferable,
                      DragSourceListener dsl)  throws InvalidDnDOperationException
           여기서는 Transferable 로 StringSelection 클래스가 사용되었다.
           이 클래스는 간단한 java String 을 text 포맷으로 수송하는데 필요한 기능을 
           구현하는 클래스이다.
// startDrag() 메소드로 Tree 는 drag 를 초기화하고, draggesture 에 반응하게 된다. } else { // invokeLater 호출에 대해서 생각해 봐야 한다. 이 메소드는 application thread 가 GUI 를 갱신해야 될 필요가 있을때 사용되어지며 당면해 있는 AWT 이벤트가 수행된 이후에 수행되게 한다. 이 메소드의 사용으로 경고창이 뜨는 시간을 조절할 수가 있게 한다. SwingUtilities.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog( SwingUtilities.getRootPane(DragTree.this), "Only \".txt\" and \".java\" files " + "can be dragged", "Not Draggable", JOptionPane.ERROR_MESSAGE); } }); } } // 이 부분은 DragSourceListener 메소드 구현부분이다. public void dragDropEnd(DragSourceDropEvent e) {} public void dragEnter(DragSourceDragEvent e) {} public void dragExit(DragSourceEvent e) {} public void dragOver(DragSourceDragEvent e) {} public void dropActionChanged(DragSourceDragEvent e) {} public String getFilename() { TreePath path = getLeadSelectionPath(); FileNode node = (FileNode)path.getLastPathComponent(); return ((File)node.getUserObject()).getAbsolutePath(); } private DefaultTreeModel createTreeModel() { File root = new File("C:" + File.separator); FileNode rootNode = new FileNode(root); rootNode.explore(); return new DefaultTreeModel(rootNode); } } // 이 부분은 왼쪽 창의 JTree 인터페이스 설정부분으로 자세한 설명은 안하겠다. class FileNode extends DefaultMutableTreeNode { private boolean explored = false; public FileNode(File file) { setUserObject(file); } public boolean getAllowsChildren() { return isDirectory(); } public boolean isLeaf() { return !isDirectory(); } public File getFile() { return (File)getUserObject(); } public boolean isExplored() { return explored; } public boolean isDirectory() { File file = getFile(); return file.isDirectory(); } public String toString() { File file = (File)getUserObject(); String filename = file.toString(); int index = filename.lastIndexOf(File.separator); return (index != -1 && index != filename.length()-1) ? filename.substring(index+1) : filename; } public void explore() { if(!isDirectory()) return; if(!isExplored()) { File file = getFile(); File[] children = file.listFiles(); for(int i=0; i < children.length; ++i) add(new FileNode(children[i])); explored = true; } } }
이상으로 DragTree.java 소스를 살펴보았다. 설명이 조금 부족한 감이 있으니 자기나름대로 살펴보길 바란다. 다음에는 이 프로그램의 메인코드인 Test.java 에 대해서 살펴보겠다.


 
  
Source Analysis
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();
 public class DataFlavor extends Object implements Externalizable, Cloneable
 
 이 클래스는 D&D 수행할 때 클립보드에 들어가는 데이타의 형태를 제공해주는 클래스이다.
 여기서 사용된 StringFlavor 는 java Unicode String class 을 나타낸다.
 MimeType = "application/x-java-serialized-object" 을 나타내게 된다.
if(e.isDataFlavorSupported(stringFlavor)) { String filename = (String)tr.getTransferData(stringFlavor); e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); // 인자로 넘어온 액션으로 drop 을 받아들인다. readFile(filename); textPane.setCaretPosition(0); // Text 의 첫줄이 TextPane 의 첫줄에 오게끔 설정한다. e.dropComplete(true); } else { e.rejectDrop(); // Drop 을 거절한다. } } catch(IOException ioe) { ioe.printStackTrace(); } catch(UnsupportedFlavorException ufe) { ufe.printStackTrace(); } } // DropTargetListener 메소드를 구현한 부분이다. public void dragEnter(DropTargetDragEvent e) { } public void dragExit(DropTargetEvent e) { } public void dragOver(DropTargetDragEvent e) { } public void dropActionChanged(DropTargetDragEvent e) { } }
출처:http://www.javastudy.co.kr/docs/b612/swing/draganddrop.html

저작자 표시 비영리
Posted by 티엘로

Java Swing - JTable

Java 2008/12/19 12:53


Java Swing - JTable

[class javax.swing.JTable]

 

클래스는 스윙의 테이블 콤포넌트를 나타내며 그것의 동작과 외형을 관리하기 위해 풍부한 API 제공한다.

JTable JComponent 직접 확장하며, TableModelListener, TableColumnModelListener, ListSelectionListener, CellEditorListener, Scrollable 인터페이스를 구현한다.

------

JTable 다음과 같이 세가지 모델을 가진다: TableModel, TableColumnModel, ListSelectionModel.

 

TableModel

모든 table data TableModel 안에 저장된다. TableModel 2-D 배열이나 벡터들의 백터와 같이 2차원 구조를 가진다. TableModel data 저장하는 방법을 규정하는 것은 물론 데이터를 추가하고 조작하며 가져오는 방법을 규정한다.

또한 TableModel 특정 셀이 수정될 있도록 지시하는 역할을 하며, 컬럼의 데이터 형을 지정한다.

JTable TableModel 있는 데이터의 위치는 JTable 의해 표시되는 데이터의 위치와 직접적으로 부합되지 않는다. 부분은 TableColumnModel 의해 보다 낮은 레벨에서 콘트롤된다.

 

TableColumnModel

TableColumnModel TableColumn 인스턴스를 유지하도록 설계된다. TableColumn 각각은 TableModel 데이터의 단일 컬럼을 나타낸다. TableColumn 클래스는 실제 JTableGUI에서 컬럼 표시를 관리한다.

각각의 TableColumn cell renderer, cell editor, table header, table header 위한 cell renderer 수반한다. JTabledl JScrollPane 놓일 , 이런 헤더들 스크롤 구획의 COLUMN_HEADER viewport 놓이게 되며, 그것들은 드래그될 있고 컬럼의 순서나 크기를 바꿀 있다.

Cell renderers  TableCellRenderer 인스턴스이고 Cell editors TableCellEditor 인스턴스이다.

TableColumnModel 역할은 정렬이라든가 컬럼의 선택, 컬럼의 여백 크기 등과 같이 모든 TableColumn 들을 관리하는 것이다.

 

③ ListSelectionModel

여러가지 다른 선택 모드를 지원하기 위해서 TableColumnModel ListSelectionModel 유지해야 한다. ListSelectionModel single 선택은 물론 single-interval multiple-interval 선택을 지원한다.

------

table 함께 스윙에서는 2개의 새로운 이벤트가 추가되었다: TableModelEvent TableColumnModelEvent.

------

 

JTable에서 사용되는 Default Implementations

createDefaultColumnModel()

createDefaultDataModel()

createDefaultSelectionModel()

createDefaultEditors()

createDefaultRenderers()

createDefaultTableHeaders()

 

 

[TableModel 인터페이스]

TableModel 인스턴스는 2차원 배열이나 벡터들의 벡터와 같이 2차원 구조 안에 테이블의 데이터를 저장하는데 사용한다.

getValueAt() 메소드 : 주어진 행과 인덱스로 부터 데이터 객체를 가져온다.

setValueAt() 메소드 : 주어진 위치에 데이터 객체를 제공한다.

getColumnClass() 메소드 : 지정된 컬럼에 저장되어 있는 데이터 객체들을 나타내는 클래스를 반환한다.

getColumnName() 메소드 : 지정된 컬럼의 이름을 String 객체로 반환한다.

getColumnCount() 메소드 : 테이블에 포함된 열의 수를 반환한다.

getRowCount() 메소드 : 테이블에 포함된 행의 수를 반환한다.

isCellEditable() 메소드 : 행과 인덱스로 지정된 셀이 수정가능할 경우 true값을 반환한다.

 

모델은 모델의 데이터가 변견되는 것을 파악할 있도록 TableModelListener 제공한다.


 

[JTable 만드는 순서]

 

1) JTable 객체에 들어갈 TableModel 객체를 준비한다.

   TableModel 객체는 이후에 추상클래스인 AbstractTableModel 클래스로 부터 정의내려진

별도의 TableModel 클래스에 의해 만들어진다.

 )  m_data = new StockTableData();

 

2) JTable 클래스로 JTable 객체를 만든다.

   필요할 경우 JTable 객체에 대해서 setAutoCreateColumnsFromModel(false) 사용하여

컬럼이 데이터 모델로 부터 자동적으로 만들어 지지 않도록 한다.

 )  m_table = new JTable();

      m_table.setAutoCreateColumnsFromModel(false);

 

3) 만들어진 JTable 객체에 준비된 TableModel 객체를 setModel()메소드로 지정한다.

     )  m_table.setModel(m_data);

 

4) JTable 객체 추가될 TableColumn 객체들을 생성한 , TableColumn객체들을

   addColumn()메소드로 만들어진 JTable 객체에 추가한다.

     )  TableColumn column = new TableColumn(컬럼 인덱스, 컬럼 , renderer, editable 여부);

          m_table.addColumn(column);

 

5) getTableHeader() 메소드로 JTableHeader 객체를 만든다.

필요에 따라 setUpdateTableInRealTime() 메소드를 사용하여 헤더 객체의 실시간 업데이트

처리 가능  여부를 지정한다.

   )  JTableHeader header = m_table.getTableHeader();

        header.setUpdateTableInRealTime(false);

 

6) JScrollPane 객체를 만들고, JScrollPane 객체의 Viewport 만들어진 JTable 객체를 추가한다.

   다음에 JScrollPane 객체를 ContentPane 객체에 추가한다.

     )  JScrollPane ps = new JScrollPane();

       ps.getViewport().setBackground(m_table.getBackground());

       ps.getViewport().add(m_table);

       getContentPane().add(ps, BorderLayout.CENTER);

 

- 예제 -

 

import java.awt.*;

import java.awt.event.*;

import java.util.*;

import java.io.*;

import java.text.*;

import javax.swing.*;

import javax.swing.border.*;

import javax.swing.event.*;

import javax.swing.table.*;

 

public class StocksTable extends JFrame

{

   protected JTable m_table;

   protected StockTableData m_data;

   protected JLabel m_title;

      

   public StocksTable()

   {

       super("Stocks Table");

       setSize(600, 400);

       UIManager.put("Table.focusCellHighlightBorder",

       new LineBorder(Color.black, 0));

       m_data = new StockTableData();

       m_title = new JLabel(m_data.getTitle(),

       new ImageIcon("money.gif"), SwingConstants.CENTER);

       m_title.setFont(new Font("Helvetica", Font.PLAIN, 24));

       getContentPane().add(m_title, BorderLayout.NORTH);

            

       m_table = new JTable();

       m_table.setAutoCreateColumnsFromModel(false);

       m_table.setModel(m_data);

            

       for(int k=0; k<m_data.getColumnCount(); k++) {

          DefaultTableCellRenderer renderer = new

             ColoredTableCellRenderer();

          renderer.setHorizontalAlignment

             (StockTableData.m_columns[k].m_alignment);

          TableColumn column = new TableColumn(k,

             StockTableData.m_columns[k].m_width, renderer, null);

          m_table.addColumn(column);

       }

       JTableHeader header = m_table.getTableHeader();

       header.setUpdateTableInRealTime(false);

 

       JScrollPane ps = new JScrollPane();

       ps.getViewport().setBackground(m_table.getBackground());

       ps.getViewport().add(m_table);

       getContentPane().add(ps, BorderLayout.CENTER);

   }

      

   public static void main(String argv[])

   {

       StocksTable frame = new StocksTable();

       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

       frame.setVisible(true);

   }

}

 

class ColoredTableCellRenderer extends DefaultTableCellRenderer

{

       public void setValue(Object value)

       {

             if (value instanceof ColorData) {

                    ColorData cvalue = (ColorData) value;

                    setForeground(cvalue.m_color);

                    setText(cvalue.m_data.toString());

             }

             else if (value instanceof IconData)     {

                    IconData ivalue = (IconData)value;

                    setIcon(ivalue.m_icon);

                    setText(ivalue.m_data.toString());

             }

             else

                    super.setValue(value);

       }

}

 

class ColorData

{

       public Color m_color;

       public Object m_data;

       public static Color GREEN = new Color(0, 128, 0);

       public static Color RED = Color.red;

       public static Color BLUE = Color.blue;

      

       public ColorData(Color color, Object data)

       {

             m_color = color;

             m_data = data;

       }

       public ColorData(Double data)

       {

             m_color = data.doubleValue() >= 0 ? BLUE : RED;

             m_data = data;

       }

       public String toString()

       {

             return m_data.toString();

       }

}

 

class IconData

{

       public ImageIcon m_icon;

       public Object m_data;

       public IconData(ImageIcon icon, Object data)

       {

             m_icon = icon;

             m_data = data;

       }

       public String toString()

       {

             return m_data.toString();

       }

}

 

class StockData

{

       public static ImageIcon ICON_UP = new ImageIcon("ArrUp.gif");

       public static ImageIcon ICON_DOWN = new ImageIcon("ArrDown.gif");

       public static ImageIcon ICON_BLANK = new ImageIcon("Blank.gif");

      

       public IconData m_symbol;

       public String m_name;

       public Double m_last;

       public Double m_open;

       public ColorData m_change;

       public ColorData m_changePr;

       public Long m_volume;

 

       public StockData(String symbol, String name,

             double last, double open, double change,

             double changePr, long volume)

       {

             m_symbol = new IconData(getIcon(change), symbol);

             m_name = name;

             m_last = new Double(last);

             m_open = new Double(open);

             m_change = new ColorData(new Double(change));

             m_changePr = new ColorData(new Double(changePr));

             m_volume = new Long(volume);

       }

       public static ImageIcon getIcon(double change)

       {

             return (change>0 ? ICON_UP :

                    (change<0 ? ICON_DOWN : ICON_BLANK));

       }

}

 

class ColumnData

{

       public String m_title;

       public int m_width;

       public int m_alignment;

       public ColumnData(String title, int width, int alignment)

       {

             m_title = title;

             m_width = width;

             m_alignment = alignment;

       }

}

class StockTableData extends AbstractTableModel

{

       static final public ColumnData m_columns[] = {

             new ColumnData( "Symbol", 100, JLabel.LEFT),

             new ColumnData( "Name", 160, JLabel.LEFT),

             new ColumnData( "Last", 100, JLabel.RIGHT),

             new ColumnData( "Open", 100, JLabel.RIGHT),

             new ColumnData( "Change", 100, JLabel.RIGHT),

             new ColumnData( "Change %", 100, JLabel.RIGHT),

             new ColumnData( "Volume", 100, JLabel.RIGHT)

       };

       protected SimpleDateFormat m_frm;

       protected NumberFormat m_volumeFormat;

       protected Vector m_vector;

       protected Date m_date;

      

       public StockTableData() {

             m_frm = new SimpleDateFormat("mm/dd/yyyy");

             m_volumeFormat = NumberFormat.getInstance();

             m_volumeFormat.setGroupingUsed(true);

             m_volumeFormat.setMaximumFractionDigits(0);

            

             m_vector = new Vector();

             setDefaultData();

       }

       public void setDefaultData() {

             try {

                    m_date = m_frm.parse("05/01/2007");

             }

             catch (java.text.ParseException ex) {

                    m_date = null;

             }

             m_vector.removeAllElements();

             m_vector.addElement(new StockData("ORCL", "Oracle Corp.",23.6875, 25.275, -1.6875, -6.42, 23976600));

             m_vector.addElement(new StockData("EGGS", "Egghead.com", 17.25, 17.4375, -0.1875, -1.43, 2146400));

             m_vector.addElement(new StockData("T", "AT&T", 65.1875, 66, -0.8125, -0.10, 554000));

             m_vector.addElement(new StockData("LU", "Lucent Technology", 64.625, 59.9375, 4.6875, 9.65, 29856300));

             m_vector.addElement(new StockData("FON", "Sprint", 104.5625, 106.375, -1.8125, -1.82, 1135100));

             m_vector.addElement(new StockData("ENML", "Enamelon Inc.", 4.875, 5, -0.125, 0, 35900));

             m_vector.addElement(new StockData("CPQ", "Compaq Computers", 30.875, 31.25, -0.375, -2.18, 11853900));

             m_vector.addElement(new StockData("MSFT", "Microsfot Corp.", 94.0625, 92.1875, -1.125, -0.92, 19836900));            

             m_vector.addElement(new StockData("DELL", "Dell Computers", 46.1875, 44.5, 1.6875, 6.24, 47310000));

             m_vector.addElement(new StockData("SUNW", "Sun Microsystems", 140.625, 130.9375, 10, 10.625, 17734600));

             m_vector.addElement(new StockData("IBM", "Intl. Bus. Machines", 183, 183.125, -0.125, -0.51, 4371400));

             m_vector.addElement(new StockData("HWP", "Hewlet-Packard", 70, 71.0625, -1.4375, -2.01, 2410700));

             m_vector.addElement(new StockData("UIS", "Unisys Corp.", 28.25, 29, -0.75, -2.59, 2576200));

             m_vector.addElement(new StockData("SNE", "Sony Corp.", 96.1875, 95.625, 1.125, 1.18, 330600));

             m_vector.addElement(new StockData("NOVL", "Novell Inc.", 24.0625, 24.375, -0.3125, -3.02, 6047900));

             m_vector.addElement(new StockData("HIT", "Hitachi, Ltd.", 78.5, 77.625, 0.875, 1.12, 49400));

       }

       public int getRowCount()

       {

             return m_vector == null ? 0 : m_vector.size();

       }

       public int getColumnCount()

       {

             return m_columns.length;

       }

       public String getColumnName(int column)

       {

             return m_columns[column].m_title;

       }

       public boolean isCellEditable(int nRow, int nCol)

       {

             return false;

       }

       public Object getValueAt(int nRow, int nCol)

       {

             if (nRow < 0 || nRow >= getRowCount())

                    return "";

             StockData row = (StockData)m_vector.elementAt(nRow);

             switch(nCol){

                    case 0: return row.m_symbol;

                    case 1: return row.m_name;

                    case 2: return row.m_last;

                    case 3: return row.m_open;

                    case 4: return row.m_change;

                    case 5: return row.m_changePr;

                    case 6: return m_volumeFormat.format(row.m_volume);

             }

             return "";

       }

       public String getTitle()

       {

             if (m_date == null)

                    return "Stock Quotes";

             return "Stock Quotes at " + m_frm.format(m_date);

       }

}

 

저작자 표시 비영리
Posted by 티엘로

JTextField 클래스를 이용하면 간단하게 텍스트 입력 필드를 화면에 뿌려줄 수 있다. 그런데 JTextField 자체에는 입력 글자수를 제한하는 메써드가 존재하지 않는다. 즉, 10자 까지만 입력하게 하고 싶어도 기본 API로는 구현이 불가능하다. 입력 할 글자수를 제한하려면 다음과 같이 PlainDocument 클래스를 상속받은 클래스를 정의하여 구현할 수 있다.

 

public class JTextFieldLimit extends PlainDocument
{
      private int limit;                             // 제한할 길이
      public JTextFieldLimit(int limit)        // 생성자 : 제한할 길이를 인자로 받음
      {
            super();
            this.limit = limit;
      }

       // 텍스트 필드를 채우는 메써드 : 오버라이드
      public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException 
      {
            if (str == null)
                  return;


            if (getLength() + str.length() <= limit) 
                  super.insertString(offset, str, attr);
       }
}

사용법은 다음과 같다.

textfield.setDocument(new JTextFieldLimit(10));

Posted by 티엘로