【第157期 October 5, 2010】
 

研發新視界

Swing元件的資料傳輸機制-Drag and Drop研究

作者/宋定遠

[發表日期:2010/9/24]




前言

Apple iPhone的熱賣告訴我們一件事-成功的商品不只是提供所需的功能,還要注重在介面上能提供良好的使用經驗,因為這是使用者能直接感覺到好惡的部份。在設計圖形化使用者介面(Graphical User Interface, GUI)時常因為考量方便性而感到苦惱,不但要避免讓使用者有過多的輸入動作,還要求越直覺化越好,因此,若能使元件中的資訊透過直覺的方式傳輸給另一個元件,或許就能給使用者更佳的使用經驗。

本文以Java Swing為探討的主要架構。資料傳輸(Data Transfer)的途徑有二,分別是透過滑鼠拖曳的Drag and Drop(簡稱DnD)和透過按鍵組合來觸發不同動作的Cut, Copy and Paste(簡稱CCP),其中CCP是以Drag and Drop的機制為基礎,只是事件的觸發不同,因此弄清楚Java Swing對於Drag and Drop的支援度是最重要的。

本文首先以一個操作範例來說明DnD的操作情景,並輔以專有名詞介紹,最後並將講解Java Swing的DnD架構。

DnD操作範例

元件A中有要傳輸的資料,要將這些資料傳送到元件B,在這個情境中元件A稱為資料來源(Source)、元件B稱為傳送目標(Target);在傳輸開始前使用者會先選取要傳輸的資料,然後按住滑鼠左鍵並拖曳至傳送目標上,以確定整個資料傳輸的收受方;傳輸開始時會將要傳輸的資料打包成Transferable物件,並根據資料來源和傳送目標的設定來決定資料如何傳輸,這個操作流程如圖一所示:


《圖一》Drag and Drop的基本操作流程


現在將重點放在資料來源和傳輸目標的設定上,在判斷是否能接受資料來源的Drop動作時會考慮一些事,例如傳輸的資料是否是傳輸目標能處理的類型、或者傳輸目標是否能支援資料來源的傳輸動作(Copy, Move等等),這些因素在判斷是否能接受Drop動作時都是極其重要的;在DnD中支援的資料類型稱為Data Flavor,而各種傳輸動作則稱為Drop Action。此外,拖曳中使用者可能會額外按下不同的按鍵來表示不同的傳輸動作,例如一般的拖曳是表示移動(Move),但拖曳中按住Control鍵卻是表示複製(Copy),這類在拖曳中發生、由使用者主動產生的動作在DnD中稱為User Drop Action。

最後,當決定要接受Drop動作時需要決定資料放置的方法和位置,這在DnD中稱為Drop Mode和Drop Location,但由於不同的傳輸目標元件在決定資料放置時會有不同的邏輯,例如一般的Text元件可能只需決定資料在第幾個位置插入,但Table元件卻必需連帶判斷要插入哪一列或哪一行,因此這個部份有時會使用特別的類別來協助。

事實上,在Java Swing中對DnD的支援已經十分完整,只需要最少的動作就能讓Swing元件間提供DnD的資料傳輸機制,最簡單的使用範例如圖二:


《圖二》Java Swing所支援的最基本DnD


由上圖可以看出,當資料來源為圖中所列的其中之一元件時,只要將DragEnabled設為True就能啟動拖曳資料的功能,而傳輸目標元件更是什麼更動都不必做,就預設支援Drop的動作(假設Target是圖中所列的元件之一),由此可知在Java Swing中對於DnD的支援其實已非常完整,但需注意在設計使用者介面時常使用的List、Tree、Table都沒有納入預設支援Drop動作的元件清單中,這是因為這三個元件擺放資料的邏輯各異,所以需要程式設計師針對不同的資料擺放方式來客製化,為此我們還是需要重頭開始瞭解Swing提供DnD支援的框架。

Java Swing的DnD架構

整個DnD的核心是一個名為TransferHandler的類別,它包含在所有JComponent和其子類別的內部(可用JComponent.setTransferHandler函式來設定所使用的類別),提供的功能可分為匯出(Export)和匯入(Import)兩個部分,整體來說匯出是對應於Drag,而匯入是對應於Drop。匯出時需做的準備動作和TransferHandler類別中的相對應函式包括:

1.createTransferable(JComponent)將要傳輸的資料打包成Transferable物件

2.getSourceActions(JComponent)用於查詢資料來源支援的傳輸動作

3.exportDone(JComponent, Transferable, int)用於定義傳輸完成後所需的後續動作,例如當資料從一元件移出時,資料來源應該將這些資料移除。

匯入部份的主要工作有二:

1.canImport(TransferHandler.TransferSupport)是用於在拖曳過程中持續不斷判斷滑鼠遊標底下的元件是否能接受來自資料來源的傳輸以完成Drop的動作。

2.importData(TransferHandler.TransferSupport)回傳值是Boolean型態,代表之前Drop的動作是成功或失敗。

這些功能或多或少都會使用TransferHandler的子類別TransferSupport,這是一個使用函式來提供資訊給TransferHandler、進而協助資料傳輸的類別,重要的函式如下:

1.getComponent()可以得到傳輸目標

2.getDropAction()回傳Drop動作中傳輸目標支援的Drop Action

3.getUserDropAction()回傳Drop動作中傳輸目標的User Drop Action

4.getSourceDropActions()回傳Drop動作中資料來源支援的Drop Action

5.getDataFlavors()得到這個JComponent支援的Data Flavor

6.isDataFlavorSupported(DataFlavor)判斷傳入的Data Flavor是否支援

7.getTransferable()得到資料來源打包好的Transferable物件

8.getDropLocation()得到資料放置的位置。但就如同之前所說,不同的物件有不同的資料擺放邏輯,因此有些物件會自己提供子類別來決定Drop Location,例如JList、JTree、JTable都有。

綜觀之前所介紹的,我們可以得到一個JComponent的構造圖,如圖三:


《圖三》JComponent和提供DnD機制物件的構造


在認識DnD機制的架構之後,就有可能使JList、JTree、JTable這三個重要的元件也能支援Drop動作,補足DnD中缺失的一塊。

資料來源

http://download.oracle.com/javase/tutorial/uiswing/dnd/index.html