JTable中AbstractTableModel的使用(可以实现直接修改数据,下拉栏,图片等)

最近在做一个java的项目管理软件,做gui的时候需要用到Jtable,看到别人写的代码里面,都会有一个Tablemodel,所有感觉很不解,遂上网查了一下,得到如下心得体会:

在Java中JTable的数据是以TableModel表模式的方式存放的,这个TableModel就是用来存放数据的,当Table初始化的时候通过TableModel获取表格的行数、列数、列标题、以及每个单元格存放的数据,于是当表格现实的时候就可以显示出对应的数据了。

首先介绍一下JTable的构造方法:

  JTable() 构造一个默认的 JTable,使用默认的数据模型、默认的列模型和默认的选择模型对其进行初始化。

  JTable(int numRows, int numColumns) 使用 DefaultTableModel 构造具有 numRows 行和 numColumns列个空单元格的 JTable。

  JTable(Object[][] rowData, Object[] columnNames) 构造一个 JTable 来显示二维数组rowData 中的值,其列名称为 columnNames。

  JTable(TableModel dm) 构造一个 JTable,使用数据模型 dm、默认的列模型和默认的选择模型对其进行初始化。

  JTable(TableModel dm, TableColumnModel cm) 构造一个 JTable,使用数据模型dm、列模型 cm和默认的选择模型对其进行初始化。

  JTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) 构造一个 JTable,使用数据模型dm、列模型cm 和选择模型 sm 对其进行初始化。

  JTable(Vector rowData, Vector columnNames) 构造一个 JTable 来显示 Vector 所组成的 Vector rowData中的值,其列名称为 columnNames。

 这里我们着重讨论JTable(TableModeldm)这种构造方法,在这个构造函数中,需要传递一个数据模TableModel 用来存放数据,当表格现显示的时候就直接通过这个TableModel来获取表格的信息以及数据,TableMode是一个接口,需要实现AbstractTableModel的方法,而AbstractTableModel又是一个抽象的类,也就是说在使用自己的TableModel的时候要重写一个自己的TableMode类,通过这个Model来控制自己表格的数据, AbstractTableModel中有些方法是已经实现的了,所以我们只需要对自己需要的方法进行重写:AbstractTableModel的基本方法:

  Int getColumnCount() 返回该模型中的列数。

  String getColumnName(int columnIndex) 返回 columnIndex 位置的列的名称。

  Int getRowCount() 返回该模型中的行数。

  Object getValueAt(int rowIndex,int columnIndex) 返回columnIndex 和 rowIndex 位置的单元格值。

  Boolean isCellEditable(int rowIndex,int columnIndex) 如果 rowIndex 和 columnIndex位置的单元格是可编辑的,则返回   true。就是设置当前位置的单元格的数据是否可以被编辑

  Void setValueAt(Object aValue, int rowIndex, int columnIndex) 将 columnIndex和 rowIndex 位置的单元格中的值设置为 aValue。

这里我们就通过这些基本的方法来写一个基本的TableModel。表格既然是一个二维的,那么表格的数据存放如果也是二维的,那么数据的一一映射就很容易实现了我们知道Vector是一个一维线性表,存放的数据类型是Object(Java所有类的'爹'),那么如果我们在一维的线性表中的每个位置都放一个一维的线性表,那么就像把很多条直线并排放一样就变成了一个二维的空间了。

JTable中AbstractTableModel的使用(可以实现直接修改数据,下拉栏,图片等)_第1张图片

下面是一个Model的示例:(在test包下的JMain class文件中)

package test;

import java.util.Scanner;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

class ModelDemo extends AbstractTableModel
{        
       private Vector TableData;//用来存放表格数据的线性表
       private Vector TableTitle;//表格的列标题        
       public ModelDemo()
       {
           TableData = new Vector();
           TableTitle= new Vector();
           TableTitle.add("第一列");
           TableTitle.add("第二列");
           TableTitle.add("第三列");
           String []Line1 = {"(0,0)","(0,1)","(0,2)"};
           //第1行
           String []Line2 = {"(1,0)","(1,1)","(1,2)"};
           //第2行
           String []Line3 = {"(2,0)","(2,1)","(2,2)"};
           //将数据挂到线性表形成二维的数据表,形成映射
           TableData.add(Line1);
           TableData.add(Line2);
           TableData.add(Line3);               
       }
       @Override
       public int getRowCount()
       {
           //返回表的行数,返回TableData上挂的String数组个数
           return TableData.size();
       }
       
       @Override
       public int getColumnCount()
       {
           //返回表的列数,直接返回TableTitle.size()
           return TableTitle.size();
       }
       
       @Override
       public Object getValueAt(int rowIndex, int columnIndex)
       {
            
           //获取相应坐标位置的值,分下面两步
           //获取每一行对应的String[]数组
           String LineTemp[] = (String[])this.TableData.get(rowIndex);
           //提取出对 应的数据
           return LineTemp[columnIndex];
       }
  
       @Override
       public boolean isCellEditable(int rowIndex, int columnIndex)
       {
           //这个函数式设置每个单元格的编辑属性的
           //这个函数AbstractTableModel已经实现
           //这里我们设置为前两列为允许编辑状态
           if(rowIndex<2&&columnIndex<2){
               return true;
           }
           else{
               return false;
           }
       }
       @Override
       public void setValueAt(Object aValue, int rowIndex, int columnIndex)
       {
           //当单元格的数据发生改变的时候调用该函数重设单元格的数据
           //我们想一下,数据是放在TableData中的,说白了修改数据就是修改的
           //TableData中的数据,所以我们仅仅在此处将TableData的对应数据修改即可              
           ((String[])this.TableData.get(rowIndex))[columnIndex]=(String)aValue;
           //然后调用函数fireTableCellUpdated(int row,int col);更新一下表格对应位置的数据显示即可完成对表格的数据修改;
           this.fireTableCellUpdated(rowIndex, columnIndex);
       }
}
//创建一个Dialog显示一下
public class JMain
{
    public static void main(String[] args)
    {               
        ModelDemo myModel=  new ModelDemo(); 
        JTable table = new JTable(myModel);
        JFrame jf = new JFrame();
        JScrollPane jsp = new JScrollPane(table);
        jf.add(jsp);
        jf.setBounds(0, 0, 500, 500);
        jf.setVisible(true);
        //这里我们可以检验直接在table的相应单元格上修改数据后,此单元格的值会不会变化
        Scanner in=new Scanner(System.in);
        int m=in.nextInt();
        int n=in.nextInt();
        String a=(String)myModel.getValueAt(m,n);
        System.out.println(a);
    }
}
最后我们得到如图所示的表格

JTable中AbstractTableModel的使用(可以实现直接修改数据,下拉栏,图片等)_第2张图片

修改数据后:

JTable中AbstractTableModel的使用(可以实现直接修改数据,下拉栏,图片等)_第3张图片

 在eclipse输入数字2 0,可以得到修改后的字符串“我修改了数据”。(注意:编辑完成之后按下Enter才算完成编辑)

当然JTable中也可以存储图片或者是下拉栏,下面我们将演示如何实现图片与下拉栏:

除了上述的表格与JTableModel之外,编辑器也很重要,因为使用 JTable的3要素就是: 表格、JTableModel、编辑器。其中显示数据靠的是表格,存储数据靠的是TableModel,修改数据靠的是对应的编辑器,所谓的编辑器这也就是的 CellEditor表格编辑器。在表的维护中是以列作为一个组的,所以对于表的每一列的数据类型必须是相同的,在默认状态下即是我们不指定表的每一列的显示的数据类型的时候,TableModel中的数据将会被默认为String类型在Table中进行维护。

下面我们举个例子,在表格内显示图片,与下拉列表JComboBox:(首先建立一个表格Model存放数据,为了简单起见,设置为 2x2的表格,第一列显示JComboBox、第二列显示图片)


========================================================================================

package test;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.util.EventObject;

import javax.swing.AbstractCellEditor;
import javax.swing.DefaultCellEditor;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableModel;
import javax.swing.text.TableView.TableCell;
/**
 * 
 * 本实例是测试CellEditor的用法
 * 结论
 * 		在 Mode中设置 表格为允许编辑的状态,并且要设置 编辑器后才能响应的
 * 		编辑器的响应顺序是
 * 		1、构造编辑器[调用构造函数]
 * 		2、调用 getTableCellEditorComponent() 返回一个组件,这个组件是 就是编辑器
 * 		的状态 例如是一个按钮,下拉框等
 * 		3、在编辑器中 响应 对应组件的消息(例如文本框、按钮作为编辑器的时候),
 * 		  在组件的Action作出自己的处理
 * 		4、调用函数fireEditingStopped();告知 编辑完成,例如 Textfield是 Enter时调用的
 *      5、返回编辑器的编辑结果
 *      6、剩下的就 只需要 有 Mode完成了
 */


class MyTableModel extends AbstractTableModel
{
	String head[]={"下拉列表","图片"};
	//表格每一列的数据维护类型,String.class是反射的用法,得到一个String的类型
	Class[]TypeArr = {String.class,Icon.class};
	//表格的数据
	Object[][]data = {
						{"选项A",new ImageIcon("C://I//pic1.png")},
						{"选项B",new ImageIcon("C://I//pic2.png")}
					 };

	@Override
	public int getRowCount()
	{return 2;}

	@Override
	public int getColumnCount()
	{return 2;}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex)
	{return data[rowIndex][columnIndex];}
	
	//通知Mode每一列的数据类型
	@Override
	public Class getColumnClass(int columnIndex)
	{
		// TODO Auto-generated method stub
		return TypeArr[columnIndex];//super.getColumnClass(columnIndex);
	}

	@Override
	public boolean isCellEditable(int rowIndex, int columnIndex)
	{return true;}

	//表格的列标题
	@Override
	public String getColumnName(int column)
	{return head[column];}

	@Override
	public void setValueAt(Object aValue, int rowIndex, int columnIndex)
	{
		data[rowIndex][columnIndex] = aValue;
		//只需要更新对应的位置
		this.fireTableCellUpdated(rowIndex, columnIndex);
	}
}

///////////////////////
//************************************************************************自定义的图片编辑器
//实现 TableCellEditor,ActionListener 接口
class MyPicEditor extends AbstractCellEditor implements TableCellEditor,ActionListener
{
	/*
	*	ReadMe:当我们点击表格Cell的时候,表格检测点击的消息,检测Cell是否允许编辑,
	*	如果允许编辑 则去调用 表格编辑器 来获取图片,获取完后将图片 送达给 TableModel
	*	结束编辑器的编辑状态,表格刷新显示 对应的图片
	*/
	//用于获取图片的变量
	private Icon m_IconPic;
	//作为 编辑器 ,当我们点击的时候进行图片的选择
	private JButton m_IconButton;
	//点击按钮的时候 进行文件选择的 Filechooser
	private JFileChooser m_PicFileChooser;
	//设置当我们 点击2次的时候 编辑器 才起作用
	private static final int clickCountToStart = 2;

	//构造函数,初始化一些信息
	public MyPicEditor()
	{
		m_IconButton =new JButton();
		m_IconButton.addActionListener(this);
		m_PicFileChooser = new JFileChooser();
	}
	//检测鼠标的点击次数,判断编辑器是否起作用
	public boolean isCellEditable(EventObject anEvent) 
    {
		//如果事件 是 鼠标的事件,大于设定的次数就true,否则false
	    if (anEvent instanceof MouseEvent) 
	    {
			System.out.println("检测到了鼠标的事件");
			return ((MouseEvent)anEvent).getClickCount() >= clickCountToStart;
		}
		return false;
    }

	@Override
	public Component getTableCellEditorComponent(JTable table, Object value,
			boolean isSelected, int row, int column)
	{
		System.out.println("表格Cell获取将要显示的编辑器组件,返回值编辑器包含的控件");
		//先前的表格Cell的 数据 先保存下来,用于初始化编辑器包含的控件的数据
		m_IconPic = (Icon)value;
		//返回作为编辑器的组件,这里是一个按钮
		return m_IconButton;
	}
	//响应编辑器包含的组件的事件
	@Override
	public void actionPerformed(ActionEvent e)
	{
		System.out.println("编辑器组件事件响应");
		if(e.getSource()==m_IconButton)
		{
			//初始化编辑器,显示原始的图片
			m_IconButton.setIcon(m_IconPic);
			//显示文件选择器,用于选择图片
			m_PicFileChooser.showOpenDialog(m_IconButton);

			if(m_PicFileChooser.getSelectedFile()!=null)
			{
				//如果选择了新的图片将按钮设置为新的图标
				m_IconPic= new ImageIcon(m_PicFileChooser.getSelectedFile().getAbsolutePath());
			}
			//数据获取完成,终止编辑器,将数据送达 调用者
			this.fireEditingStopped();
		}
		
	}
	
	//将数据送达调用者,关闭编辑器,表格正常显示
	@Override
	public Object getCellEditorValue()
	{
		System.out.println("返回结果");
		return m_IconPic;
	}
}


//****************************************************************再写一个Commbox的
class ComBoxEditor extends AbstractCellEditor implements TableCellEditor
{
	/*
	*	ReadMe: 这个 ComboBox下拉列表的编辑器 使用一个 JLable 和一个 JComboBox组合的
	*	将JComboBox放到JLable里,所以只需要将 JLable 作为编辑器组件返回就行了
	*/
	private JComboBox m_ComboBox;
	//获取 下拉列表的 选择的值
	private String m_SelStr;
	private JLabel m_OutLable;
	//这里我们设置 鼠标点击 1 次就响应编辑器
	private static final int clickCountToStart = 1;
	//初始化编辑器包含的控件信息
	public ComBoxEditor()
	{
		m_ComboBox = new JComboBox();
		m_ComboBox.addItem("选项A");
		m_ComboBox.addItem("选项B");
		m_ComboBox.addItem("选项C");
		
		m_ComboBox.setSize(100,30);
		
		m_OutLable= new JLabel();
		m_OutLable.setLayout(null);
		m_OutLable.setBounds(0, 0, 120, 40);
		m_OutLable.add(m_ComboBox);
		m_ComboBox.setLocation(50, 50);
		
		//响应下拉列表的事件
		m_ComboBox.addItemListener(new ItemListener()
		{
			@Override
			public void itemStateChanged(ItemEvent e)
			{
				System.out.println("下拉列表的选中事件");
				if(e.getStateChange() == e.SELECTED)
				{
					//获取选择的值
					m_SelStr = (String)m_ComboBox.getSelectedItem();
					//结束选择
					fireEditingStopped();
				}
			}
		});
	}
	//检测鼠标的点击次数,判断编辑器是否起作用
	public boolean isCellEditable(EventObject anEvent) 
    {
		//如果事件 是 鼠标的事件,大于设定的次数就true,否则false
	    if (anEvent instanceof MouseEvent) 
	    {
			System.out.println("检测鼠标的点击次数,设置编辑器是否响应");
			return ((MouseEvent)anEvent).getClickCount() >= clickCountToStart;
		}
	    return false;
    }

	//获取编辑器的组件
	@Override
	public Component getTableCellEditorComponent(JTable table, Object value,
			boolean isSelected, int row, int column)
	{
		System.out.println("获取编辑器的组件");
		//将下拉列表设置为之前的选项
		m_SelStr = (String)value;
		m_ComboBox.setSelectedItem(m_SelStr);
		//返回值为 null的时候 是空的编辑器,就是说 = =不允许 编辑的
		return m_OutLable;
	}
	//获取编辑器的 值
	@Override
	public Object getCellEditorValue()
	{return m_SelStr;}	
}

//****************************************************定义显示表格的Frame
public class CellEditorFrame 
{
	public static void main(String[]sre)
	{
		
		MyTableModel m_TableModel = new MyTableModel();
		JTable m_TableDemo = new JTable(m_TableModel);

		ComBoxEditor m_ComboBoxEditor = new ComBoxEditor();
		MyPicEditor m_PicEditor =new MyPicEditor();

		JFrame m_MyFrame = new JFrame("我的CellEditor,图片位置请双击 选择图片");

		m_TableDemo.getColumnModel().getColumn(0).setCellEditor(m_ComboBoxEditor);
		m_TableDemo.getColumnModel().getColumn(1).setCellEditor(m_PicEditor);
		m_TableDemo.setRowHeight(200);

		JScrollPane m_JScroolPanel = new JScrollPane(m_TableDemo);
		m_JScroolPanel.setViewportView(m_TableDemo);
		m_JScroolPanel.setSize(480, 200);

		m_MyFrame.add(m_JScroolPanel);
		m_MyFrame.setBounds(200, 200, 500, 500);
		m_MyFrame.setDefaultCloseOperation(m_MyFrame.EXIT_ON_CLOSE);
		m_MyFrame.setVisible(true);
		
	}

}





你可能感兴趣的:(JAVA学习)