2010. 10. 27. 16:49

Swing framework 는 여러가지 컨트롤들을 가지고 있지만, 아쉽게도 Tree table 같이 개발에 필요한 컨트로를 가지고 있지 않다.

SwingX Project는 아래의 몇몇개의 유용한 컴포넌트를 제공한다.

  • Sorting, filtering, and highlighting for tables, trees, and lists
  • Find/search
  • Auto-completion
  • Login/authentication framework
  • TreeTable component
  • Collapsible panel component
  • Date picker component
  • Tip-of-the-Day component

SwingX Download

Swing은 멋없는 UI와 필요한 컴포넌트가 없다라는 편견으로 SWT를 주로 사용했지만.. 자바 진영에서 Swing에 투자를 좀 하는 듯 싶다. look and feel과 SwingX 등.. 재미난 것들이 많은 듯..


Posted by 짱가쟁이
메지지 박스 요넘도 자주 사용 안하는 관계로 매번 사용할 때 마다 검색을 해야 한다능..

Example
JOptionPane.showMessageDialog(메시지박스가 보여질 컴포넌트, "메시지");

JOptionPane <- 요넘은 여러개의 메시지 창을 가지고 있음.. 알아서 잘 찾아 사용하면 됨
Posted by 짱가쟁이

가끔 문서를 제대로 읽지 않아서 삽질하는 경우가 있기는 하지만 이번엔 정말 대박인듯..

swing은 thread 에 안전하지 않다는 문구는 여기 저기 돌아 댕기고.. api 내에 설명까지 하고 있지만.. Swing을 자주 사용하지 않을 뿐더러.. 테스트용 어플만 만들다 보니 이런 실수가 발생되는 듯.

우선은 데이터 추출하는 Thread 에서 아이템을 차례대로 JList에 삽입함과 동시에 마지막 아이템을 select 하고 싶고, 스크롤을 마지막으로 보내고 싶다는 단순한 생각이 이렇게 삽질하게 만들줄은 꿈에도 생각못 함. 쩌ㅃ~

우선은 스크롤을 마지막으로 보내는 ensureIndexIsVisible() 요넘을 Thread 에서 사용하면 exception 이 발생된다. 뭐 이때 사용하라고 있는 넘이 SwingUtilities.invokeLater 요넘인건 기초중에 기초인데.. 왜 자꾸 까먹고 실수 하는 건지.. ㅡㅡ;;

Example
public void run() {
..............................
jList.setListData(v);
jList..setSelectedIndex(v.size()-1); // 마지막 아이템 select 하자

 SwingUtilities.invokeLater(new Runnable() {  // Thread safety 하게 만들자..
        public void run() {
            jList.ensureIndexIsVisible(jList.getSelectedIndex());
        }
});
}

위 샘플 코드를 보면 쓰레드에 안전하지 않은 넘을 쓰레드에 안전하게 만들어.. race condition 을 해결한다.

ps.
다음번에도 같은 실수를 반복할까? 머릿속의 지우개가 결정할 문제일 듯 쩌ㅃ~

'java > swing' 카테고리의 다른 글

[swing] - SwingX  (0) 2010.10.27
[swing] - 메시지 박스  (0) 2010.07.19
[swing] - look and feel  (0) 2010.06.30
[swing] - JFileChooser 사용하자  (0) 2010.06.30
[swing] - JTableHeader 값 중앙에 배치하기  (0) 2010.06.30
Posted by 짱가쟁이
2010. 6. 30. 14:27
자바로 UI를 개발하면 기존에 사용하던 윈도우 포맷과 틀려서 거부감이 들때가 있었다. 뭐 그래서 SWT를 찾아 공부도 하고했지만.. look and feel 이라는 넘이 존재하더라.. 이넘 언제부터 나온건지 모름..

기본적으로 윈도우 포맷에 맞춰서 보여주는 코드..

Example
try {
// Set cross-platform Java L&F (also called "Metal")
    UIManager.setLookAndFeel(
        UIManager.getSystemLookAndFeelClassName());
}
catch (UnsupportedLookAndFeelException e) {
   // handle exception
}
catch (ClassNotFoundException e) {
   // handle exception
}
catch (InstantiationException e) {
   // handle exception
}
catch (IllegalAccessException e) {
   // handle exception
}


Posted by 짱가쟁이


요넘 예전에 공부하다가 대충 훌터보고 넘겼었는데.. 드디에 사용하게 되는구만..

가끔 엑셀로 데이터를 주면서 배치작업을 요청 하는 경우가 있더라.. 뭐.. 텍스트 파일로 옮긴 후 처리를 하곤 했지만.. 내가 계속 해주기 귀찮더라이거지.. 그래서 아싸리. UI 프로그램을 하나 주면 편할 듯 싶어서 작업을 하게 됨.

Example
private JFileChooser fileChooser = new JFileChooser();

fileChooser.setDialogTitle("Excel Load");
fileChooser.setFileFilter(new TypeOfFile());

TypeOfFile
- 요넘.. 어딘가에서 가져왔지만. 까먹음.. 흠...
import java.io.File;
import javax.swing.filechooser.FileFilter;

/**
 *
 * @author 0216
 */
public class TypeOfFile extends FileFilter {
     //Type of file that should be display in JFileChooser will be set here
     //We choose to display only directory and text file
     public boolean accept(File f)
     {
        return f.isDirectory()||f.getName().toLowerCase().endsWith(".xlsx");
     }

     //Set description for the type of file that should be display
     public String getDescription()
     {
        return ".xlsx";
     }
}

Posted by 짱가쟁이
swing tutorial 의 예제를 보면 알아서 테이블 헤더의 값이 중앙에 배치되는데.. 요넘.. NetBeans 를 사용해서 생성한 테이블은 기본이 왼쪽 정렬이 된다. 이것저것 뒤져봐도 마땅한 방법이 생각이 안나.. 그냥 코드로 추가했다.

이렇게 무식한 방법말고, 좋은 방법이 있을 법도 한데.. 더이상 찾기가 귀찮다. 쩌ㅃ~

밑의 소스코드를 보면 현재 테이블의 Header CellRenderer 를 가져와서 랜더의 수평 정렬을 center 로 설정했다.

Code
DefaultTableCellRenderer renderer =  

                         (DefaultTableCellRenderer)table.getTableHeader().getDefaultRenderer();
renderer.setHorizontalAlignment(SwingConstants.CENTER);
table.getTableHeader().setDefaultRenderer(renderer);


Posted by 짱가쟁이

<실행화면>



Cell Editor 를 콤보박스로 변경하는 단순한 형태의 코드.

changeCellEditor(table, table.getColumnModel().getColumn(3));

void changeCellEditor(JTable table, TableColumn column) {

        JComboBox comboBox = new JComboBox();
        comboBox.addItem("사람");
        comboBox.addItem("요크");
        comboBox.addItem("마르티스");
        comboBox.addItem("MIX");
        column.setCellEditor(new DefaultCellEditor(comboBox));

        DefaultTableCellRenderer renderer =
                new DefaultTableCellRenderer();
        renderer.setToolTipText("클릭하면 콤보박스로 변경됩니다.");
        column.setCellRenderer(renderer);
}


위 코드 샘플의 bold line 이 cell editor 를 변경하는 부분이다.

출처

Posted by 짱가쟁이

쉽게 번 돈은 쉽게 날려버린다고.. 이거 어딘가에서 쉽게 찾아 사용하던 코드라 그런지.. 또 사용할려고 하니.. 도통 기억이 안남..

보통 테이블에서 셀을 수정하다가(셀이 editing 상태임) 버튼을 눌러서 수정된 값을 저장할려고 시도하는 경우가 있다. 그런데 JTable 이라는 넘은 셀이 수정중이면 엔터를 치거나, 아니면 포커스를 변경해줘야지 값이 변경된다. 물론 사용자한테 책임을 떠 넘기고 '니들이 사용할 때 잘 사용해라' 라고 할 수 있겠지만, 보통은 이런식으로 작업을 하는 사람이 없을 듯 싶다.

위 문제를 해결하는 방법을 또 찾기 싫어서 여기에 올림 .. 쩌ㅃ~

1. editCellAt() 를 이용해서 현재 수정중인 값을 가져온다.

2. setValueAt() 넘을 사용해서 가져온 값으로 변경하면 땡.


editCellAt()   : 현재 수정중인 셀의 값을 가져오는 넘.

setValueAt() : 특정 셀에 값을 삽입한다.

editingStopped()  : 처음에 이넘을 사용하면 될듯 싶었지만. 저넘은 단순히 에디팅이 끝났다고 알려주기만 하는듯 싶다. 뭐.. 엄밀히 말해서 셀의 에디팅 상태를 종료하는 넘으로 해석하면 될듯.

 

table.setValueAt(table.editCellAt(table.getSelectedRow(), table.getSelectedColumn()), table.getSelectedRow(), table.getSelectedColumn());

table.editingStopped(null);

Posted by 짱가쟁이

하나만 만들어 놓고 언제나 사용할 수 있는 Table Model 을 만들자는 취지로 시작했지만.. Table 의 요구사항 변경에 따라 수정 할 수 밖에 없다. 좀 아쉬운 감이 있지만 모든걸 다 적용시키기는 힘들다는 결론에 다다르고 단순히 범용적으로 사용할 수 있는 넘 하나만 만들었다.

TableModel 은 column name 과 data 두개의 부분으로 나눠지는데.. 이 두개만 생성해 model에 등록하면 알아서 동작하는 넘으로 작성되었다. Reflection 기능을 이용하니 손쉽게 작성된듯...


MyTableModel.java

AbstractTableModel 클래스를 상속받아 구현된 단순한 넘. 기본 골격은 (http://java.sun.com/docs/books/tutorial/uiswing/components/table.html#validtext) 참조했으며 신규 추가된 부분이 data 와 header 부분을 Value Object로 변경한 부분이다. 물론 변경된 사항에 따라서 column name 과 data get, set 부분이 수정되었다.

import javax.swing.table.AbstractTableModel;

 /**
 *
 * @author Administrator
 */
public class MyTableModel extends AbstractTableModel {

    private AbstractTableVo[] data;
    private CustomTableColumnName[] columnNames;

    public Object[] longValues;

    public Object[] getLongValues() {
        return longValues;
    }

    public void setLongValues(Object[] longValues) {
        this.longValues = longValues;
    }

    public CustomTableColumnName[] getColumnNames() {
        return columnNames;
    }

    public void setColumnNames(CustomTableColumnName[] columnNames) {
        this.columnNames = columnNames;
    }

   
    public AbstractTableVo[] getData() {
        return data;
    }

    public void setData(AbstractTableVo[] data) {
        this.data = data;
    }

    public String getColumnName(int col) {
        return columnNames[col].getName();
    }


    public int getRowCount() {
        return data.length;
    }

    public int getColumnCount() {
        return columnNames.length;
    }

    public Object getValueAt(int rowIndex, int columnIndex) {
        return data[rowIndex].getValueAt(columnNames[columnIndex].getId());
    }
   
    /*
     * Don't need to implement this method unless your table's
     * editable.
     */
    public boolean isCellEditable(int row, int col) {
        return true;
    }

    /*
     * Don't need to implement this method unless your table's
     * data can change.
     */
    public void setValueAt(Object value, int row, int col) {

        data[row].setValueAt(value, columnNames[col].getId());

    }

    /*
     * JTable uses this method to determine the default renderer/
     * editor for each cell.  If we didn't implement this method,
     * then the last column would contain text ("true"/"false"),
     * rather than a check box.
     */
    public Class getColumnClass(int c) {
        return getValueAt(0, c).getClass();
    }

}


SampleVo.java
- 샘플로 작성된 Table Data Value 객체. AbstractTableVo 클래스를 상속받아야 하는 rule 만 지켜주면 만사형통임.

/**
 *
 * @author Administrator
 */
public class SampleVo extends AbstractTableVo {

    private String firstName;
    private String lastName;
    private String sport;
    private Integer ofYears;
    private Boolean vegetarian;
       
    public Boolean getVegetarian() {
        return vegetarian;
    }

    public void setVegetarian(Boolean vegetarian) {
        this.vegetarian = vegetarian;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Integer getOfYears() {
        return ofYears;
    }

    public void setOfYears(Integer ofYears) {
        this.ofYears = ofYears;
    }

    public String getSport() {
        return sport;
    }

    public void setSport(String sport) {
        this.sport = sport;
    }  
}


AbstractTableVo.java
- CustomTableColumnName, SampleVo 클래스 들이 규칙에 따라 작성되면 TableModel 과 연결되어 알아서 작동한다.

import java.lang.reflect.Method;

/**
 *
 * @author Administrator
 */
public abstract class AbstractTableVo {

    public void setValueAt(Object value, String id) {
         try {

            String methodName = "set" + id.substring(0, 1).toUpperCase() + id.substring(1, id.length());

            Method method = this.getClass().getMethod(methodName , new Class[]{value.getClass()});

            method.invoke(this, value);
        } catch(Exception e) {

        }
    }
  
    public Object getValueAt(String id) {

        try {

            String methodName = "get" + id.substring(0, 1).toUpperCase() + id.substring(1, id.length());

            System.out.println("Method Name : " + methodName);

            Method method = this.getClass().getMethod(methodName ,null);

            return method.invoke(this, null);
        } catch(Exception e) {

        }
        return null;
    }
}


CustomTableColumnName.java
- table의 header 명을 가지는 클래스로 name 필드는 컬럼명을 의미하며, id 는 컴럼명을 의미하는 키로 SampleVo 의 필드명과 일치해야 한다.    

/**
 *
 * @author Administrator
 */
public class CustomTableColumnName {

    private String id;
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


사용법
Model 을 만드는 부분이 화면에 보여질 데이터를 작성하는 것이기 때문에 어쩔 수 없이 무식하고 지져분해질 수 밖에 없는듯하다. 좀더 좋은 방법이 있겠지만, 귀찮니즘 때문인지 단순히 데이터를 무식하게 쌓기만 했다. 사용은 각자가 알아서 하면 될듯하다.

SampleVo 를 토대로 작성했기 때문에 다른 Data Value Object 작성 시 아래 코드는 참조용으로만 사용해야 할듯.

private MyTableModel myTableModel = new MyTableModel();

// Table Model 초기화

private void initTable() {       

        SampleVo[] tableDataValues;
        CustomTableColumnName[] tableColumnNames;

        // table data 초기화

        tableDataValues = new SampleVo[5];
        setData(tableDataValues, 0, "Mary", "Campione", "Snowboarding", new Integer(5), new Boolean(false));
        setData(tableDataValues, 1, "Mary", "Campione", "Snowboarding", new Integer(5), new Boolean(true));
        setData(tableDataValues, 2, "Mary", "Campione", "Snowboarding", new Integer(5), new Boolean(false));
        setData(tableDataValues, 3, "Mary", "Campione", "Snowboarding", new Integer(5), new Boolean(true));
        setData(tableDataValues, 4, "Mary", "Campione", "Snowboarding", new Integer(5), new Boolean(true));
        myTableModel.setData(tableDataValues);

        // column name 초기화
        tableColumnNames = new CustomTableColumnName[5];
        setColumnName(tableColumnNames,0, "firstName", "First Name");
        setColumnName(tableColumnNames,1, "lastName", "Last Name");
        setColumnName(tableColumnNames,2, "sport", "Sport");
        setColumnName(tableColumnNames,3, "ofYears", "# of Years");
        setColumnName(tableColumnNames,4, "vegetarian", "Vegetarian");
        myTableModel.setColumnNames(tableColumnNames);

       // column 최대 사이즈를 구하기 위해서..
       Object[] longValues = {"Sharon", "Campione", "None of the above", new Integer(20), Boolean.TRUE};
       myTableModel.setLongValues(longValues);
    }

     public void setColumnName(CustomTableColumnName[] tableColumnNames, int offset, String id, String name) {
        tableColumnNames[offset] = new CustomTableColumnName();
        tableColumnNames[offset].setId(id);
        tableColumnNames[offset].setName(name);
    }
   
    public void setData(SampleVo[] tableDataValues, int offset, String firstName, String lastName, String sport, Integer integer, Boolean boo) {
        tableDataValues[offset] = new SampleVo();
        tableDataValues[offset].setFirstName(firstName);
        tableDataValues[offset].setLastName(lastName);
        tableDataValues[offset].setSport(sport);
        tableDataValues[offset].setOfYears(integer);
        tableDataValues[offset].setVegetarian(boo);
    }



Posted by 짱가쟁이

<실행화면>


Netbeans IDE 에서 Tree Cell 을 변경하는 방법.

1. DefaultTreeCellRenderer 를 상속받아 CustomTreeCellRenderer.java 작성

- Cell 의 배경색과 Selected 된 Cell 의 배경색을 변경한다.

2. Netbeans 에서 변경하고 싶은 Tree의 Properties 에서 cellRenderer 'Custom code' 선택 후 [new CustomTreeCellRenderer()] 변경.

위 1, 2 번을 수행하면 실행화면과 같이 Cell 의 background color 이 변경된다.


Example

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package swing_sample.tree.cellrenderer;

import java.awt.Color;
import java.awt.Component;
import javax.swing.JComponent;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeCellRenderer;

/**
 *
 * @author Administrator
 */
public class CustomTreeCellRenderer extends DefaultTreeCellRenderer {

    @Override
    public Component getTreeCellRendererComponent (
            JTree tree,
            Object value,
            boolean selected,
            boolean expanded,
            boolean leaf,
            int row,
            boolean hasFocus)
    {
        JComponent comp = (JComponent) super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
        comp.setOpaque(true);

        if(selected) {
            comp.setBackground(Color.DARK_GRAY);
        } else {
            comp.setBackground(tree.getBackground());
        }
        return comp;
    }
}

Posted by 짱가쟁이
이전버튼 1 2 이전버튼