精华帖 (0) :: 良好帖 (10) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-09-10
前言
以前开发过一些简单的UI程序,大多都是借助IDE完成所有的工作,比如用DELPHI,或者C++ Builder等构筑,在这些工具中,UI是很容易开发的,但是在Java中就不一样了。Swing,在计算机科学界来说,其设计思想,整个体系的内涵,都是无以伦比的,但是要快速的用Swing开发一个能用的程序出来,还是比较困难的,其一,没有一个IDE支持(netbeans的高版本确实可以支持,但是,你对其Swing Application Framework不熟悉的话,开发出来的东西,你自己都不清楚所有细节),其二,貌似Swing的资料比较难找,找到的也大多是写不忍卒读的,即使想学也没有办法。
长期进行Java开发,UI部分还真没有涉及过,大多是WEB方面的,所以就找机会自己补习补习,关于Swing的资料,国外的好像比较多,而且也比较好,特别是Sun的员工的博客,确实不一般,如果英文过的去,就赶紧找找RSS订阅。
总体设计先看下效果: Todo-list
新建一个Task stodo(http://code.google.com/p/stodo/)是我最近学习swing的一个总结,也算是个可以用的小软件,尽管不是很好用,呵呵。
stodo内部使用sqlite作为数据存储,一来为了小巧,二来数据库配起来比较麻烦,这个好,不要用户名密码,但是对数据的存取都是通过标准的SQL语句,很方便。
主要用到JList控件,对JList的ListCellRenderer进行定制,使得这个List的Cell比较漂亮,然后对List的DataModel进行扩展,使得List中的数据为我们需要的数据类型。通过这些的定制,发现Swing的设计思想确实很了不起,框架很清晰,以后做UI框架,最好还是学习Swing的这一套机制。
部分实现好了,我们来看看实现的部分代码,如果要所有的源代码,可以去(http://code.google.com/p/stodo/)下载。
经验来看,做UI还是用IDE帮忙,我的界面中元素的布局都是用netbeans的UI设计器来设计的,然后将之CC到应用框架中即可。
public static void main(String[] args){ SwingUtilities.invokeLater(new Runnable(){ public void run() { ListMainFrame lMain = new ListMainFrame("My todo list"); lMain.initUI(); } }); }
在主入口处,创建TodoList的主界面,绑定菜单:
public void initUI(){ try{ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); }catch(Exception e){ System.err.println(e.getMessage()); } systemTray = SystemTray.getSystemTray(); try { trayIcon = new TrayIcon(ImageIO.read(new File("imgs/icon.png"))); systemTray.add(trayIcon); } catch (IOException e) { e.printStackTrace(); } catch (AWTException e) { e.printStackTrace(); } addWindowListener(new WindowAdapter() { public void windowIconified(WindowEvent e) { dispose(); } }); trayIcon.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) setExtendedState(NORMAL); setVisible(true); } }); //get todo-items from embedded database TodoItemListBuilder builder = new TodoItemListBuilder(); List<TodoItem> tlist = builder.getTodoItems(); TodoListModel listModel = new TodoListModel(); for(TodoItem item : tlist){ listModel.addElement(item); } todolist = new TodoList(listModel); todolist.setCellRenderer(new TodoListCellRenderer()); todolist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JScrollPane sp = new JScrollPane(todolist); todolist.addMouseListener(new ListItemListener()); JMenuBar mbar = new JMenuBar(); JMenu fileMenu = new JMenu("File"); JMenuItem newTask = new JMenuItem("New task", new ImageIcon("imgs/schedule_new.gif")); newTask.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_MASK)); newTask.setIconTextGap(4); newTask.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { if(newTaskDialog == null){ newTaskDialog = new NewTaskDialog(ListMainFrame.this, "New Task"); } newTaskDialog.setVisible(true); } }); JMenuItem exit = new JMenuItem("Exit", new ImageIcon("imgs/close.gif")); exit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK)); exit.setIconTextGap(4); exit.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { int y = JOptionPane.showConfirmDialog( null, "Confirm exit", "Confirm Exit Dialog", JOptionPane.YES_NO_OPTION); if(y == JOptionPane.YES_OPTION){ System.exit(0); } } }); fileMenu.add(newTask); fileMenu.add(exit); JMenu editMenu = new JMenu("Edit"); JMenuItem exportText = new JMenuItem("Export Text", new ImageIcon("imgs/exptotext.gif")); JMenuItem exportExcel = new JMenuItem("Export Excel", new ImageIcon("imgs/exptoexcel.gif")); editMenu.add(exportText); editMenu.add(exportExcel); mbar.add(fileMenu); mbar.add(editMenu); setJMenuBar(mbar); getContentPane().add(sp); setSize(400, 650); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setResizable(false); setLocationRelativeTo(null);//center of the screen setVisible(true); } 在JDK6中,用Swing开发桌面应用,比如调用浏览器,邮件客户端等都已经很容易,这里用到了系统托盘,这个也是JDK6中新添加的,可以使应用程序显得更加专业。
当添加一个新的task的时候,stodo先将记录插入数据库,然后更新主界面的JList的dataModel,Swing很好的做使用了MVC框架,更新了数据模型dataModel以后,控制器会自动更新JList。
另一个比较隐晦的问题是,当你自定义渲染器为List的Cell提供渲染的时候,你会发现,你在渲染器中绑定的事件会失效,因为JList会将这些事件处理,而不会继续传递,这就需要自己为鼠标绑一些事件处理(目前还没有找到更好的解决方法,如果你有好办法,请分享,谢谢)。
class ListItemListener extends MouseAdapter{ public void mouseEntered(MouseEvent e){ JList list = (JList)e.getSource(); int index = list.locationToIndex(e.getPoint()); TodoItem item = (TodoItem)list.getModel().getElementAt(index); String tooltip = "Desc : "+item.getDesc()+ ", Status : "+item.getStatus()+ ", Timeout:"+item.getTimeout(); list.setToolTipText( "<html>"+tooltip+ "</html>" ); } public void mouseExited(MouseEvent e){ JList list = (JList)e.getSource(); list.setToolTipText(""); } public void mouseClicked(MouseEvent e){ if(e.getClickCount() == 2){ JList list = (JList)e.getSource(); int index = list.locationToIndex(e.getPoint()); TodoItem item = (TodoItem)list.getModel().getElementAt(index); EditTaskDialog editTaskDialog = new EditTaskDialog(ListMainFrame.this, "Edit exist task", item); editTaskDialog.setVisible(true); } } } 注意:这里摘录的都是一些代码片段,尽量给出解释,要看完整的代码,请参考上文。
后记Swing非常强大,非常灵活,如果能基于现有的框架进行一个更高层次的封装,则可以更方便,易用。Swing的定制功能令人惊叹,这种哲学和*nix下的编辑器,shell等都是一脉相承的。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-09-11
不错,挺好的,支持一下现在还在学习swing的同学们
|
|
返回顶楼 | |
发表时间:2009-09-11
现在还在继续开发,准备加入Export,e-Mail等功能,希望这个可以成为一个可用的小软件,呵呵。
|
|
返回顶楼 | |
发表时间:2009-09-11
选准 定位是很重要的,这样就可以把大致的结构定下来。希望你能开发成功。
|
|
返回顶楼 | |
发表时间:2009-09-11
引用
另一个比较隐晦的问题是,当你自定义渲染器为List的Cell提供渲染的时候,你会发现,你在渲染器中绑定的事件会失效,因为JList会将这些事件处理,而不会继续传递,这就需要自己为鼠标绑一些事件处理(目前还没有找到更好的解决方法,如果你有好办法,请分享,谢谢)。
不是Jlist处理了这些事件,而不会继续传递
renderer都是画上去的 不是真实存在的 根本就无法传递
不明白这点很难学好swing |
|
返回顶楼 | |
发表时间:2009-09-11
qianhd 写道
renderer都是画上去的 不是真实存在的 根本就无法传递
不是真实存在是什么意思?panel(作为listcellrenderer的那个panel)上确实有checkbox的,而且可以设置其isSelected等,应该是存在的,只不过是事件没有继续传递吧,这个是我的理解。
如果你对这块比较清楚,望不吝赐教,让我好好学习一下,呵呵。 |
|
返回顶楼 | |
发表时间:2009-09-12
写日记的哇...小软件...支持一下
引用 汉化 |
|
返回顶楼 | |
发表时间:2009-09-13
abruzzi 写道
qianhd 写道
renderer都是画上去的 不是真实存在的 根本就无法传递
不是真实存在是什么意思?panel(作为listcellrenderer的那个panel)上确实有checkbox的,而且可以设置其isSelected等,应该是存在的,只不过是事件没有继续传递吧,这个是我的理解。
如果你对这块比较清楚,望不吝赐教,让我好好学习一下,呵呵。 当一个组件被用做Renderer的时候,它只是被单纯的调用了paint方法而已,你所有的事情是不可能被触发的,也就是说你的Renderer只是充当了一只画笔,它只管用来把内容画出来,其它你需要做交互动作的,你可以用Editor来实现相关的功能,Renderer只是画内容而已,SWING这样设计是很好的,每个组件各司其职 |
|
返回顶楼 | |
发表时间:2009-09-13
把calendar分出去就更好了
|
|
返回顶楼 | |
发表时间:2009-09-13
java.lang.Object 写道
abruzzi 写道
qianhd 写道
renderer都是画上去的 不是真实存在的 根本就无法传递
不是真实存在是什么意思?panel(作为listcellrenderer的那个panel)上确实有checkbox的,而且可以设置其isSelected等,应该是存在的,只不过是事件没有继续传递吧,这个是我的理解。
如果你对这块比较清楚,望不吝赐教,让我好好学习一下,呵呵。 当一个组件被用做Renderer的时候,它只是被单纯的调用了paint方法而已,你所有的事情是不可能被触发的,也就是说你的Renderer只是充当了一只画笔,它只管用来把内容画出来,其它你需要做交互动作的,你可以用Editor来实现相关的功能,Renderer只是画内容而已,SWING这样设计是很好的,每个组件各司其职
|
|
返回顶楼 | |