一個Swing的小應用(Todo-List)

前言

 

以前開發過一些簡單的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等都是一脈相承的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章