苹果的成长日记

我还是个青苹果呀!

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  57 随笔 :: 0 文章 :: 74 评论 :: 0 Trackbacks
     从7月14日开始来上海实习已经3个星期了,项目还没正式开始,前期工作准备了很多,而我主要是负责GUI这块,工具是Swing,所以陪伴Swing也已经快一个月的日子了.项目下个星期正式启动,所以对前面的体会作个小小的总结.
    以前在inforsense公司的KDE平台上已经有一个Table Editor,点击主界面上含有表格数据的节点,可以打开表格,然后可以对各种表格进行编辑,增删等简单操作,而同时这些操作也会反映到主界面上的工作流模型中.现在的目标是对这个工具进一步扩展其功能,不仅融如Excel spreadsheet的功能(过滤,对cell进行编辑等),还有将树图与表格视图连接起来,可以进行两种视图之间的拖拽(dnd),切换等.现在更要与化学专业结合起来,本来这款软件是为化学家设计的,目的使他们操作起来更方便.所以还要把扩展后的表格编辑器和Interactive Browser结合起来,做到对同一组数据的多种视图,而且它们是同步的.比如表格中会有Structure(化学分子结构),分子量这样的特定的域,而点击后可启动特定的编辑化学分子结构的软件进行编辑,同时变化反映在表格数据中.
    而我接触的都是Swing,它给我的感觉虽然好象仅仅是在AWT的类前面都加上了个J,但仔细研究,里面有各种设计模式的存在,这一点让我兴奋不已,正好借这个机会学习设计模式.我大部分时间接触的都是JTable和JTree.所以主要谈谈他们.Swing基本是就是个MVC的设计架构,就拿JTable来说,JTable就是View的部分,而TableModel就是M的部分.下面一点点讲讲实现的细节:
1.Filter(过滤器):
          要在表格中实现过滤的功能,而实际上不影响原来的模型,可以考虑在原来的模型增加一个过滤器.它其实上也是一个TableModel(可以子类化TableModel的实现框架AbstractTableModel.),它把原来的TM作为自己的成员,任何实际的操作如getColumnCount(),getRowCount(),getColumnName()等都交给原来的TM来完成(调用TM的相应方法),只是在应该控制的地方控制一下,比如,getValueAt(i,j)就通过控制i,j来只返回过滤器想显示的行或列的数据,而具体的返回数据的操作还是由TM来完成.对setValueAt(),isCellEditable()也是同样的道理.我具体的做法就是用一个List把我想显示的行(列)号保存下来,在getValueAt(i,j)中,i的取值范围就是这个List了.这其实是一种Adapter模式的思想.同样,实现Sort也可以用这种方式.
2)Selection:
    JTable中的选择都是由ListSelectionModel来完成的,行列都有默认的选择模型,访问行的SelectionModel的方式是getSelectionModel(),访问列的SelectionModel的方式是getColumnModel().getSelectionModel().你也可以实现自己的选择模型.可以通过
getRowSelectionAllowed()和getColumnSelectionAllowed()获取现在行列是否可选的信息,如果都可选,则在Cell级别是可选的.这就是为什么在行列都可选的情况下,设置i行被选中setRowSelectionInterval(i),同时设置j列被选中setColumnSelectionInterval(j),这样只有(i,j)的Cell单元被选中得到原因.但是反过来,如果我只想使(i,j)的Cell不被选中,而仅仅靠removeColumnSelectionInterval(j)和removeRowSelectionInterval(i)是实现不了的.这难道是Swing的漏洞?
    前面已经讲到,设置改变选择状态主要是通过行列SelectionModel的setSelectionInterval(),addSelectionInterval(),removeSelectionInterval()三个方式实现的.
3)header
    表的行,列的表头着实让我头痛了一阵.尤其是row header.我的row header是用一个JTable实现的,关键是要和表格同步起来.可以考虑与表格共用一个Filter,关键是改写getValueAt()和getRowCount()这两个方法.这样表格过滤留下的行也是表头这个JTable中所需要留下的行.而选择的同步则是覆盖changeSelection()这个方法实现的.而操作的方法就是在2)中提到的那几个方法.设置rowHeader为表头只需要在JScrollPane中用setRowHeaderView()指定即可,而表格最左上角的单元(行表头的表头)用setCorner()指定.
    ColumnHeader其实在JTable中已有实现,如果要通过单击列头来选择全列的话,实现的方法可通过在列头上添加一个MouseListener,然后在它的MouseClicked方法中进行选择的同步,其余步骤与行在changeSelection()中的类似,有一点值得注意,要获取单击的列的索引是通过getTableHeader()后得到的tableHeader.columnAtPoint(e.getPoint())得到的,这里e是MouseEvent,也就是这个单击的动作事件.
具体的控制代码如下:
/**
  * once click on the header, that column should be selected
  */
 public void mouseClicked(MouseEvent e) {
  JTableHeader header = table.getTableHeader();
  TableColumnModel columns = header.getColumnModel();
  if(!columns.getColumnSelectionAllowed())
   return;
  //get the column index being clicked
  int column = header.columnAtPoint(e.getPoint());
  if(column == -1)
   return;
  int count = table.getRowCount();
  //set the entire column to be selected
  if(count != 0)
   table.setRowSelectionInterval(0,count-1);
  ListSelectionModel selection = columns.getSelectionModel();
  //if the shift modifier is pushed down, need to select multiple columns
  if(e.isShiftDown()) {
   int anchor = selection.getAnchorSelectionIndex();// the first index
   int lead = selection.getLeadSelectionIndex();//the last index
   
   if(anchor != -1) {
    boolean old = selection.getValueIsAdjusting();
    selection.setValueIsAdjusting(true);
    
    boolean anchorSelected = selection.isSelectedIndex(anchor);
    
    if(lead != -1) {
     if(anchorSelected)
      selection.removeSelectionInterval(anchor,lead);
     else
      selection.addSelectionInterval(anchor,lead);
    }
    
    if(anchorSelected)
     selection.addSelectionInterval(anchor,column);
    else
     selection.removeSelectionInterval(anchor,column);
    selection.setValueIsAdjusting(old);
   }
   else
    //select single column
    selection.setSelectionInterval(column,column);
   }
  else if(e.isControlDown()) {
   if(selection.isSelectedIndex(column))
    selection.removeSelectionInterval(column,column);//unselect this column
   else
    selection.setSelectionInterval(column,column);
  }
  else {
    selection.setSelectionInterval(column,column);
  }
 }
4)dnd:
   构造一个Transferable对象,保存传送的数据.而两方分别实现自己的TransferHandler即可.
5)表示器和编辑器.
   如果想在JTree中添加JCheckbox,其实只需要实现自己的CellRenderer和CellEditor,在getTreeCellRendererComponent(Object value)和setTreeCellRendererComponent(Object value)中返回或设置一个JCheckBox(value.toString())即可.value就是Tree中节点node的UserObject.如果你想更改树中显示的文字,比如在父节点中显示子节点的数量,只需要在TreeNode类中(子类化DefaultMutableTreeNode)改写toString()方法即可.
   目前的代码可以在"文件"中下载.
posted on 2005-08-07 12:08 苹果 阅读(3908) 评论(5)  编辑  收藏 所属分类: J2EE/JAVA学习

评论

# re: 学习Swing的一点体会 2006-05-11 10:09 hhh
就拿JTable来说,JTable就是View的部分,而TableModel就是M的部分.

JTable应该是controller,负责UI的才是view,它们实现相应的look and feel
swing是一件艺术品,有着相当优秀的架构。
  回复  更多评论
  

# 请教几个问题(很急) 2006-05-16 00:59
你写的关于列头选整列的方法,对我非常有帮助,谢谢!
1 能否再写一下点行头选正行的方法。
2 如何在单击左上角的CELL 选中全部CELL
3 如何对JTABLE 加LISTENER,当想改变全部CELL的值的时候,谢谢!!  回复  更多评论
  

# re: 学习Swing的一点体会 2007-01-03 15:50 hhh[匿名]
我欣赏优雅和一致的设计,一致性反应了设计者极高的抽象水平,优秀的概念表达.
  
  我们现在来看MFC和swing.
  MFC是如何表达GUI的,首先对于GUI元素没有一个共性抽象,使得无法进行任意的递归组合.
  MFC认为 GUI元素是原子的,GUI元素自身提供了设置它属性的API基本决定了这个GUI元素给外部定制它的能力.低灵活性!
  
  MFC认为GUI元素自身应该处理事件,并且使用消息映射来实现这一点.
  所以通常如果你要处理某个GUI元素的事件,你需要写一个继承这个GUI元素的类.
  但殊不知很多情况下事件处理者和GUI元素是分离的!
  
  MFC的MVC是实现是不一致的,他并没有将这一理念贯穿于整个框架.而只是单独搞了个doc/view.
  
  所以如果我以今天的思路来看MFC,我觉得它对GUI的抽象是糟糕的,差劲概念表达.
  回复  更多评论
  

# re: 学习Swing的一点体会 2007-01-03 15:51 hhh[匿名]
而swing的组件都是四两拨千斤,模型和绘图都是分离的,如表格
  JTable 就有TableModel ---->JTable<-----TableUI,从结构来说JTable完全扮演着控制器的角色.从API的使用者角度来说它是一个MVC体系的外观,让人想起了设计模式中的外观模式.
  
  因为swing中一个GUI元素就是一个MVC体系,而不原子的.
  除此之外还可以为组件自定义renderer/editor.
  回复  更多评论
  

# re: 学习Swing的一点体会 2007-01-03 15:53 hhh[匿名]
swing是我见过最优雅和灵活的GUI框架!
从美学来讲没有其它框架能比之!  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: